Free ZIP code API
Type an email and get a key on the next page: 1,000 requests a day, free, with no card and no trial that runs out. Every endpoint is included: ZIP lookup, radius search, distance, ZIP to county, ZIP codes by city and bulk lookup.
Get a free API key
1,000 requests a day, free · no card · the key appears on the next page
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.
curl "https://zipcodes.pro/v1/zip/10001" \
-H "Authorization: Bearer YOUR_API_KEY"const res = await fetch("https://zipcodes.pro/v1/zip/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/zip/10001",
headers={"Authorization": "Bearer YOUR_API_KEY"},
timeout=10,
)
r.raise_for_status()
data = r.json() Response 200
{
"zip": "10001",
"type": "standard",
"type_source": "inferred",
"city": "New York",
"other_place_names": [],
"state": "NY",
"state_name": "New York",
"county": {
"fips": "36061",
"name": "New York County",
"short_name": "New York"
},
"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"
} | Parameter | Meaning |
|---|---|
zippath, required | Five digit ZIP code, or ZIP+4 (95014-2083). |
What you get
- 1,000 requests a day, reset at 00:00 UTC, and 60 a minute.
- Every endpoint and every field, the same data the paid plans get.
- CORS is open, so you can call it from a browser page.
- Bulk lookup of up to 100 ZIP codes in one request.
- Going over the limit returns 429 with the reset time. There is nothing to be billed for.
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.
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.
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"
} Limits
The free plan includes 1,000 requests a day, reset at 00:00 UTC, and 60 a minute. Every response says where you stand:
| 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. |
Over the limit, the API answers 429 with the reset time; nothing is
billed. Paid plans for higher volume (Starter 100,000 a month, Pro 500,000 a month, Business 3,000,000 a month)
are listed on the pricing page and are not on sale yet.
Questions
Is the ZIP code API really free?
Yes. The free plan includes 1,000 requests a day with no card on file, and it does not expire. Paid plans are for higher volume.
How do I get a free API key?
Enter your email in the form on this page. The key is shown on the next page; copy it then, because only a hash of it is stored.
What happens when I reach the daily limit?
Requests return HTTP 429 with error quota_exceeded and the time the quota resets (00:00 UTC). Nothing is charged, and the key works again after the reset.
Can I use it from JavaScript in the browser?
Yes. CORS is open on the free plan. A key in page code is visible to anyone who opens the page, so use it for low-stakes lookups such as filling in a city from a ZIP code.
Where does the data come from?
US Census Bureau files (Gazetteer, ZCTA relationship files, American Community Survey, ZIP Code Business Patterns), IRS statistics, the OMB metro area delineations and GeoNames. Each source and its vintage is listed by the /v1/meta endpoint and on the sources page.
I need every ZIP code at once, not one at a time.
Download the free ZIP code database instead: every ZIP code as CSV, JSON and SQLite under CC BY 4.0, from the same data release the API serves.
More
- Full API reference: every endpoint, error and header.
- No code needed? Free ZIP code database download.
- ZIP code API
- ZIP code radius API
- ZIP to county API
- ZIP code distance API