AutoPostcode
Developers · Docs Hub

Address API documentation

Two endpoints, one API key. Postcode Lookup for button-triggered 'Find my address' flows, and Address Autocomplete for Fetchify-style search-as-you-type. Copy-paste code below — you can be live in minutes.

No card required · Royal Mail PAF-verified data

Trusted UK address intelligence

~31M
PAF addresses
<1s
typical response
REST
JSON API
PAF-verified address lookup
Fast REST API & no-code plugins
GDPR-friendly, UK-hosted

The AutoPostcode address API exposes two endpoints for looking up real UK addresses. Both take a single API key in the x-api-key header, both return JSON, both are CORS-enabled, and both draw from the same shared credit balance.

~31M
UK addresses
~1.8M
UK postcodes
99.9%
uptime

Which endpoint do I need?

Use caseEndpointInput
User already typed a postcode and clicks Find address/api/public/lookupFull postcode
Single search box that resolves as they type (Fetchify / Loqate style)/api/public/autocompleteAny partial text (street, town, postcode, house number)

Authentication

Every request must include your API key. Keys start with ap_live_ and are minted per website from your dashboard (Websites → Copy full key). The endpoint accepts any of these forms — use whichever fits your stack:

  • x-api-key: ap_live_… (recommended)
  • Authorization: Bearer ap_live_…
  • api-key: ap_live_…
  • ?api_key=ap_live_… query string (server-to-server proxies only — never expose in browser code)

AI-agent tip: if you're building a proxy on another platform, store the key as a server secret (e.g. AUTOPOSTCODE_API_KEY) and forward it as the x-api-key header — do not ship the key to the browser. A 401 response includes a hint field naming exactly which header we read; use it to debug misconfigured proxies.

1. Postcode Lookup

Send a full UK postcode; get back every deliverable address on that postcode as a JSON array. Ideal for the classic "enter postcode → pick address from dropdown" checkout pattern.

Endpoint

GET https://autopostcode.com/api/public/lookup?postcode=<POSTCODE>

Request

cURL
curl "https://autopostcode.com/api/public/lookup?postcode=SW1A%201AA" \
  -H "x-api-key: ap_live_xxxxxxxxxxxxxxxx"
JavaScript
// Postcode Lookup — return every address on a full UK postcode.
const API_KEY = "ap_live_xxxxxxxxxxxxxxxx";

async function lookupPostcode(postcode) {
  const res = await fetch(
    "https://autopostcode.com/api/public/lookup?postcode=" +
      encodeURIComponent(postcode),
    { headers: { "x-api-key": API_KEY } }
  );
  if (!res.ok) throw new Error("Lookup failed: " + res.status);
  return res.json(); // Array of address objects
}

lookupPostcode("SW1A 1AA").then(console.log);

Response

200 OK · application/json
[
  {
    "postcode": "SW1A 1AA",
    "line1": "Buckingham Palace",
    "line2": "",
    "line3": "",
    "town": "London",
    "county": "Greater London",
    "country": "United Kingdom"
  }
]

2. Address Autocomplete

Send a free-text query — the endpoint matches against street, town, postcode and building number and returns ranked address suggestions. Debounce input by ~300 ms and only fire once q is at least 3 characters.

Endpoint

GET https://autopostcode.com/api/public/autocomplete?q=<QUERY>&session=<SESSION>

Request

cURL
curl "https://autopostcode.com/api/public/autocomplete?q=10%20downing&session=s_abc123" \
  -H "x-api-key: ap_live_xxxxxxxxxxxxxxxx"
JavaScript
// Address Autocomplete — Fetchify-style search-as-you-type.
// BILLING: one address search = one credit. Send the SAME session id with
// every keystroke of a single search; reset it when the user picks an
// address or clears the field. Keystrokes after the first are free.
const API_KEY = "ap_live_xxxxxxxxxxxxxxxx";

let session = null;
const newSession = () =>
  "s_" + Date.now().toString(36) + Math.random().toString(36).slice(2, 10);
export const resetSession = () => { session = null; };

async function searchAddresses(query) {
  if (!query || query.trim().length < 3) return { count: 0, suggestions: [] };
  if (!session) session = newSession();
  const res = await fetch(
    "https://autopostcode.com/api/public/autocomplete?q=" +
      encodeURIComponent(query) +
      "&session=" + encodeURIComponent(session),
    { headers: { "x-api-key": API_KEY } }
  );
  if (!res.ok) throw new Error("Search failed: " + res.status);
  return res.json(); // { suggestions, groups, credits_charged, credits_remaining }
}

// Debounce input by 300ms, then:
searchAddresses("86 marland fold").then(console.log);
// After the user selects an address (or clears the input): resetSession();

Response

200 OK · application/json
{
  "query": "aberdeen",
  "count": 9,
  "groups": [
    {
      "token": "eyJzIjoiVW5pb24gU3RyZWV0IiwidCI6IkFiZXJkZWVuIn0",
      "label": "Union Street, Aberdeen",
      "postcode": "",
      "count": 42
    }
  ],
  "suggestions": [
    {
      "suggestion": "AB10 1AA — 20, Union Street, Aberdeen",
      "label": "20, Union Street, Aberdeen",
      "postcode": "AB10 1AA",
      "line1": "20 Union Street",
      "line2": "",
      "line3": "",
      "town": "Aberdeen",
      "county": "Aberdeenshire",
      "country": "United Kingdom"
    }
  ],
  "session": "s_lx8f2k9a1b2c",
  "credits_charged": 1,
  "credits_remaining": 1584
}

Billing — one credit per search session, not per keystroke

A customer typing 375375 B375 Belle 375 Bellegrove is one address search and one credit. Pass a session value (query param, or the x-session-id header) that stays the same for the whole search: the first request charges 1 credit and every later keystroke in that session is logged with credits_charged: 0. Reset the session when the user selects an address or clears the field — the next search starts a new billable session.

Session lifetime

A session id you supply stays free for 1 hour from its first billable request; reusing the same id after that starts a new billable session. There is no inactivity timeout — leaving the field idle does not end a session, so you decide exactly when one ends by resetting the id. If you send no session id, the server-derived fallback (API key + IP + browser) is bucketed in 10-minute windows, so a long pause can roll into a new billable bucket — send your own id if you want precise control.

  • Send the same session id with every keystroke of one address search
  • Reset the session on address select, or when the input is cleared
  • A client-supplied session id is valid for 1 hour from the first billable request
  • No inactivity timeout — you decide when a session ends by resetting the id
  • No session sent? We derive one from your API key, IP and browser over a 10-minute window, so you are still charged once per search
  • Debounce input by 300ms and only search once q is 3+ characters
  • Group expansions and no-result searches are always free
  • Every response returns credits_charged (0 or 1) and credits_remaining

Groups (optional — recommended for streets with many addresses)

When a query matches a street or block containing many delivery points, the API returns them as a single group entry inside the top-level groups array (rendered above suggestions in your dropdown). Show groups as a single row like "View 42 addresses at Union Street, Aberdeen ›". On click, call the endpoint again with the opaque group token to fetch every individual address in that group:

GET https://autopostcode.com/api/public/autocomplete?group=<TOKEN>
cURL
curl "https://autopostcode.com/api/public/autocomplete?group=eyJzIjoiVW5pb24…" \
  -H "x-api-key: ap_live_xxxxxxxxxxxxxxxx"
200 OK · application/json
{
  "group": "eyJzIjoiVW5pb24…",
  "count": 42,
  "suggestions": [
    { "suggestion": "AB10 1AA — 1, Union Street, Aberdeen", "postcode": "AB10 1AA", "label": "1, Union Street, Aberdeen", "line1": "1 Union Street", "town": "Aberdeen", "county": "Aberdeenshire" },
    { "suggestion": "AB10 1AA — 2, Union Street, Aberdeen", "postcode": "AB10 1AA", "label": "2, Union Street, Aberdeen", "line1": "2 Union Street", "town": "Aberdeen", "county": "Aberdeenshire" }
  ]
}

Group expansions are free — only the initial q search costs a credit. Pass the token back verbatim (do not decode). After opening a group, filter its addresses locally in the browser as the user keeps typing; only issue a new q search once your local filter has zero matches or the input is cleared.

Complete working example — vanilla JS widget

A drop-in autocomplete box: debounced input, in-flight request cancellation, dropdown list, graceful empty / error states, and address-field population on select.

autopostcode-widget.js
// Drop-in vanilla-JS address autocomplete.
// Renders a search box, debounces input, calls the API, shows a
// dropdown, and populates address fields on select.
//
// Expected HTML:
//   <input id="apc-search" placeholder="Start typing your address" />
//   <ul id="apc-suggestions"></ul>
//   <input id="line1" /> <input id="line2" /> <input id="town" />
//   <input id="county" /> <input id="postcode" />

const API_KEY = "ap_live_xxxxxxxxxxxxxxxx";
const ENDPOINT = "https://autopostcode.com/api/public/autocomplete";

const input = document.getElementById("apc-search");
const list  = document.getElementById("apc-suggestions");

let timer, controller;

input.addEventListener("input", () => {
  clearTimeout(timer);
  timer = setTimeout(() => runSearch(input.value.trim()), 300);
});

async function runSearch(q) {
  list.innerHTML = "";
  if (q.length < 3) return;

  if (controller) controller.abort();
  controller = new AbortController();

  try {
    const res = await fetch(
      ENDPOINT + "?q=" + encodeURIComponent(q),
      { headers: { "x-api-key": API_KEY }, signal: controller.signal }
    );
    const data = await res.json();
    if (!data.suggestions?.length) {
      list.innerHTML = "<li>No addresses found.</li>";
      return;
    }
    for (const s of data.suggestions) {
      const li = document.createElement("li");
      li.textContent = s.suggestion;
      li.onclick = () => fill(s);
      list.appendChild(li);
    }
  } catch (err) {
    if (err.name !== "AbortError") {
      list.innerHTML = "<li>Unable to search addresses.</li>";
    }
  }
}

function fill(addr) {
  document.getElementById("line1").value    = addr.line1 || "";
  document.getElementById("line2").value    = addr.line2 || "";
  document.getElementById("town").value     = addr.town  || "";
  document.getElementById("county").value   = addr.county || "";
  document.getElementById("postcode").value = addr.postcode || "";
  input.value = addr.suggestion;
  list.innerHTML = "";
}

Complete working example — React hook

The same behaviour as a reusable hook. Feed it a controlled input value; render suggestions however you like.

useAddressSearch.ts
// React hook — address autocomplete with debounce + abort.
import { useEffect, useRef, useState } from "react";

const API_KEY = import.meta.env.VITE_AUTOPOSTCODE_KEY!;
const ENDPOINT = "https://autopostcode.com/api/public/autocomplete";

export function useAddressSearch(query: string) {
  const [suggestions, setSuggestions] = useState<any[]>([]);
  const [status, setStatus] = useState<"idle"|"loading"|"empty"|"error">("idle");
  const ctrl = useRef<AbortController | null>(null);

  useEffect(() => {
    if (query.trim().length < 3) { setSuggestions([]); setStatus("idle"); return; }
    const t = setTimeout(async () => {
      ctrl.current?.abort();
      ctrl.current = new AbortController();
      setStatus("loading");
      try {
        const res = await fetch(
          ENDPOINT + "?q=" + encodeURIComponent(query),
          { headers: { "x-api-key": API_KEY }, signal: ctrl.current.signal }
        );
        const data = await res.json();
        setSuggestions(data.suggestions ?? []);
        setStatus(data.suggestions?.length ? "idle" : "empty");
      } catch (e: any) {
        if (e.name !== "AbortError") setStatus("error");
      }
    }, 300);
    return () => clearTimeout(t);
  }, [query]);

  return { suggestions, status };
}

Response fields

  • postcode — canonical UK postcode (e.g. SW1A 1AA)
  • line1 — building name/number + thoroughfare
  • line2 — dependent locality (often empty)
  • line3 — double-dependent locality (rare)
  • town — post town
  • county — administrative county
  • country — United Kingdom
  • Autocomplete only: suggestion — a single pre-formatted display string

Errors & status codes

StatusMeaningWhat to do
200SuccessRender suggestions / addresses.
401Missing or invalid API keyCheck the x-api-key header.
402Insufficient creditsTop up in dashboard.
429Rate limitedDebounce input, back off and retry.

Setup recipe for AI agents

Building with an AI code assistant? Point it at this page — or paste the recipe below straight into the prompt. It contains everything needed to wire up a working address search box in one pass.

autopostcode.recipe
# AutoPostcode — one-shot setup recipe (for AI agents)
#
# Goal: add a live UK address search box to a checkout / signup form.
# Data returned: PAF-verified UK addresses (line1, line2, town, county, postcode).
#
# 1. Get a key at https://autopostcode.com  (starts with ap_live_)
# 2. Store as VITE_AUTOPOSTCODE_KEY (or proxy through your backend).
# 3. Call the autocomplete endpoint on every debounced keystroke:
#
#    GET https://autopostcode.com/api/public/autocomplete?q=<user_text>&session=<search_session_id>
#    Billing: ONE credit per search session, not per keystroke. Generate a
#    session id when a search starts, send it with every keystroke, reset it
#    when an address is selected or the input is cleared. Debounce 300ms.
#    A session id stays free for 1 hour; there is no inactivity timeout, so
#    you control when a session ends by resetting the id.
#    Header: x-api-key: ap_live_...
#
# 4. Render response.groups[] FIRST as "View {count} addresses at {label} ›" rows,
#    then response.suggestions[] as one-line "{POSTCODE} — {label}" rows.
# 5. On group click: GET .../autocomplete?group=<token> (opaque, pass verbatim,
#    no credit charge). Replace dropdown with returned suggestions.
# 6. After opening a group, filter locally in the browser as the user keeps
#    typing. Only issue a new global ?q= search when the local filter is empty
#    OR the input is cleared.
# 7. On address click, populate line1 / line2 / town / county / postcode fields
#    and keep the full raw address object on the form.
#
# For a postcode-first flow (button-triggered) use instead:
#    GET https://autopostcode.com/api/public/lookup?postcode=<postcode>
#    Header: x-api-key: ap_live_...
#
# Both endpoints:
#   - Cost 1 credit per successful call (no-result calls are free; group
#     expansions are free).
#   - Support GET (query string) and CORS from any origin.
#   - Return JSON with the same address field names.
One API key, one credit pool, two endpoints. Use lookup for postcode-first flows and autocomplete for search-as-you-type — you can even use both on the same form.

Ready to build?

Grab a key from the free trial and paste one of the snippets above — most teams are returning live addresses within minutes.

Frequently asked questions

What is the difference between Postcode Lookup and Address Autocomplete?

Postcode Lookup takes a complete UK postcode (e.g. SW1A 1AA) and returns every address on it — perfect for a 'Find my address' button on a checkout. Address Autocomplete takes any partial free-text query (street name, town, postcode, building number, or a mix) and returns matching addresses as the user types — perfect for a Fetchify/Loqate-style live search-as-you-type box.

Which endpoint should I use?

Use Postcode Lookup if your form already has a postcode field and a button. Use Address Autocomplete if you want a single search box that resolves as the user types. Both use the same API key and the same shared credit balance.

How do I authenticate?

Send your API key in the x-api-key request header. Keys start with ap_live_ and are minted per website from your dashboard. Requests without a valid key return 401.

What is the base URL?

https://autopostcode.com/api/public/ — both endpoints are simple GETs on this base. CORS is open so you can call directly from the browser.

How much does each call cost?

One credit per successful postcode lookup, and ONE credit per address autocomplete search session — not per keystroke. Send the same `session` value with every keystroke of one address search and only the first request is charged; the rest are logged with credits_charged: 0. Group expansion is always free, and failed calls (no match, invalid postcode) are not billed. Every response includes credits_charged and credits_remaining.

Do I need to send a session id?

It is optional but recommended. Without one, AutoPostcode derives a session from the API key, IP and browser for a 10-minute window, so you are still only charged once per search. Generate your own id when a search starts, resend it on every keystroke, and reset it when the user picks an address or clears the field.

How long does an autocomplete session last?

A session id you supply stays free for 1 hour from its first billable request; reusing it after that starts a new billable session. There is NO inactivity timeout — an idle field does not end a session. You decide when a session ends by resetting the id (address selected or field cleared). If you send no session id, the server-derived fallback is bucketed in 10-minute windows, so a long pause can roll into a new billable bucket — send your own id for exact control.

What data source powers the API?

Every result is matched against the AutoPostcode UK address database — around 31 million deliverable addresses across roughly 1.8 million postcodes, refreshed continuously.

Price beat guarantee

We'll beat any like-for-like UK address lookup price

Send us your current quote or invoice and we'll come back the same working day with a better price — plus free migration and integration support.

  • Beat any like-for-like UK quote, monthly or annual
  • Free migration + hands-on integration support
  • No setup fees, no lock-in, cancel any time

Ready to get started?

Add Royal Mail PAF-verified UK address lookup to your site in minutes — start free, no card required.