Postal Codes API

Postal code search, validation, and spatial queries for 124 countries

Search, validate, and look up postal codes worldwide. Covers 124 countries with 4.25 million records including place names, administrative divisions, and geographic coordinates. Spatial queries let you find codes near a point or within a polygon.

124 Countries, 4.25M+ Records

Comprehensive postal code coverage from regularly updated sources. Place names, admin divisions, and coordinates for every record.

Format & Existence Validation

Validate postal codes by regex format or check actual existence in the database. Batch validate up to 100 codes across multiple countries in one request.

Spatial Queries (PostGIS)

Find postal codes within a GeoJSON polygon or near a GPS coordinate with distance calculations. Powered by PostGIS for accurate haversine distances.

Multi-Level Admin Divisions

Each record includes up to three levels of administrative divisions (state, county, municipality) for detailed geographic context.

Use Cases

Built for real-world scenarios

Quick Start

Start using this API in seconds

Look Up a Postal Code

# Look up a US ZIP code
$ curl "https://api.apicrate.io/api/v1/postal/US/90210" \
  -H "X-API-Key: YOUR_API_KEY"

# Response
{
  "status": "ok",
  "data": [
    {
      "postal_code": "90210",
      "place_name": "Beverly Hills",
      "admin_name1": "California",
      "admin_code1": "CA",
      "admin_name2": "Los Angeles",
      "admin_code2": "037",
      "admin_name3": null,
      "admin_code3": null,
      "latitude": 34.0901,
      "longitude": -118.4065,
      "accuracy": 4
    }
  ]
}
import requests

# Look up a German postal code
response = requests.get(
    "https://api.apicrate.io/api/v1/postal/DE/10115",
    headers={"X-API-Key": "YOUR_API_KEY"},
)

for place in response.json()["data"]:
    print(f"{place['postal_code']}: {place['place_name']}, {place['admin_name1']}")
    # 10115: Mitte, Berlin
// Look up a UK postcode
const res = await fetch("https://api.apicrate.io/api/v1/postal/GB/SW1A 1AA", {
  headers: { "X-API-Key": "YOUR_API_KEY" },
});

const { data } = await res.json();
console.log(data[0].place_name);  // "London"
console.log(data[0].admin_name1); // "England"

Search Postal Codes

# Search UK postal codes by place name
$ curl "https://api.apicrate.io/api/v1/postal/GB/search?q=Westminster&limit=3" \
  -H "X-API-Key: YOUR_API_KEY"

# Response
{
  "status": "ok",
  "data": [
    {
      "postal_code": "SW1A 0AA",
      "place_name": "Westminster",
      "admin_name1": "England",
      "admin_code1": "ENG",
      "latitude": 51.4995,
      "longitude": -0.1248,
      "accuracy": 6
    }
  ],
  "total": 42
}
import requests

# Search by postal code prefix and admin code
response = requests.get(
    "https://api.apicrate.io/api/v1/postal/US/search",
    headers={"X-API-Key": "YOUR_API_KEY"},
    params={"prefix": "902", "admin_code": "CA", "limit": 5},
)

data = response.json()
print(f"Found {data['total']} codes starting with 902 in California")
for place in data["data"]:
    print(f"  {place['postal_code']}: {place['place_name']}")
// Search Polish postal codes by place name
const res = await fetch(
  "https://api.apicrate.io/api/v1/postal/PL/search?q=Kraków&limit=5",
  { headers: { "X-API-Key": "YOUR_API_KEY" } },
);

const { data, total } = await res.json();
console.log(`Found ${total} results`);
data.forEach((p) => console.log(`${p.postal_code}: ${p.place_name}`));

Batch Validate

# Validate multiple codes across countries in one request
$ curl -X POST "https://api.apicrate.io/api/v1/postal/validate" \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "codes": [
      {"country_code": "US", "postal_code": "90210"},
      {"country_code": "GB", "postal_code": "SW1A 1AA"},
      {"country_code": "DE", "postal_code": "XXXXX"}
    ]
  }'

# Response
{
  "status": "ok",
  "data": [
    {"postal_code": "90210",    "country_code": "US", "format_valid": true,  "exists": true},
    {"postal_code": "SW1A 1AA", "country_code": "GB", "format_valid": true,  "exists": true},
    {"postal_code": "XXXXX",    "country_code": "DE", "format_valid": false, "exists": false}
  ]
}
import requests

# Batch validate with format + existence checks
response = requests.post(
    "https://api.apicrate.io/api/v1/postal/validate",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={
        "codes": [
            {"country_code": "US", "postal_code": "90210"},
            {"country_code": "PL", "postal_code": "00-001"},
            {"country_code": "US", "postal_code": "00000"},
        ]
    },
)

for result in response.json()["data"]:
    status = "valid" if result["format_valid"] and result["exists"] else "invalid"
    print(f"{result['country_code']} {result['postal_code']}: {status}")
// Validate a list of postal codes from a form submission
const codes = [
  { country_code: "FR", postal_code: "75001" },
  { country_code: "JP", postal_code: "100-0001" },
  { country_code: "AU", postal_code: "2000" },
];

const res = await fetch("https://api.apicrate.io/api/v1/postal/validate", {
  method: "POST",
  headers: {
    "X-API-Key": "YOUR_API_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ codes }),
});

const { data } = await res.json();
const invalid = data.filter((r) => !r.format_valid || !r.exists);
console.log(`${invalid.length} invalid codes found`);

Find Nearby Postal Codes

# Find postal codes within 5 km of central London
$ curl -X POST "https://api.apicrate.io/api/v1/postal/GB/nearby" \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"lat": 51.5074, "lng": -0.1278, "radius_km": 5, "limit": 3}'

# Response — ordered by distance
{
  "status": "ok",
  "data": [
    {
      "postal_code": "WC2N 5DU",
      "place_name": "London",
      "admin_name1": "England",
      "latitude": 51.5074,
      "longitude": -0.1278,
      "distance_km": 0.012
    },
    {
      "postal_code": "WC2E 8HD",
      "place_name": "London",
      "latitude": 51.5112,
      "longitude": -0.1215,
      "distance_km": 0.593
    }
  ],
  "total_count": 847,
  "limit": 3,
  "offset": 0
}
import requests

# Find US ZIP codes within 10 km of a store location
response = requests.post(
    "https://api.apicrate.io/api/v1/postal/US/nearby",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={"lat": 40.7128, "lng": -74.0060, "radius_km": 10},
)

data = response.json()
print(f"{data['total_count']} postal codes within 10 km")
for place in data["data"][:5]:
    print(f"  {place['postal_code']} — {place['place_name']} ({place['distance_km']} km)")
// Find nearby postal codes for a store locator
const res = await fetch("https://api.apicrate.io/api/v1/postal/DE/nearby", {
  method: "POST",
  headers: {
    "X-API-Key": "YOUR_API_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ lat: 52.52, lng: 13.405, radius_km: 3 }),
});

const { data, total_count } = await res.json();
console.log(`${total_count} codes near Berlin center`);
data.forEach((p) => console.log(`${p.postal_code}: ${p.distance_km} km`));

Find Postal Codes Within a Polygon

# Find US postal codes inside a GeoJSON polygon (Manhattan bounding box)
$ curl -X POST "https://api.apicrate.io/api/v1/postal/US/within" \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "geometry": {
      "type": "Polygon",
      "coordinates": [[
        [-74.02, 40.70], [-73.97, 40.70], [-73.93, 40.78],
        [-73.94, 40.82], [-74.02, 40.78], [-74.02, 40.70]
      ]]
    },
    "limit": 5
  }'

# Response
{
  "status": "ok",
  "data": [
    {
      "postal_code": "10001",
      "place_name": "New York City",
      "admin_name1": "New York",
      "admin_code1": "NY",
      "latitude": 40.7484,
      "longitude": -73.9967
    }
  ],
  "total_count": 87,
  "limit": 5,
  "offset": 0
}
import requests

# Find all postal codes within a delivery zone
delivery_zone = {
    "type": "Polygon",
    "coordinates": [[
        [2.25, 48.81], [2.42, 48.81], [2.42, 48.90],
        [2.25, 48.90], [2.25, 48.81],
    ]],
}

response = requests.post(
    "https://api.apicrate.io/api/v1/postal/FR/within",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={"geometry": delivery_zone, "limit": 1000},
)

data = response.json()
codes = [p["postal_code"] for p in data["data"]]
print(f"{data['total_count']} postal codes in delivery zone: {codes[:5]}...")
// Find postal codes within a sales territory polygon
const res = await fetch("https://api.apicrate.io/api/v1/postal/GB/within", {
  method: "POST",
  headers: {
    "X-API-Key": "YOUR_API_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    geometry: {
      type: "Polygon",
      coordinates: [[
        [-0.15, 51.49], [-0.07, 51.49], [-0.07, 51.53],
        [-0.15, 51.53], [-0.15, 51.49],
      ]],
    },
  }),
});

const { data, total_count } = await res.json();
console.log(`${total_count} postal codes in territory`);

List Postal Systems

# Browse all supported postal systems
$ curl "https://api.apicrate.io/api/v1/postal?limit=3&fields=country,country_code,format,total_codes" \
  -H "X-API-Key: YOUR_API_KEY"

# Response
{
  "status": "ok",
  "data": [
    {"country": "Andorra",    "country_code": "AD", "format": "AD###", "total_codes": 7},
    {"country": "Argentina",  "country_code": "AR", "format": "@####@@@", "total_codes": 20261},
    {"country": "Austria",    "country_code": "AT", "format": "####",  "total_codes": 2213}
  ],
  "total": 124
}
import requests

# Find countries using 5-digit postal codes
response = requests.get(
    "https://api.apicrate.io/api/v1/postal",
    headers={"X-API-Key": "YOUR_API_KEY"},
    params={"format_like": "#####", "fields": "country,country_code,format"},
)

data = response.json()
print(f"{data['total']} countries use 5-digit codes:")
for system in data["data"]:
    print(f"  {system['country_code']}: {system['country']} ({system['format']})")
// List all postal systems with their regex patterns
const res = await fetch(
  "https://api.apicrate.io/api/v1/postal?limit=500&fields=country_code,regex,total_records",
  { headers: { "X-API-Key": "YOUR_API_KEY" } },
);

const { data } = await res.json();
const bySize = data.sort((a, b) => b.total_records - a.total_records);
console.log("Top 5 by record count:");
bySize.slice(0, 5).forEach((s) =>
  console.log(`  ${s.country_code}: ${s.total_records.toLocaleString()} records`),
);
Endpoints

Available endpoints

GET /api/v1/postal List postal systems
POST /api/v1/postal/validate Validate postal codes (batch)
GET /api/v1/postal/{country_code} Get postal system for a country
GET /api/v1/postal/{country_code}/codes List all postal codes for a country
GET /api/v1/postal/{country_code}/search Search postal codes
POST /api/v1/postal/{country_code}/within Find postal codes within a polygon
POST /api/v1/postal/{country_code}/nearby Find postal codes near a point
GET /api/v1/postal/{country_code}/validate/{postal_code} Quick-validate a postal code
GET /api/v1/postal/{country_code}/{postal_code} Look up a postal code
FAQ

Frequently asked questions

How many countries are covered?

The API covers 124 countries with 4.25 million postal code records from regularly updated sources.

What is the difference between format validation and existence validation?

Format validation (GET /postal/{country}/validate/{code}) checks if a postal code matches the country's regex pattern — it's a fast, lightweight check. Batch validation (POST /postal/validate) also checks if the code actually exists in the database, giving you both format_valid and exists fields.

Are alpha-3 country codes supported?

Yes. All endpoints accept both alpha-2 (US) and alpha-3 (USA) country codes interchangeably.

How do the spatial queries work?

The nearby endpoint (POST /postal/{country}/nearby) finds postal codes within a radius of a GPS point, returning results ordered by distance. The within endpoint (POST /postal/{country}/within) finds postal codes inside a GeoJSON polygon or multi-polygon. Both use PostGIS for accurate geographic calculations.

How accurate are the coordinates?

Accuracy varies by country and is indicated by the accuracy field: 0 = address-level sourced, 1 = estimated, 4 = place ID, 6 = centroid of the postal code area.

Can one postal code return multiple places?

Yes. A single postal code can map to multiple places (e.g., a ZIP code covering several neighborhoods). The lookup endpoint returns all matching records as a list.

Ready to start building?

Create a free account and start making requests in under a minute.

Sign Up Free → View Documentation