ZIP code API reference
A JSON REST API for 41,711 US ZIP codes. Base URL
https://zipcodes.pro, every endpoint under /v1, a key in the
Authorization header. The responses below are what the API
returned when this page was generated, from data release 2026-10-22.
Contents
- Quick start
- Authentication
- Look up a ZIP code
- ZIP codes for a city
- ZIP codes within a radius
- Distance between two ZIP codes
- ZIP codes in a county
- Bulk lookup
- Release and sources
- Your usage
- Limits and plans
- Errors
- OpenAPI spec
Quick start
- Get a free key below. It is shown once, on the next page.
- Send it as a bearer token:
Authorization: Bearer zk_live_… - Call any endpoint. Free keys include 1,000 requests a day.
Get a free API key
1,000 requests a day, free · no card · the key appears on the next page
Authentication
Every /v1 endpoint needs a key, sent as
Authorization: Bearer YOUR_API_KEY. A ?key= query parameter
is also accepted for cases where a header cannot be set, but it ends up in logs
and browser history, so prefer the header. Keys start zk_live_. Only a
hash of each key is stored: a lost key cannot be recovered, only replaced.
CORS is open on the free plan, so a key works from browser code.
Look up a ZIP code
GET /v1/zip/{zip}
Everything about one ZIP code: USPS city, other place names, state, every county it touches with a weight, coordinates, time zone, metro area, census demographics, business counts, IRS figures and the nearest ZIP codes. ZIP+4 input is accepted and cut to five digits.
| Parameter | Meaning |
|---|---|
zippath, required | Five digit ZIP code, or ZIP+4 (95014-2083). |
curl "https://zipcodes.pro/v1/zip/95014" \
-H "Authorization: Bearer YOUR_API_KEY"const res = await fetch("https://zipcodes.pro/v1/zip/95014", {
headers: { Authorization: "Bearer YOUR_API_KEY" },
});
if (!res.ok) throw new Error((await res.json()).message);
const data = await res.json();import requests
r = requests.get(
"https://zipcodes.pro/v1/zip/95014",
headers={"Authorization": "Bearer YOUR_API_KEY"},
timeout=10,
)
r.raise_for_status()
data = r.json() Response 200
{
"zip": "95014",
"type": "standard",
"type_source": "inferred",
"city": "Cupertino",
"other_place_names": [
"San Jose",
"Sunnyvale"
],
"state": "CA",
"state_name": "California",
"county": {
"fips": "06085",
"name": "Santa Clara County",
"short_name": "Santa Clara"
},
"counties": [
{
"county_fips": "06085",
"name": "Santa Clara County",
"short_name": "Santa Clara",
"weight": 1,
"basis": "land_area"
}
],
"location": {
"lat": 37.308354,
"lng": -122.081882,
"source": "gazetteer_internal_point",
"timezone": "America/Los_Angeles"
},
"is_zcta": true,
"parent_zcta": null,
"metro": {
"cbsa_code": "41940",
"name": "San Jose-Sunnyvale-Santa Clara, CA",
"type": "metro",
"csa_code": "488",
"csa_name": "San Jose-San Francisco-Oakland, CA"
},
"demographics": {
"population": 60932,
"households": 21868,
"housing_units": 23104,
"median_age": 41,
"median_household_income": 230913,
"median_home_value": 2000001,
"median_rent": 3501,
"pct_bachelors_plus": 81.9,
"mean_commute_min": 23.7,
"density_per_sqmi": 2451
},
"business": {
"establishments": 1615,
"employees": 59243,
"annual_payroll_thousands": 22133258
},
"tax": {
"irs_returns": 27000,
"irs_agi_thousands": 8673961
},
"area": {
"land_sqmi": 24.86,
"water_sqmi": 0.131
},
"nearest_zips": [
{
"zip": "95015",
"miles": 1.9,
"city": "Cupertino",
"state": "CA"
},
{
"zip": "94024",
"miles": 3,
"city": "Los Altos",
"state": "CA"
},
{
"zip": "94087",
"miles": 3.9,
"city": "Sunnyvale",
"state": "CA"
},
"… 7 more"
],
"release": "2026-10-22"
} ZIP codes for a city
GET /v1/zips
Every ZIP code whose USPS city or other place names match, in one state.
| Parameter | Meaning |
|---|---|
cityquery, required | City name, any case: Cupertino. |
statequery, required | Two letter state code: CA. |
curl "https://zipcodes.pro/v1/zips?city=Cupertino&state=CA" \
-H "Authorization: Bearer YOUR_API_KEY"const res = await fetch("https://zipcodes.pro/v1/zips?city=Cupertino&state=CA", {
headers: { Authorization: "Bearer YOUR_API_KEY" },
});
if (!res.ok) throw new Error((await res.json()).message);
const data = await res.json();import requests
r = requests.get(
"https://zipcodes.pro/v1/zips",
params={"city": "Cupertino", "state": "CA"},
headers={"Authorization": "Bearer YOUR_API_KEY"},
timeout=10,
)
r.raise_for_status()
data = r.json() Response 200
{
"city": "Cupertino",
"state": "CA",
"count": 2,
"zips": [
{
"zip": "95014",
"type": "standard",
"usps_city": "Cupertino",
"state": "CA",
"county_fips": "06085",
"timezone": "America/Los_Angeles",
"lat": 37.308354,
"lng": -122.081882,
"population": 60932,
"median_household_income": 230913,
"median_home_value": 2000001,
"land_sqmi": 24.86,
"density_per_sqmi": 2451
},
{
"zip": "95015",
"type": "unknown",
"usps_city": "Cupertino",
"state": "CA",
"county_fips": "06085",
"timezone": "America/Los_Angeles",
"lat": 37.323,
"lng": -122.0527,
"population": null,
"median_household_income": null,
"median_home_value": null,
"land_sqmi": null,
"density_per_sqmi": null
}
]
} ZIP codes within a radius
GET /v1/radius
Every ZIP code whose centre point is within the radius, nearest first, each with its distance in miles. Measure from a ZIP code or from any coordinate.
| Parameter | Meaning |
|---|---|
zipquery, optional | Centre ZIP code. Either this, or lat and lng. |
latquery, optional | Centre latitude, −90 to 90. |
lngquery, optional | Centre longitude, −180 to 180. |
milesquery, optional | Radius in miles, above 0 and up to 500. Default 10. |
curl "https://zipcodes.pro/v1/radius?zip=95014&miles=5" \
-H "Authorization: Bearer YOUR_API_KEY"const res = await fetch("https://zipcodes.pro/v1/radius?zip=95014&miles=5", {
headers: { Authorization: "Bearer YOUR_API_KEY" },
});
if (!res.ok) throw new Error((await res.json()).message);
const data = await res.json();import requests
r = requests.get(
"https://zipcodes.pro/v1/radius",
params={"zip": "95014", "miles": "5"},
headers={"Authorization": "Bearer YOUR_API_KEY"},
timeout=10,
)
r.raise_for_status()
data = r.json() Response 200
{
"origin": {
"zip": "95014",
"lat": 37.308354,
"lng": -122.081882
},
"miles": 5,
"count": 11,
"zips": [
{
"zip": "95014",
"type": "standard",
"usps_city": "Cupertino",
"state": "CA",
"county_fips": "06085",
"timezone": "America/Los_Angeles",
"lat": 37.308354,
"lng": -122.081882,
"population": 60932,
"median_household_income": 230913,
"median_home_value": 2000001,
"land_sqmi": 24.86,
"density_per_sqmi": 2451,
"miles": 0
},
{
"zip": "95015",
"type": "unknown",
"usps_city": "Cupertino",
"state": "CA",
"county_fips": "06085",
"timezone": "America/Los_Angeles",
"lat": 37.323,
"lng": -122.0527,
"population": null,
"median_household_income": null,
"median_home_value": null,
"land_sqmi": null,
"density_per_sqmi": null,
"miles": 1.896
},
{
"zip": "94024",
"type": "standard",
"usps_city": "Los Altos",
"state": "CA",
"county_fips": "06085",
"timezone": "America/Los_Angeles",
"lat": 37.350388,
"lng": -122.098686,
"population": 23051,
"median_household_income": 250001,
"median_home_value": 2000001,
"land_sqmi": 8.273,
"density_per_sqmi": 2786.3,
"miles": 3.047
},
"… 8 more"
]
} Distance between two ZIP codes
GET /v1/distance
Great-circle distance in miles and kilometres between the two ZIP codes' published centre points, with each end's city, state and coordinates.
| Parameter | Meaning |
|---|---|
fromquery, required | Five digit ZIP code. |
toquery, required | Five digit ZIP code. |
curl "https://zipcodes.pro/v1/distance?from=95014&to=10001" \
-H "Authorization: Bearer YOUR_API_KEY"const res = await fetch("https://zipcodes.pro/v1/distance?from=95014&to=10001", {
headers: { Authorization: "Bearer YOUR_API_KEY" },
});
if (!res.ok) throw new Error((await res.json()).message);
const data = await res.json();import requests
r = requests.get(
"https://zipcodes.pro/v1/distance",
params={"from": "95014", "to": "10001"},
headers={"Authorization": "Bearer YOUR_API_KEY"},
timeout=10,
)
r.raise_for_status()
data = r.json() Response 200
{
"from": {
"zip": "95014",
"city": "Cupertino",
"state": "CA",
"lat": 37.308354,
"lng": -122.081882
},
"to": {
"zip": "10001",
"city": "New York",
"state": "NY",
"lat": 40.750649,
"lng": -73.997298
},
"miles": 2559.524,
"kilometers": 4119.155,
"method": "great-circle distance between the ZIP codes' published centre points"
} ZIP codes in a county
GET /v1/county/{fips}/zips
Every ZIP code in a county, by five digit county FIPS code (state FIPS + county FIPS).
| Parameter | Meaning |
|---|---|
fipspath, required | Five digit county FIPS code: 06085 is Santa Clara County, CA. |
curl "https://zipcodes.pro/v1/county/06085/zips" \
-H "Authorization: Bearer YOUR_API_KEY"const res = await fetch("https://zipcodes.pro/v1/county/06085/zips", {
headers: { Authorization: "Bearer YOUR_API_KEY" },
});
if (!res.ok) throw new Error((await res.json()).message);
const data = await res.json();import requests
r = requests.get(
"https://zipcodes.pro/v1/county/06085/zips",
headers={"Authorization": "Bearer YOUR_API_KEY"},
timeout=10,
)
r.raise_for_status()
data = r.json() Response 200
{
"county_fips": "06085",
"count": 109,
"zips": [
{
"zip": "94022",
"type": "standard",
"usps_city": "Los Altos",
"state": "CA",
"county_fips": "06085",
"timezone": "America/Los_Angeles",
"lat": 37.35536,
"lng": -122.144935,
"population": 20027,
"median_household_income": 250001,
"median_home_value": 2000001,
"land_sqmi": 17.689,
"density_per_sqmi": 1132.2
},
{
"zip": "94023",
"type": "unknown",
"usps_city": "Los Altos",
"state": "CA",
"county_fips": "06085",
"timezone": "America/Los_Angeles",
"lat": 37.3852,
"lng": -122.1141,
"population": null,
"median_household_income": null,
"median_home_value": null,
"land_sqmi": null,
"density_per_sqmi": null
},
{
"zip": "94024",
"type": "standard",
"usps_city": "Los Altos",
"state": "CA",
"county_fips": "06085",
"timezone": "America/Los_Angeles",
"lat": 37.350388,
"lng": -122.098686,
"population": 23051,
"median_household_income": 250001,
"median_home_value": 2000001,
"land_sqmi": 8.273,
"density_per_sqmi": 2786.3
},
"… 106 more"
]
} Bulk lookup
POST /v1/bulk
Many ZIP codes in one request (100 on Free, 1,000 on paid plans). Results come back in the order you sent them, with null for a ZIP code that does not exist, and lists of what was not found or not valid.
| Parameter | Meaning |
|---|---|
zipsbody, required | JSON array of ZIP code strings: {"zips": ["95014", "10001"]}. |
curl -X POST "https://zipcodes.pro/v1/bulk" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"zips":["95014","10001","99999"]}'const res = await fetch("https://zipcodes.pro/v1/bulk", {
method: "POST",
headers: {
Authorization: "Bearer YOUR_API_KEY",
"Content-Type": "application/json",
},
body: JSON.stringify({"zips":["95014","10001","99999"]}),
});
const data = await res.json();import requests
r = requests.post(
"https://zipcodes.pro/v1/bulk",
headers={"Authorization": "Bearer YOUR_API_KEY"},
json={"zips":["95014","10001","99999"]},
timeout=10,
)
r.raise_for_status()
data = r.json() Response 200
{
"requested": 3,
"found": 2,
"not_found": [
"99999"
],
"invalid": [],
"results": [
{
"zip": "95014",
"type": "standard",
"type_source": "inferred",
"city": "Cupertino",
"other_place_names": [
"San Jose",
"Sunnyvale"
],
"state": "CA",
"state_name": null,
"county": null,
"counties": [
{
"county_fips": "06085",
"name": "Santa Clara County",
"short_name": "Santa Clara",
"weight": 1,
"basis": "land_area"
}
],
"location": {
"lat": 37.308354,
"lng": -122.081882,
"source": "gazetteer_internal_point",
"timezone": "America/Los_Angeles"
},
"is_zcta": true,
"parent_zcta": null,
"metro": {
"cbsa_code": "41940",
"name": "San Jose-Sunnyvale-Santa Clara, CA",
"type": "metro",
"csa_code": "488",
"csa_name": "San Jose-San Francisco-Oakland, CA"
},
"demographics": {
"population": 60932,
"households": 21868,
"housing_units": 23104,
"median_age": 41,
"median_household_income": 230913,
"median_home_value": 2000001,
"median_rent": 3501,
"pct_bachelors_plus": 81.9,
"mean_commute_min": 23.7,
"density_per_sqmi": 2451
},
"business": {
"establishments": 1615,
"employees": 59243,
"annual_payroll_thousands": 22133258
},
"tax": {
"irs_returns": 27000,
"irs_agi_thousands": 8673961
},
"area": {
"land_sqmi": 24.86,
"water_sqmi": 0.131
},
"nearest_zips": [
{
"zip": "95015",
"miles": 1.9,
"city": "Cupertino",
"state": "CA"
},
{
"zip": "94024",
"miles": 3,
"city": "Los Altos",
"state": "CA"
},
{
"zip": "94087",
"miles": 3.9,
"city": "Sunnyvale",
"state": "CA"
},
"… 7 more"
],
"release": "2026-10-22"
},
{
"zip": "10001",
"type": "standard",
"type_source": "inferred",
"city": "New York",
"other_place_names": [],
"state": "NY",
"state_name": null,
"county": null,
"counties": [
{
"county_fips": "36061",
"name": "New York County",
"short_name": "New York",
"weight": 1,
"basis": "land_area"
}
],
"location": {
"lat": 40.750649,
"lng": -73.997298,
"source": "gazetteer_internal_point",
"timezone": "America/New_York"
},
"is_zcta": true,
"parent_zcta": null,
"metro": {
"cbsa_code": "35620",
"name": "New York-Newark-Jersey City, NY-NJ",
"type": "metro",
"csa_code": "408",
"csa_name": "New York-Newark, NY-NJ-CT-PA"
},
"demographics": {
"population": 30511,
"households": 15737,
"housing_units": 18356,
"median_age": 35.8,
"median_household_income": 129852,
"median_home_value": 561100,
"median_rent": 3361,
"pct_bachelors_plus": 72.6,
"mean_commute_min": 26,
"density_per_sqmi": 48895.8
},
"business": {
"establishments": 6325,
"employees": 173194,
"annual_payroll_thousands": 21557555
},
"tax": {
"irs_returns": 16960,
"irs_agi_thousands": 4479865
},
"area": {
"land_sqmi": 0.624,
"water_sqmi": 0
},
"nearest_zips": [
{
"zip": "10199",
"miles": 0.1,
"city": "New York",
"state": "NY"
},
{
"zip": "10119",
"miles": 0.2,
"city": "New York",
"state": "NY"
},
{
"zip": "10122",
"miles": 0.3,
"city": "New York",
"state": "NY"
},
"… 7 more"
],
"release": "2026-10-22"
},
null
]
} Release and sources
GET /v1/meta
The data release the API is serving, every source with its vintage and licence, and field coverage.
curl "https://zipcodes.pro/v1/meta" \
-H "Authorization: Bearer YOUR_API_KEY"const res = await fetch("https://zipcodes.pro/v1/meta", {
headers: { Authorization: "Bearer YOUR_API_KEY" },
});
if (!res.ok) throw new Error((await res.json()).message);
const data = await res.json();import requests
r = requests.get(
"https://zipcodes.pro/v1/meta",
headers={"Authorization": "Bearer YOUR_API_KEY"},
timeout=10,
)
r.raise_for_status()
data = r.json() Response 200
{
"release": {
"release": "2026-10-22",
"built_at": "2026-10-22T16:19:31+00:00",
"zip_count": 41711,
"zcta_count": 33791,
"added": 0,
"retired": 0,
"modified": 0,
"notes": null
},
"sources": [
{
"key": "gazetteer_county_2020",
"name": "Census 2020 Gazetteer county file",
"url": "https://www2.census.gov/geo/docs/maps-data/data/gazetteer/2020_Gazetteer/2020_Gaz_counties_national.zip",
"license": "Public domain (U.S. Census Bureau)",
"attribution": "U.S. Census Bureau, Gazetteer Files",
"vintage": "2020 Gazetteer",
"retrieved_at": "2026-10-22"
},
{
"key": "zcta_county_rel",
"name": "Census 2020 ZCTA-to-County relationship file",
"url": "https://www2.census.gov/geo/docs/maps-data/data/rel2020/zcta520/tab20_zcta520_county20_natl.txt",
"license": "Public domain (U.S. Census Bureau)",
"attribution": "U.S. Census Bureau, 2020 ZCTA to County Relationship File",
"vintage": "2020 relationship file",
"retrieved_at": "2026-10-22"
},
{
"key": "zcta_place_rel",
"name": "Census 2020 ZCTA-to-Place relationship file",
"url": "https://www2.census.gov/geo/docs/maps-data/data/rel2020/zcta520/tab20_zcta520_place20_natl.txt",
"license": "Public domain (U.S. Census Bureau)",
"attribution": "U.S. Census Bureau, 2020 ZCTA to Place Relationship File",
"vintage": "2020 relationship file",
"retrieved_at": "2026-10-22"
},
"… 22 more"
],
"coverage": [
{
"field": "cbsa_code",
"populated": 31568,
"total": 41711,
"gap_reason": "A ZIP outside any metro or micro area has none, which is normal."
},
{
"field": "county_fips",
"populated": 41171,
"total": 41711,
"gap_reason": "Military and some territory ZIPs have no county."
},
{
"field": "employees",
"populated": 34944,
"total": 41711,
"gap_reason": "CBP withholds employment where it would disclose a single firm."
},
"… 8 more"
],
"note": "A null value means the source did not publish that figure. It never means zero."
} Your usage
GET /v1/usage
Your plan, requests used and refused in the current quota window, and requests per day this month. Counts as a request.
curl "https://zipcodes.pro/v1/usage" \
-H "Authorization: Bearer YOUR_API_KEY"const res = await fetch("https://zipcodes.pro/v1/usage", {
headers: { Authorization: "Bearer YOUR_API_KEY" },
});
if (!res.ok) throw new Error((await res.json()).message);
const data = await res.json();import requests
r = requests.get(
"https://zipcodes.pro/v1/usage",
headers={"Authorization": "Bearer YOUR_API_KEY"},
timeout=10,
)
r.raise_for_status()
data = r.json() Limits and plans
| Plan | Requests | Window | Per minute | Price |
|---|---|---|---|---|
| Free | 1,000 | per day, reset 00:00 UTC | 60 | Free |
| Starter | 100,000 | per calendar month | 120 | $19/mo, not on sale yet |
| Pro | 500,000 | per calendar month | 600 | $49/mo, not on sale yet |
| Business | 3,000,000 | per calendar month | 2,000 | $149/mo, not on sale yet |
Bulk lookup takes up to 100 ZIP codes a request on Free and 1,000 on paid plans. The quota is a hard stop, not a meter: over it, requests are refused with a 429 until the reset, and nothing is ever billed for them. The per-minute limit is separate, keeps one client from crowding out the rest, and does not use quota.
| Response header | Meaning |
|---|---|
X-RateLimit-Limit | Requests your plan includes in the current window (1,000 a day on Free). |
X-RateLimit-Remaining | Requests left in the window, after this one. |
X-RateLimit-Reset | When the window resets, ISO 8601 in UTC. Free resets at 00:00 UTC. |
Retry-After | Seconds to wait, on a 429 for the per-minute limit only. |
Errors
Every error has the same shape: a stable error code to branch on, a
message a person can act on, and a docs link. For example,
a four digit ZIP code:
GET /v1/zip/9501 → 400
{
"error": "not_a_zip",
"message": "\"9501\" is not a five digit US ZIP code.",
"docs": "https://zipcodes.pro/api/docs"
} | Status | error | When |
|---|---|---|
| 400 | not_a_zip / bad_parameter / too_many_zips | The request is malformed. The message says which parameter and why. |
| 401 | missing_key / invalid_key | No key, a key that does not exist, or a revoked one. |
| 404 | not_found | No such ZIP code or county in the current release. |
| 422 | no_coordinate | The ZIP code exists but has no published centre point to measure from. |
| 429 | rate_limited | Too many requests this minute. Wait Retry-After seconds; it does not use quota. |
| 429 | quota_exceeded | The plan's allowance is used up until reset_at. Nothing is billed; requests are refused instead. |
A field the source never published is null. It never means zero.
OpenAPI spec
The machine-readable spec is at https://zipcodes.pro/v1/openapi.json
(OpenAPI 3.1, no key needed), for code generators and API clients.
Guides
- ZIP code API
- Free ZIP code API
- ZIP code radius API
- ZIP to county API
- ZIP code distance API
- Free ZIP code database download (every ZIP code at once, CC BY 4.0)