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.
Which endpoint do I need?
| Use case | Endpoint | Input |
|---|---|---|
| User already typed a postcode and clicks Find address | /api/public/lookup | Full postcode |
| Single search box that resolves as they type (Fetchify / Loqate style) | /api/public/autocomplete | Any 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
Request
curl "https://autopostcode.com/api/public/lookup?postcode=SW1A%201AA" \
-H "x-api-key: ap_live_xxxxxxxxxxxxxxxx"// 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
[
{
"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
Request
curl "https://autopostcode.com/api/public/autocomplete?q=10%20downing&session=s_abc123" \
-H "x-api-key: ap_live_xxxxxxxxxxxxxxxx"// 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
{
"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 375 → 375 B → 375 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:
curl "https://autopostcode.com/api/public/autocomplete?group=eyJzIjoiVW5pb24…" \
-H "x-api-key: ap_live_xxxxxxxxxxxxxxxx"{
"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.
// 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.
// 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
| Status | Meaning | What to do |
|---|---|---|
| 200 | Success | Render suggestions / addresses. |
| 401 | Missing or invalid API key | Check the x-api-key header. |
| 402 | Insufficient credits | Top up in dashboard. |
| 429 | Rate limited | Debounce 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 — 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.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.
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.
