How to use it: 3 steps
- Make a free Apify accountApify is the cloud platform where our tools run. The free plan gives you $5 of usage every month, no credit card needed. You pay Apify, not us.
- Paste your input and press StartNo code needed: you fill in a form (the input). It already has a working example. For example:
SW1A 2AAin Postcodes. Each start is one run. - Download the resultsWhen the run ends, the results are in a table (the dataset). Open Output, press Export and pick Excel or CSV. JSON and the API are there too.
This tool is an Apify Actor: a small program that runs on Apify. You can also run it on a schedule, for example every Monday. New to it? Read how it works, step by step and the 8 words you need first.
What it does
Paste a list of Great Britain postcodes. UK Postcode Lookup returns one row per postcode with latitude/longitude, OS grid eastings/northings, local authority, ward, parliamentary constituency, LSOA/MSOA and their official GSS codes from the ONS Postcode Directory (via Postcodes.io). No login or API key is needed.
Good for
- Add map coordinates to a list of customer or store postcodes.
- Join postcodes to ONS statistics with LSOA, MSOA, output area and district GSS codes.
- Group leads by local authority, region or parliamentary constituency.
- Find typos and old postcodes in a CRM export.
Not a good fit
- No Northern Ireland (BT) data. Its licence does not allow free commercial reuse.
- No street addresses or house numbers. Coordinates are the postcode centre, not a building.
- No reverse geocoding (coordinates → postcode) and no autocomplete.
- Isle of Man (IM) and Channel Islands (GY, JE) postcodes return
foundbut without coordinates or census areas. Some old IM postcodes areterminatedwithout coordinates.
Who uses it
Data analysts
Join postcodes to ONS and Census tables with LSOA codes.
Sales and ops teams
Group customers by council, region or constituency.
CRM owners
Find typos and old postcodes in a contact list.
Price
Pay per event: you pay for each result the tool saves, plus a small start fee for each run. The price includes Apify platform costs. Apify bills you, not us. Prices below are for the Apify free plan.
| What you pay for | Price | When |
|---|---|---|
| Start fee | $0.002 | Charged once per run. This tool uses 1 GB of memory or less by default; more memory costs more. |
| Postcode result | $0.01 (from $0.0085 on paid Apify plans) | One unique found or terminated Great Britain postcode with coordinates saved to the Dataset. Rows without a paid result are charged as Postcode checked; error rows are free. |
| Postcode checked | $0.00005 | One answered row without a paid result: not found, invalid format, duplicate, Northern Ireland (not supported) or found without coordinates. Error rows are free. |
Examples
- One postcode: about $0.01.
- 1,000 postcodes: about $10.00.
- Every day 100 postcodes, for 30 days: about $30.06 a month.
Free to try: the Apify free plan gives you $5 of usage every month, no credit card needed. That is enough for about 490 postcodes.
Prices checked on 24 October 2026. See the current price on Apify.
Questions
Does it cover Northern Ireland?
No. BT postcodes are not included because of their licence.
Can I get the street address?
No. You get area data and the postcode centre, not house numbers.
What happens with old postcodes?
They show as terminated. You still get the last known location.
Do I pay for bad postcodes?
Error rows are free. Full price is only for found postcodes with coordinates.
Do I need an API key or account for Postcodes.io?
No. The Actor uses the public Postcodes.io API without a key.
Why do terminated postcodes have no district or ward?
The directory keeps only the last location and end date for old postcodes. Use latitude/longitude if you need an area.
Is the data complete?
It covers active and terminated postcodes in England, Scotland and Wales from the ONS Postcode Directory. Isle of Man and Channel Islands postcodes are listed but have no coordinates. Coordinates are postcode centres. Some areas, like county or parish, are empty where they do not apply.
Details
Step by step on Apify
- Open the Input tab.
- Keep the 5 example postcodes in Postcodes (
postcodes) or paste your own, one per line. - Keep Check terminated postcodes (
includeTerminated) on. - Click Start.
- Open the Overview table in the Output tab and check
status,latitudeandadminDistrict.
Use cases
| Task | Input | Key fields | Next step |
|---|---|---|---|
| Map customers | postcodes from a CRM column | latitude, longitude, status | Map tool, BI dashboard |
| Join to ONS statistics | postcodes | lsoaCode, msoaCode, outputArea, adminDistrictCode | ONS / Census tables |
| Sales territories | postcodes | region, adminDistrict, adminCounty, parliamentaryConstituency | CRM fields |
| Data cleaning | postcodes | status, postcode (normalized), terminatedOn | Fix or remove bad records |
Tips for better results
- Do not worry about spaces or case. The tool cleans each postcode.
- Filter by status to see not-found and old postcodes fast.
- Coordinates are the postcode centre, not one house.
Limits and good to know
- Northern Ireland (BT): NI postcode data needs a commercial licence from Land & Property Services. These rows are marked
unsupported_licensed_regionbefore any lookup and cost only the check fee - Freshness: data follows the ONS Postcode Directory loaded by Postcodes.io (ONSPD August 2026 when we checked on 2026-10-12). It is updated quarterly. Very new postcodes may be
not_founduntil the next update - Source problems: requests have a 20-second timeout and up to 3 tries. If the source stops answering, the rest of the rows are saved as
error(notnot_found) and are not charged. The summary saysPARTIAL(some results) orERROR(none, the run fails)
For developers
Sample result
One result from a real test run, in JSON. Real values change from run to run. In Excel or CSV each field is a column.
{
"inputIndex": 0,
"input": "SW1A 2AA",
"postcode": "SW1A 2AA",
"status": "found",
"statusReason": "Active postcode",
"errorCode": null,
"duplicateOfIndex": null,
"latitude": 51.503541,
"longitude": -0.12767,
"eastings": 530047,
"northings": 179951,
"positionalQuality": 1,
"country": "England",
"region": "London",
"adminDistrict": "Westminster",
"adminDistrictCode": "E09000033",
"adminWard": "St James's",
"adminWardCode": "E05013806",
"parliamentaryConstituency": "Cities of London and Westminster",
"parliamentaryConstituencyCode": "E14001172",
"lsoa": "Westminster 018C",
"lsoaCode": "E01004736",
"msoa": "Westminster 018",
"msoaCode": "E02000977",
"introducedOn": "1980-01",
"terminatedOn": null,
"provider": "Postcodes.io (ONS Postcode Directory)",
"fetchedAt": "2026-10-12T07:46:24.376Z"
}Use it from code
Actor ID: cliqtomedia/gb-postcode-lookup. You need your Apify API token from Apify Console → Settings → API & Integrations.
from apify_client import ApifyClient
client = ApifyClient("YOUR_APIFY_TOKEN")
run = client.actor("cliqtomedia/gb-postcode-lookup").call(run_input={
"postcodes": [
"SW1A 2AA",
"EH99 1SP",
"CF99 1SN",
"M60 2LA",
"B1 1BB"
],
"includeTerminated": True
})
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
print(item)curl -X POST "https://api.apify.com/v2/acts/cliqtomedia~gb-postcode-lookup/run-sync-get-dataset-items?token=YOUR_APIFY_TOKEN" \
-H "Content-Type: application/json" \
-d '{"postcodes": ["SW1A 2AA", "EH99 1SP", "CF99 1SN", "M60 2LA", "B1 1BB"], "includeTerminated": true}'# pip install langchain-apify
# set APIFY_TOKEN in your environment
from langchain_apify import ApifyActorsTool
tool = ApifyActorsTool("cliqtomedia/gb-postcode-lookup")
result = tool.invoke({"run_input": {"postcodes": ["SW1A 2AA", "EH99 1SP", "CF99 1SN", "M60 2LA", "B1 1BB"], "includeTerminated": True}})
print(result)The cURL call waits for the results, up to 300 seconds. For longer runs, start a run (/runs) and get the results with a webhook or by checking the run. Apify API docs. LlamaIndex, MCP for AI agents and the JavaScript client: AI agents & MCP.
Data fields (40)
| Field | Type | Meaning |
|---|---|---|
inputIndex | integer | Position of the postcode in your input list (0-based). Rows come back in input order. |
input | string | The postcode as you entered it, with leading and trailing spaces removed (items over 40 characters are cut). |
postcode | string | Normalized postcode, e.g. SW1A 2AA (uppercase, one space). Null for invalid_format rows. |
status | string | found (active postcode), terminated (no longer in use; last known location only), not_found, invalid_format, unsupported_licensed_region (Northern Ireland BT postcodes are not looked up) or error (source unavailable; not a negative answer). |
statusReason | string | Plain-English explanation of the status. |
errorCode | string | Machine code for error rows, e.g. SOURCE_UNAVAILABLE, RATE_LIMITED, DEADLINE_REACHED, MAX_CHARGE_REACHED; null otherwise. |
duplicateOfIndex | integer | For a repeated postcode: inputIndex of its first occurrence (same data, not charged again). Null otherwise. |
latitude | number | WGS84 latitude of the postcode centroid (not a specific address). Null when the source has no grid reference. |
longitude | number | WGS84 longitude of the postcode centroid. |
eastings | integer | OS National Grid easting in metres. |
northings | integer | OS National Grid northing in metres. |
positionalQuality | integer | ONSPD positional quality indicator 1–9 (1 = within the building of a matched address; 9 = no grid reference). |
outcode | string | Outward code, e.g. SW1A. |
incode | string | Inward code, e.g. 2AA. |
country | string | England, Scotland, Wales (or Isle of Man / Channel Islands, without coordinates). |
region | string | English region (former GOR), e.g. London. Null outside England. |
adminDistrict | string | Local authority district / unitary authority, e.g. Westminster. |
adminDistrictCode | string | GSS code of the district, e.g. E09000033. |
adminCounty | string | County (two-tier areas only); null elsewhere. |
adminCountyCode | string | GSS code of the county; null where not applicable. |
adminWard | string | Electoral ward / division. |
adminWardCode | string | GSS code of the ward. |
parish | string | Civil parish or community (unparished areas are labelled as such). |
parishCode | string | GSS code of the parish. |
parliamentaryConstituency | string | Westminster parliamentary constituency (2024 boundaries). |
parliamentaryConstituencyCode | string | GSS code of the constituency (2024 boundaries). |
lsoa | string | Lower Layer Super Output Area name (2021 Census geography). |
lsoaCode | string | LSOA GSS code, e.g. E01004736. |
msoa | string | Middle Layer Super Output Area name. |
msoaCode | string | MSOA GSS code. |
outputArea | string | 2021 Census Output Area code. |
builtUpArea | string | Built-up area name. |
policeForceArea | string | Police force area. |
nhsIcb | string | NHS Integrated Care Board (England). |
ruralUrban2021 | string | 2021 rural-urban classification label. |
introducedOn | string | Month the postcode was introduced, YYYY-MM. |
terminatedOn | string | Month the postcode was terminated, YYYY-MM; null for active postcodes. |
provider | string | Data provider: Postcodes.io (ONS Postcode Directory). |
sourceUrl | string | API endpoint the row was read from; null when no lookup was made. |
fetchedAt | string | ISO 8601 time the data was fetched. |
Input settings (2)
The main fields of the input form. With the API you send them as JSON.
| Setting | Name in the API | Type | Example value |
|---|---|---|---|
| Postcodes required | postcodes | array | ["SW1A 2AA", "EH99 1SP", "CF99 1SN", "M60 2LA", "B1 1BB"] |
| Check terminated postcodes | includeTerminated | boolean | true |
