Try it Live — No Signup

API Documentation

Complete US & Canadian postal code data with optional demographic, census, and geographic enrichments.

https://api.zip-codes.com
Open in Postman → Download JSON Download OpenAPI Spec

Introduction

The ZIP Codes API v2 provides comprehensive postal code data for the United States and Canada. Look up ZIP codes, calculate distances, search by radius, and enrich results with census demographics, political districts, school districts, and more.

Base URL: https://api.zip-codes.com. All API endpoints live under the /v2/ prefix. Responses use a consistent JSON envelope with built-in error handling and credit tracking.

Key Features

  • US ZIP & ZIP+4 lookups with demographics, delivery points, and business data
  • Canadian FSA & postal code lookups with province-level data
  • Radius search with cross-border support (US + Canada)
  • Distance calculation between any combination of ZIP codes, FSAs, postal codes, and coordinates
  • Opt-in enrichments including ACS demographics, census boundaries, political districts, school districts, timezone, and more
  • Five batch routes: up to 250 addresses or 100 items on each other route
Want to try it live? Use our Interactive Playground to make API calls directly from your browser.

Authentication

Get a key and review plans at API pricing. Lookup requests require a valid API key. Health, documentation, OpenAPI, Swagger and Postman are public. Send it in the X-Api-Key header or as Authorization: Bearer:

X-Api-Key header (recommended)

Request Header
X-Api-Key: your_api_key

Authorization: Bearer

Request Header
Authorization: Bearer your_api_key

Query parameter (convenience only, e.g. testing in a browser)

Query String
GET /v2/quick-zip?code=90210&key=your_api_key
Use the X-Api-Key header (or Authorization: Bearer) in applications. Two different keys in one request are refused. The key query parameter is supported for quick tests, but keys in URLs can end up in server logs, proxies and browser history.

Request & Response Format

Code inputs (including distance from/to) are strings of at most 50 characters: 5-digit ZIP, ZIP+4 with or without a dash, FSA, full Canadian postal code with an optional space, or lat,lon (latitude -90..90, longitude -180..180). Include flags are split on commas, trimmed, lowercased, normalized from hyphens to underscores, and de-duplicated. Single POST include fields are strings. Authorization: Bearer followed by your key is accepted as well as X-Api-Key; two different keys in one request are refused.

AspectDetails
Content-TypeAll responses are application/json; charset=utf-8. Send Content-Type: application/json; POST bodies under /v2 are treated as JSON even if this header is omitted.
POST BodyNon-batch endpoints accept GET query parameters or POST with a JSON body containing the same parameter names. Body values win; missing body fields fall back to the query. Batch endpoints are POST-only.
CompressionThe API supports Brotli and Gzip response compression. Send Accept-Encoding: br or Accept-Encoding: gzip for smaller payloads.
CachingAll API responses include Cache-Control: no-store. Implement your own caching layer if needed.
CORSCORS allows any origin. Per-key IP and Origin allowlists are enforced when configured.
Request IDEvery response includes an X-Request-Id header and meta.request_id field (a ULID). Include this in support requests for faster troubleshooting.
include ParameterControls what data appears in the response. On /zip and /radius, it adds enrichment data (e.g. include=timezone,census). On /suggest, it filters which entity types are returned (e.g. include=zip,place) — all types are included by default.

Quick Start

Make your first API call in seconds:

cURL
curl -H "X-Api-Key: YOUR_API_KEY" \
  "https://api.zip-codes.com/v2/quick-zip?code=90210"
Python
import requests

resp = requests.get(
    "https://api.zip-codes.com/v2/quick-zip",
    params={"code": "90210"},
    headers={"X-Api-Key": "YOUR_API_KEY"}
)
data = resp.json()
print(data["results"][0]["city"])  # Beverly Hills
JavaScript
const resp = await fetch(
  "https://api.zip-codes.com/v2/quick-zip?code=90210",
  { headers: { "X-Api-Key": "YOUR_API_KEY" } }
);
const data = await resp.json();
console.log(data.results[0].city); // Beverly Hills

Your Dashboard

The API Portal dashboard gives you full control over your account:

  • API Keys — Create, rotate, and configure keys with per-key IP whitelisting and CORS origin restrictions.
  • Usage Analytics — Daily and monthly usage charts, per-key breakdowns, and CSV export for your records.
  • Billing — Manage your subscription tier, purchase credit packs, and download invoices.
  • Settings — Update account details and notification preferences.

Free tier accounts start with 2,500 credits/day. Sign in to get started →

Fields the schema marks nullable are always present and null when they have no value (for example postal_info on a US ZIP, zip_info on a Canadian code, an empty census layer, a suppressed ACS value); every other field is omitted when it has no value (an enrichment that was not requested, an empty notice list). Postal, radius, distance and suggest items are flattened: {success,input?,error?,notices?,...fields}, with no data wrapper. Address retains data; health uses a top-level data object. A well-formed lookup with no match returns HTTP 200 and a per-item error. Single address no-match uses top-level success:false and an error inside results[0].

Response Format

Every API response follows a consistent envelope structure:

Response Envelope
{
  "success": true,
  "request": {
    "code": "90210",
    "include": [
      "timezone"
    ]
  },
  "results": [
    "..."
  ],
  "meta": {
    "request_id": "01M3SX2V9NY9MRRAMWVJG0C09E",
    "version": "2.0.0",
    "timing_ms": 45,
    "credits": {
      "used": 2,
      "remaining": 74998,
      "breakdown": {
        "base": 1,
        "enrichments": {
          "timezone": 1
        }
      },
      "source": "subscription"
    }
  }
}
FieldTypeDescription
successbooleanWhether the request succeeded
requestobjectEcho of parsed request parameters
resultsarrayArray of result objects (varies by endpoint)
errorobject (optional)Omitted on success; whole-request failures contain {code,message,retryable?,url}. Lookup failures may instead have an error inside a result item.
meta.request_idstringULID request identifier (also in X-Request-Id header)
meta.versionstringAPI version: the newest released version (the info.version of the OpenAPI spec)
meta.timing_msnumberTotal processing time in milliseconds
meta.creditsobjectCredit usage breakdown for this request
meta.noticesarrayInformational messages (see Notices)

Notices

The meta.notices array contains non-error informational messages about request processing. Notices may also appear on result items. Only FREE_TIER_LIMIT_CAPPED includes an optional url, linking to pricing; all other notices omit it:

CodeDescription
INPUT_NORMALIZEDInput was reformatted (e.g., whitespace trimmed, case corrected)
FALLBACK_USEDOn quick-zip and distance only: Canadian postal code not found; FSA-level data returned instead
ENRICHMENT_UNAVAILABLERequested enrichment cannot be delivered (no credit charged for the unavailable flag)
ENRICHMENT_FLAG_RENAMEDOn /v2/zip and its batch, per item: a legacy flag on a Canadian code (census, census_YYYY, cd, cd_YYYY) was answered under statcan. Charged as statcan; asking for both returns one block, charged once
ENRICHMENT_FLAG_RETIREDReserved, not sent yet. Include flags, years, fields and endpoints are never removed from /v2: one that is retired is still accepted and answered with this notice (with its data while it exists, otherwise not charged)
VALIDATION_UNKNOWN_ENRICHMENTUnknown enrichment flags ignored without charge; a notice, not an HTTP 400 error
REVERSE_GEOCODECoordinate was reverse-geocoded to a postal code
BATCH_DUPLICATES_DETECTEDDuplicates are processed and billed individually

Address Matching

Parse and validate a raw US street address against USPS ZIP+4 delivery-point data. Returns the standardized address, ZIP+4 code, address components, and delivery metadata. Handles partial input — with a ZIP code, the city and state are optional; without one, give the city and state.

GET /v2/address
POST/v2/address

Parameters

ParameterTypeRequiredDescription
addressstringRequiredRaw street address to match: at most 500 raw characters and 9–300 after cleanup. Accepts full or partial addresses (e.g., 1600 Pennsylvania Ave NW, Washington DC 20500 or 350 5th Ave, New York).
includestringOptionalComma-separated enrichments: coordinates, census, cd, state_leg, school_district, timezone, and the tract ACS flags below.
Flexible input: The parser handles a wide range of address formats. You can omit ZIP code, state, or both — the engine resolves the address from city and street alone when unambiguous. However, providing more detail improves match accuracy.

Credit Cost

Base 1 credit per request, whether or not a match is found. Requesting coordinates adds 1 credit when a location is returned. Each boundary flag and each tract ACS profile and year adds 1 credit only when delivered. Credit availability is checked against the requested enrichments before matching.

Calculate your cost

Include Flags

Paths below are relative to results[0].data for single calls and results[i].data for batch. GET takes a comma-separated list; POST /v2/address requires a string (an array returns HTTP 400); batch accepts a string or array. All flags are optional; the default is no enrichments.

FlagWhat it addsPath below dataCredits
coordinatesZIP+4 point, latitude/longitude (6 decimals)location+1 when returned
census[_YYYY]Counties, subdivisions, places, tracts, block groups, metro areas/divisionscensus.{current|YYYY}+1 per delivered year
cd[_YYYY]Congressional districtscongressional_district.{current|YYYY}+1 per delivered year
state_leg[_YYYY]State upper/lower legislative districtsstate_legislative.{current|YYYY}.upper / lower+1 per delivered year
school_districtNCES school districtsschool_district.current+1 when delivered
acs_tract_demographic[_YYYY]DP05 tract profileacs.tract.{current|YYYY}.demographic+1 per delivered profile/year
acs_tract_social[_YYYY]DP02 tract profileacs.tract.{current|YYYY}.social+1 per delivered profile/year
acs_tract_economic[_YYYY]DP03 tract profileacs.tract.{current|YYYY}.economic+1 per delivered profile/year
acs_tract_housing[_YYYY]DP04 tract profileacs.tract.{current|YYYY}.housing+1 per delivered profile/year

Boundary Enrichments

Use census for counties, county subdivisions, places, tracts, block groups, metro/micro statistical areas, and metropolitan divisions; cd for congressional districts; state_leg for upper and lower state legislative districts; and school_district for school districts. Add a year to census, cd or state_leg for that year’s boundaries (2010–2025, for example census_2022, cd_2012, state_leg_2020); the bare flags return the latest year (2026). Flags accept either dashes or underscores and are case-insensitive; duplicate flags count once.

All four flags use a spatial lookup for one point: the matched ZIP+4 record's centroid. You do not need to request coordinates; without that flag, data.location is omitted. With coordinates, it contains latitude and longitude rounded to 6 decimals.

Each flag fills its own family beside the others in data, in the same names and order as /v2/zip: census, congressional_district, state_legislative, and school_district. Each family holds its latest year under current, and census_YYYY, cd_YYYY and state_leg_YYYY add that year under its own "YYYY" key beside current, newest first (school_district serves current only and timezone the latest release: neither has year flags). A year flag naming the newest year (cd_2026 or census_2026 today) is answered under current, not under its own key; a requested family with nothing found is present with current: null, and an unrequested family is absent. data.census.current holds arrays county (with is_usps_primary), county_subdivision, place, tract, block_group, metro_micro_cbsa_statistical_area, and metropolitan_cbsa_division (an address lies in one of each, so each list has at most one row). data.congressional_district.current contains congress, districts, and primary:{geoid,state,district_number} with an integer district number. data.state_legislative.current contains legislative_year, upper, and lower arrays. Every spatial block on /v2/address (each census, congressional and state legislative period, school_district and timezone) opens with the same attribution fields in the same order: source, product, attribution. They come from the data file's provenance: the publisher, the data product, and the credit line the publisher asks for; each source's license, product page, vintages and refresh cadence are listed under Spatial layers: full provenance. Census, congressional and state legislative periods then give the TIGER/Line year and vintage. Show attribution wherever you display the data. Point rows carry pct_of_zip, pct_of_area and shared_area_sq_mi as null (a point has no area). details.congressional_district is a different field: the district USPS assigns to the ZIP+4, as USPS publishes it, independent of the cd flag. USPS’s assignment can be wrong; the cd flag’s districts are the districts whose legal boundaries (Census TIGER/Line) contain the matched location. In the newest year, primary is the USPS district when USPS assigns one (basis says so), so it can differ from the district containing the location; earlier years’ primary is always the district containing the location, so compare districts, not primary, across years.

A boundary flag costs one credit per year, only when a usable point exists and all its lookups complete. Empty lookups are null and still count as delivered. No match, a ZIP-level answer without a location, or a failed boundary read makes the affected flag unavailable and uncharged. One ENRICHMENT_UNAVAILABLE notice names all unavailable boundary and tract enrichments; the address answer is still returned.

Tract ACS Enrichments

Use acs_tract_demographic, acs_tract_social, acs_tract_economic, or acs_tract_housing. Unsuffixed flags select the latest ACS year (2024). Append a year, _2011 to _2023, for an earlier release (for example acs_tract_housing_2015); _2024 is also accepted. Supported years are 2011–2024. An unsuffixed flag and its latest-year alias count as one enrichment, for 56 distinct tract profile/year tags.

Profiles describe the census tract that contains the matched address's location. They appear under the geography level, results[].data.acs.tract: current for the latest year, and each earlier year asked for under its own "YYYY" key. Each year block uses the same ACS field names as /v2/zip, with geography_level: "tract" and geo_id of 1400000US followed by its 11-digit tract GEOID. The tract is looked up separately for each year, in that year's Census TIGER/Line tracts, the geography that ACS release is published on: tracts are renumbered and redrawn between releases (the 2020 Census tracts from 2020, Connecticut's planning regions from 2022), so geo_id can differ from year to year for the same address. Each block includes source, product, year, vintage ({year-4}-{year}), and tables.

If the address is unmatched, the match has no location, no tract contains it, or a requested profile has no row for that year, that enrichment is not charged. One ENRICHMENT_UNAVAILABLE notice in meta.notices names the unavailable flags and explains why. A missing profile may be null within a block; data.acs is omitted if no requested profile was delivered. Unsupported tract flags or years are ignored without charge and reported through VALIDATION_UNKNOWN_ENRICHMENT. Tract flags are only available on the address endpoints.

data.school_district.current contains the attribution fields (source, product, attribution), school_year, and districts:[{leaid,name,type,grade_low,grade_high,state_fips,land_area_sq_mi,water_area_sq_mi,centroid?,office?,locale?,nces_codes}]. ACS blocks identify source:"U.S. Census Bureau", product:"ACS 5-Year Estimates Data Profiles", and a five-year vintage. timezone adds data.timezone (after school_district): the same object /v2/zip returns, {source, zones:[{iana_name, name, abbreviation, abbreviation_dst, utc_offset, utc_offset_dst, observes_dst, pct_of_zip}]}, plus the attribution fields (source, product, attribution) from the time zone boundary file (timezone-boundary-builder, newest release; OpenStreetMap-derived, ODbL). The address point lies in one zone, so zones has one entry; its pct_of_zip is null (an address is a point; the key is kept so the shape matches /v2/zip). +1 credit when delivered; with no point, or no time zone data, it is ENRICHMENT_UNAVAILABLE and not charged. Unknown flags (including acs_demographic and unsupported tract years) are ignored free with VALIDATION_UNKNOWN_ENRICHMENT: Unknown enrichment flag(s) ignored: {flags}.

Batch Address Enrichment

POST /v2/address/batch accepts up to 250 addresses and the same flags in the body include (a comma-separated string or array). A paid plan or purchased credits are required. Credits are reserved for every requested enrichment, then unused credits are refunded. Each successfully processed item costs the base credit and only its delivered coordinates, boundary flags and tract profiles; rejected or failed items are not charged. Matched items carry data.census, data.congressional_district, data.state_legislative, data.school_district, data.timezone, and data.acs in the same shapes as the single endpoint. One envelope ENRICHMENT_UNAVAILABLE notice counts processed addresses missing requested enrichments, including unmatched addresses. Unknown flags are ignored without charge and named in VALIDATION_UNKNOWN_ENRICHMENT.

{
  "addresses": [
    "1600 Pennsylvania Ave NW, Washington DC 20500",
    "not a real address 99999"
  ],
  "include": [
    "coordinates",
    "acs_tract_economic"
  ]
}

Items are {success,input:{address},matched,data?,notices?}. An unmatched item is {success:true,input:{address},matched:false}, with no engine reason code; it counts in meta.batch.succeeded. Rejected or failed items have success:false, input, and error, and cost 0 credits. The batch timeout is 45 seconds, as for every request.

Captured batch response (trimmed)
{
  "success": true,
  "request": {
    "addresses_count": 2
  },
  "results": [
    {
      "success": true,
      "input": {
        "address": "1600 Pennsylvania Ave NW, Washington DC 20500"
      },
      "matched": true,
      "data": {
        "confidence": "high",
        "formatted_address": "1600 PENNSYLVANIA AVE NW, WASHINGTON, DC 20500-0005",
        "location": {
          "latitude": 38.898704,
          "longitude": -77.036497
        },
        "acs": {
          "tract": {
            "current": {
              "source": "U.S. Census Bureau",
              "product": "ACS 5-Year Estimates Data Profiles",
              "year": 2024,
              "vintage": "2020-2024",
              "geo_id": "1400000US11001980000",
              "geography_level": "tract",
              "tables": {
                "economic": "DP03"
              },
              "economic": {
                "commuting": {
                  "drove_alone": {
                    "column": "DP03_0019",
                    "est": 12,
                    "moe": 18,
                    "pct": 70.6,
                    "pct_moe": 8.0
                  },
                  "public_transportation": {
                    "column": "DP03_0021",
                    "est": 5,
                    "moe": 8,
                    "pct": 29.4,
                    "pct_moe": 8.0
                  }
                }
              }
            }
          }
        }
      },
      "notices": [
        {
          "code": "MATCHED",
          "message": "Address matched.",
          "class": "match"
        }
      ]
    },
    {
      "success": true,
      "input": {
        "address": "not a real address 99999"
      },
      "matched": false
    }
  ],
  "meta": {
    "request_id": "01M3SX2VE1BEM6ACR6DME1GXK2",
    "version": "2.0.0",
    "timing_ms": 53,
    "credits": {
      "used": 5,
      "source": "subscription",
      "remaining": 1485722,
      "breakdown": {
        "base": 0,
        "enrichments": {},
        "note": "2 addresses x 1 base + 3 enrichment = 5 credits"
      }
    },
    "notices": [
      {
        "code": "ENRICHMENT_UNAVAILABLE",
        "message": "Enrichment(s) unavailable: acs_tract_economic for 1 of 2 processed addresses; matched location or requested enrichment data is unavailable; not charged."
      }
    ],
    "batch": {
      "requested": 2,
      "succeeded": 2,
      "failed": 0
    }
  }
}

Example Request

cURL
curl -H "X-Api-Key: YOUR_API_KEY" \
  "https://api.zip-codes.com/v2/address?address=9641+Sunset+Blvd%2C+Beverly+Hills%2C+CA+90210&include=coordinates,census,cd,state_leg,school_district,acs_tract_demographic,acs_tract_housing_2023"
Open in Playground

Example Response

Captured response excerpt: fields and ACS profiles are trimmed; the original eight-credit charge is retained.

Response
{
  "success": true,
  "request": {
    "address": "9641 Sunset Blvd, Beverly Hills, CA 90210",
    "include": "coordinates,census,cd,state_leg,school_district,acs_tract_demographic,acs_tract_housing_2023"
  },
  "results": [
    {
      "input": "9641 Sunset Blvd, Beverly Hills, CA 90210",
      "success": true,
      "data": {
        "confidence": "medium",
        "formatted_address": "9641 SUNSET BLVD, BEVERLY HILLS, CA 90210-2938",
        "address_line1": "9641 SUNSET BLVD",
        "address_line2": "BEVERLY HILLS, CA 90210-2938",
        "address_components": {
          "number": "9641",
          "street": "SUNSET",
          "suffix": "BLVD",
          "city": "BEVERLY HILLS",
          "state": "CA",
          "zip": "90210",
          "plus4": "2938",
          "country": "US"
        },
        "location": {
          "latitude": 34.081348,
          "longitude": -118.412499
        },
        "acs": {
          "tract": {
            "current": {
              "source": "U.S. Census Bureau",
              "product": "ACS 5-Year Estimates Data Profiles",
              "year": 2024,
              "vintage": "2020-2024",
              "geo_id": "1400000US06037700700",
              "geography_level": "tract",
              "tables": {
                "demographic": "DP05"
              },
              "demographic": {
                "sex_and_age": {
                  "total_population": {
                    "column": "DP05_0001",
                    "est": 2935,
                    "moe": 578,
                    "pct": 2935,
                    "pct_moe": null
                  },
                  "median_age": {
                    "column": "DP05_0018",
                    "est": 56.4,
                    "moe": 8.7
                  }
                }
              }
            },
            "2023": {
              "source": "U.S. Census Bureau",
              "product": "ACS 5-Year Estimates Data Profiles",
              "year": 2023,
              "vintage": "2019-2023",
              "geo_id": "1400000US06037700700",
              "geography_level": "tract",
              "tables": {
                "housing": "DP04"
              },
              "housing": {
                "occupancy": {
                  "total": {
                    "column": "DP04_0001",
                    "est": 1303,
                    "moe": 144,
                    "pct": 1303,
                    "pct_moe": null
                  },
                  "occupied": {
                    "column": "DP04_0002",
                    "est": 1170,
                    "moe": 149,
                    "pct": 89.8,
                    "pct_moe": 7.9
                  }
                }
              }
            }
          }
        },
        "census": {
          "current": {
            "source": "U.S. Census Bureau",
            "product": "TIGER/Line Shapefiles",
            "attribution": "Source: U.S. Census Bureau, TIGER/Line Shapefiles 2025",
            "year": 2025,
            "vintage": "2025",
            "county": [
              {
                "fips": "06037",
                "name": "Los Angeles",
                "is_usps_primary": true
              }
            ],
            "county_subdivision": [
              {
                "geoid": "0603791750",
                "name": "Los Angeles"
              }
            ],
            "place": [
              {
                "geoid": "0606308",
                "name": "Beverly Hills"
              }
            ],
            "tract": [
              {
                "geoid": "06037700700",
                "name": "7007"
              }
            ],
            "block_group": [
              {
                "geoid": "060377007002",
                "name": "2"
              }
            ],
            "metro_micro_cbsa_statistical_area": [
              {
                "cbsa_code": "31080",
                "name": "Los Angeles-Long Beach-Anaheim, CA"
              }
            ],
            "metropolitan_cbsa_division": [
              {
                "metdiv_code": "31084",
                "name": "Los Angeles-Long Beach-Glendale, CA",
                "cbsa_code": "31080"
              }
            ]
          }
        },
        "congressional_district": {
          "current": {
            "source": "U.S. Census Bureau",
            "product": "TIGER/Line Shapefiles",
            "attribution": "Source: U.S. Census Bureau, TIGER/Line Shapefiles 2025",
            "year": 2025,
            "vintage": "2025",
            "congress": 119,
            "districts": [
              {
                "geoid": "0636",
                "state": "CA",
                "district_number": 36,
                "name": "Congressional District 36",
                "land_area_sq_mi": 97.11,
                "water_area_sq_mi": 97.51
              }
            ],
            "primary": {
              "geoid": "0636",
              "state": "CA",
              "district_number": 36
            }
          }
        },
        "state_legislative": {
          "current": {
            "source": "U.S. Census Bureau",
            "product": "TIGER/Line Shapefiles",
            "attribution": "Source: U.S. Census Bureau, TIGER/Line Shapefiles 2025",
            "year": 2025,
            "vintage": "2025",
            "legislative_year": 2024,
            "upper": [
              {
                "geoid": "06024",
                "name": "24"
              }
            ],
            "lower": [
              {
                "geoid": "06051",
                "name": "51"
              }
            ]
          }
        },
        "school_district": {
          "current": {
            "source": "National Center for Education Statistics (NCES)",
            "product": "EDGE School District Boundaries",
            "attribution": "Source: U.S. Department of Education, National Center for Education Statistics (NCES), EDGE School District Boundaries, SY 2024-25 (TIGER 2025)",
            "school_year": "2024-2025",
            "districts": [
              {
                "leaid": "0604830",
                "name": "Beverly Hills Unified School District",
                "type": "unified",
                "grade_low": "KG",
                "grade_high": "12"
              }
            ]
          }
        }
      },
      "notices": [
        {
          "code": "PARTIAL_MATCH",
          "message": "Address matched; some details could not be confirmed.",
          "class": "match"
        }
      ]
    }
  ],
  "meta": {
    "request_id": "01M4CRKA0K3PCJ56G6GMA3BN34",
    "version": "2.0.0",
    "timing_ms": 24,
    "credits": {
      "used": 8,
      "source": "subscription",
      "remaining": 856921,
      "breakdown": {
        "base": 1,
        "enrichments": {
          "coordinates": 1,
          "acs_tract_demographic": 1,
          "acs_tract_housing_2023": 1,
          "census": 1,
          "cd": 1,
          "state_leg": 1,
          "school_district": 1
        }
      }
    }
  }
}

Result Fields

FieldTypeDescription
inputstringThe raw address string you submitted
successbooleantrue if the address was matched
dataobject (optional)Matched address, including requested enrichments when available

Matched Address Data

FieldTypeDescription
formatted_addressstringUSPS-standardized full address with ZIP+4
address_line1stringStreet line (number, directional, street, suffix, secondary)
address_line2stringLast line (city, state, ZIP+4)
address_componentsobjectParsed address components (see table below)
confidencestringMatch confidence: high, medium, or low
detailsobjectUSPS delivery-point metadata
pbsa_indicatorboolean (optional)Included only when true
locationobject (optional)Requested latitude/longitude
censusobject (optional)Requested census geography under current and each requested year
congressional_districtobject (optional)Requested congressional districts under current and each requested year
state_legislativeobject (optional)Requested upper/lower state legislative districts under current and each requested year
school_districtobject (optional)Requested school districts under current, with source and school_year
acsobjectRequested ACS by geography level (tract), then by year (current, 2023, 2022); omitted when no requested profile is delivered

Address Components

FieldTypeDescription
numberstring (optional)House/building number
pre_directionalstring (optional)Prefix directional (e.g., N, SW)
streetstring (optional)Street name
street_pre_typestring (optional)Street pre-type (e.g., AVE in AVE OF THE AMERICAS)
suffixstring (optional)Street suffix (e.g., ST, AVE, BLVD)
post_directionalstring (optional)Postfix directional (e.g., NW)
secondary_typestring (optional)Secondary address type (e.g., APT, STE, UNIT)
secondary_numberstring (optional)Secondary address number (e.g., 4B, 200)
citystringMatched city name
statestringTwo-letter state abbreviation
zipstring5-digit ZIP code
plus4string4-digit ZIP+4 suffix
countrystringCountry code (US)

Details (USPS Metadata)

FieldTypeDescription
record_typeobject (optional)USPS record type with code and description: S (Street), H (Highrise), P (PO Box), F (Firm), G (General Delivery), R (Rural Route)
carrier_routeobject (optional)USPS carrier route with code and description
countyobject (optional)County fips and name that USPS assigns to the ZIP+4. This is a postal assignment: the address can physically lie in a different county. For the county the address’s point actually falls in, request census (latest boundaries) or census_YYYY (that year’s boundaries) and read census.{current|YYYY}.county. A year flag shows the county as it existed then: in Connecticut, census_2021 and earlier return the former counties, and 2022 onward the planning regions that replaced them.
congressional_districtstring (optional)Congressional district number
building_namestring (optional)Building or firm name (when available)
government_buildingobject (optional)USPS government building indicator as code and description: A (City Government Building), B (Federal Government Building), C (State Government Building), D (Firm Only), E (City Government Building and Firm Only), F (Federal Government Building and Firm Only), G (State Government Building and Firm Only), or an empty code with "Not marked as a government building": the USPS record carries no indicator, which does not mean the building is not a government building. Omitted when no ZIP+4 record was matched
No match? When no match is found, success is false and data is omitted. The base credit still applies; coordinates, boundary flags and tract profiles are not charged. The per-item error identifies insufficient input, ambiguity, or no match.

Address item notices include class and codes MATCHED, PARTIAL_MATCH, ZIP_CORRECTED, CITY_CORRECTED, STATE_CORRECTED, HOUSE_NUMBER_CORRECTED, STREET_CORRECTED, NO_HOUSE_NUMBER, SECONDARY_NEEDED, SECONDARY_NOT_VERIFIED, UNIT_READ_AS_PMB, INPUT_NOT_USED, ZIP4_WITHHELD, and MILITARY_ADDRESS. HOUSE_NUMBER_CORRECTED means the house number was read differently from how it was written (37061/ read as 3706 1/2); its message names both, and the match is then PARTIAL_MATCH. UNIT_READ_AS_PMB (class correction): at a commercial mail receiving agency (a mail drop such as a UPS Store) a unit that does not confirm is the renter's private mailbox, printed as PMB n; the message names the unit as written and the box. MILITARY_ADDRESS (class info, last): an APO/FPO/DPO address, where address_components.street holds the unit (UNIT 2050) and number the box number (4190), USPS's values as they are; the message names both, or says no box number was supplied. Test keys accept only configured demo addresses.

No Match, Ambiguous, or Insufficient Input

HTTP 200, top-level success:false, no top-level error. results[0] contains input, success:false, and error:{code,message,url}. No candidate list is returned.

  • INSUFFICIENT_INPUT: Not enough information to locate the address. Include a city and state, or a ZIP code.
  • AMBIGUOUS: More than one address matches. Add detail such as a unit number, directional or ZIP code.
  • NOT_FOUND: The address could not be matched.

No match costs 1 credit. Requested coordinates and boundary/tract flags produce one ENRICHMENT_UNAVAILABLE notice and are not charged. On a matched address, unavailable enrichment notices explain that the matched location or requested enrichment data is unavailable.

{
  "success": false,
  "request": {
    "address": "123 Nowhere Lane Faketown ZZ",
    "include": "coordinates,census"
  },
  "results": [
    {
      "input": "123 Nowhere Lane Faketown ZZ",
      "success": false,
      "error": {
        "code": "INSUFFICIENT_INPUT",
        "message": "Not enough information to locate the address. Include a city and state, or a ZIP code.",
        "url": "https://www.zip-codes.com/api/"
      }
    }
  ],
  "meta": {
    "request_id": "01M3SX2VHP0M9TXH8WFHW7ZM3G",
    "version": "2.0.0",
    "timing_ms": 5,
    "credits": {
      "used": 2,
      "source": "subscription",
      "remaining": 1485721,
      "breakdown": {
        "base": 1,
        "enrichments": {
          "coordinates": 1
        }
      }
    },
    "notices": [
      {
        "code": "ENRICHMENT_UNAVAILABLE",
        "message": "Enrichment(s) unavailable: census; address was not matched; not charged."
      }
    ]
  }
}

Geographic Suggest

Typeahead search for geographic entities. Returns ZIP codes, cities/places, counties, metro areas (CBSAs), states/provinces, and Canadian FSAs/postal codes, school districts and USPS city names matching a query. Results include authoritative identifiers (FIPS, GEOID), coordinates, bounding boxes, and population data.

GET /v2/suggest
POST /v2/suggest

Parameters

ParameterTypeRequiredDescription
qstringRequiredSearch query (1–200 characters). Case-insensitive.
limitintegerNoMax results to return. Default: 10. Free/test tiers: max 15. Paid tiers: max 500.
includestringNoComma-separated entity types to include: zip, place, county, cbsa, fsa, state, school_district, ca_postal, uspscity. Default: all nine types. Unlike /zip and /radius where include adds enrichment data, here it filters which entity types are returned.
statestringNoUS state or CA province abbreviation to filter results (e.g., CA, ON).
countrystringNoFilter by country: US or CA. Default: both.
proximitystringNoGeographic bias for ranking. Accepts multiple formats: lat,lon (coordinates), auto (caller IP), IP address, 5-digit ZIP, 3-char FSA, or 2-letter state/province. Nearby results rank higher (soft bias, not a filter).
Proximity bias uses source-dependent weighting. Direct coordinates get 55% proximity weight; ZIP/FSA centroids get 50%; state centroids get 30% (gentle nudge). IP-based proximity (auto or explicit IP) resolves coordinates for the response echo but does not currently influence scoring. Numeric queries (ZIP prefixes) now benefit from proximity bias.

Entity Types

TypeCountDescriptionType-Specific Fields
zip~42KUS ZIP codescity, classification
place~30KUS Census places (cities, towns, CDPs)fips, geoid, associated_zips
county~3.2KUS counties (includes parishes, boroughs)fips, geoid
cbsa~1KMetropolitan & micropolitan statistical areascbsa_code, geoid, metro_micro
fsa~1.6KCanadian Forward Sortation Areascity, province, postal_code_count
state~67US states/territories & CA provinces (with abbreviation aliases)(none)
school_district—School districtsAvailable entity metadata
ca_postal—Canadian postal codesAvailable postal metadata
uspscity—USPS city namesAvailable city metadata

Credit Cost

Limit RangeCreditsAvailable To
1–151All tiers (including free)
16–502Paid only
51–1503Paid only
151–5005Paid only

Billing based on requested limit, not results returned: a search is charged even when it matches nothing (the search ran; the answer is no match). Free/test tier requests above 15 are clamped with a FREE_TIER_LIMIT_CAPPED notice. Validation errors (missing q, invalid include) cost 0 credits.

Calculate your cost

Example Request

cURL
curl -H "X-Api-Key: YOUR_API_KEY" \
  "https://api.zip-codes.com/v2/suggest?q=spring&limit=3"

Example: With proximity bias

cURL
curl -H "X-Api-Key: YOUR_API_KEY" \
  "https://api.zip-codes.com/v2/suggest?q=spring&proximity=40.7128,-74.006&limit=3"

Example: Filtered by type and state

cURL
curl -H "X-Api-Key: YOUR_API_KEY" \
  "https://api.zip-codes.com/v2/suggest?q=cook&include=county&state=IL&limit=3"

Example Response

Response
{
  "success": true,
  "request": {
    "q": "cook",
    "limit": 3,
    "include": [
      "county"
    ],
    "state": "IL"
  },
  "results": [
    {
      "success": true,
      "total_matched": 5,
      "matches": [
        {
          "type": "county",
          "name": "Cook County",
          "state": "IL",
          "country": "US",
          "fips": "17031",
          "geoid": "17031",
          "population": 5182090,
          "location": {
            "lat": 41.8440477,
            "lon": -87.820797
          },
          "bbox": {
            "min_lat": 41.46971,
            "max_lat": 42.154265,
            "min_lon": -88.263641,
            "max_lon": -87.524165
          }
        },
        {
          "type": "county",
          "name": "Rock Island County",
          "state": "IL",
          "country": "US",
          "fips": "17161",
          "geoid": "17161",
          "population": 142757,
          "location": {
            "lat": 41.5511206,
            "lon": -90.4512621
          },
          "bbox": {
            "min_lat": 41.326838,
            "max_lat": 41.7829645,
            "min_lon": -91.0719555,
            "max_lon": -90.159878
          }
        },
        {
          "type": "county",
          "name": "Boone County",
          "state": "IL",
          "country": "US",
          "fips": "17007",
          "geoid": "17007",
          "population": 53230,
          "location": {
            "lat": 42.3297656,
            "lon": -88.849764
          },
          "bbox": {
            "min_lat": 42.15232,
            "max_lat": 42.495046,
            "min_lon": -88.940992,
            "max_lon": -88.70541
          }
        }
      ]
    }
  ],
  "meta": {
    "version": "2.0.0",
    "timing_ms": 2,
    "credits": {
      "used": 1,
      "remaining": 74999,
      "breakdown": {
        "base": 1,
        "enrichments": {}
      },
      "source": "subscription"
    },
    "request_id": "01M3SX2V9NY9MRRAMWVJG0C09E"
  }
}

Match Object Fields

FieldTypeDescription
typestringEntity type: zip, place, county, cbsa, fsa, state, school_district, ca_postal, uspscity
namestringDisplay name (ZIP code number, place name, county name, etc.)
statestringUS state abbreviation (or province for FSA)
countrystringUS or CA
populationintegerTotal population (ACS 5-year estimates for the US, the Census of Population for Canada). A ZIP Code: its ZCTA’s total, else our delivery-point estimate; a USPS city: the sum of its ZIP Codes; a place, county, CBSA or state: its own ACS total. Omitted when there is none (PO Box, unique and military ZIP Codes, postal codes, school districts)
locationobject{ lat, lon } — a ZIP or postal code’s point; Census’s internal point for places, counties and CBSAs; the population-weighted centre of their ZIP Codes for USPS cities and states
bboxobject{ min_lat, max_lat, min_lon, max_lon } — bounding box of the boundary (USPS cities and states: of their ZIP Code boundaries); omitted when there is none (postal codes, ZIP Codes without a boundary)
fipsstringPlaces and counties: the Census GEOID (same as geoid); school districts: the NCES LEAID
geoidstringCensus GEOID (places, counties, CBSAs); school districts: the NCES LEAID
associated_zipsarrayUSPS cities: up to 50 of their ZIP Codes (standard first); places: up to 20 ZIP Codes whose USPS city name is the place’s name in the same state; school districts: up to 20 ZIP Codes sharing area with the district
cbsa_codestringCBSA code (CBSAs only)
metro_microstringMetropolitan or Micropolitan (CBSAs only)
citystringPrimary city name (ZIPs and FSAs only)
classificationstringZIP type: Standard, PO Box, Military, Unique (ZIPs only)
provincestringCA province abbreviation (FSAs and CA states only)

Quick ZIP Lookup

Fast postal code lookup returning basic location data with optional timezone enrichment. Supports US ZIP codes (5-digit), US ZIP+4 codes, Canadian FSAs (3-character), full Canadian postal codes (6-character), and latitude/longitude coordinates for reverse geocoding.

GET /v2/quick-zip
POST /v2/quick-zip

Parameters

ParameterTypeRequiredDescription
codestringRequiredZIP code, ZIP+4, FSA, Canadian postal code, or coordinates (lat,lon)
includestringOptionalComma-separated enrichment flags. Only timezone is supported on this endpoint.

Supported Code Types

TypeFormatExampleCountryBase Cost
ZIP5 digits90210US1 credit
ZIP+49 digits (with or without dash)90210-1234US2 credits
FSA3 alphanumeric (A1A)M5VCA1 credit
Postal6 alphanumeric (A1A1A1)M5V2H1CA1 credit
Coordinateslat,lon (decimal degrees)34.1030,-118.4105US + CA2 or 4 credits (before timezone)
Coordinate credits perform a reverse geocode to find the nearest postal code. The extra credit cost reflects the spatial search required. Pass coordinates directly in the code parameter as lat,lon.

Available Enrichments

FlagAdds to ResponseCostAvailability
timezoneOne flat object: source, product, attribution (the same values /v2/zip and /v2/address return), then iana_name, name, abbreviation, abbreviation_dst, utc_offset, utc_offset_dst, observes_dst. A ZIP on a zone line gives the zone covering most of it+1 data layer (× base)US + CA

Timezone (October 2026, nothing removed): timezone stays one flat object with the fields it always had, and gains product and attribution; source now names the publisher of the time zone boundaries ("timezone-boundary-builder"), the same three values /v2/zip and /v2/address return. Field order: source, product, attribution, iana_name, name, abbreviation, abbreviation_dst, utc_offset, utc_offset_dst, observes_dst. A US ZIP (and a ZIP+4, by its parent ZIP) is answered from the ZIP's boundary: a ZIP crossing a zone line gives the zone covering the largest share of it (use /v2/zip for every zone with its share). Coordinates and ZIPs with only a centroid give the zone containing the point. Canadian codes keep their zone with the source /v2/zip gives Canadian codes; product and attribution are null. Show attribution wherever you display the data. Credits are unchanged.

Credit Cost

Quick ZIP credits = base × (1 + timezone + reverse_geocode). Base is 2 for ZIP+4 and 1 for ZIP, FSA, or Postal. Coordinates use the resolved type and add a reverse-geocode layer. ZIP+4 with timezone costs 4; coordinates resolving to ZIP+4 cost 4, or 6 with timezone. Unknown flags have no effect.

Calculate your cost

Response Fields

FieldTypeDescription
countrystring"US" or "CA"
codestringThe postal code
code_typestringZIP, FSA, or Postal
parent_codestring (optional)Parent 5-digit ZIP or FSA (omitted for base codes)
citystringPrimary city name
state / state_namestringState/province abbreviation and full name
state_fipsstring (optional)2-digit state FIPS code (US only)
county / county_fipsstring (optional)County name and FIPS code (US only)
multi_countyboolean (optional)Whether ZIP spans multiple counties (US only)
classificationstring (optional)ZIP type code (US only)
classification_namestring (optional)ZIP type name: Standard, PO Box, Military, Unique. USPS’s label for how mail is delivered: it does not mean the ZIP Code has residents, deliveries or an area of its own.
municipalitystring (optional)Municipality name (where applicable)
locationobject{"lat": ..., "lon": ...}
detailobject (optional)Street-level detail for ZIP+4 / CA postal codes
timezoneobject (optional)Present when include=timezone: {source, product, attribution, iana_name, name, abbreviation, abbreviation_dst, utc_offset, utc_offset_dst, observes_dst}; for a ZIP crossing a zone line, the zone covering the largest share
Canadian postal fallback: If a full Canadian postal code is not found, the API automatically falls back to FSA-level data and includes a FALLBACK_USED notice. No additional credit is charged.

ZIP+4 Detail Fields

When a ZIP+4 code is looked up, the detail object is populated with delivery-point data:

FieldTypeDescription
zip4stringThe 4-digit ZIP+4 suffix
street[].record_typestringUSPS ZIP+4 record type name: Street, Highrise, Firm, PO Box, Rural Route or General Delivery
street[].carrier_routestringUSPS carrier route code

Street Detail Fields

Street-level address range information within the detail.street object:

FieldTypeDescription
addr_lowstringLow end of primary address range
addr_highstringHigh end of primary address range
prefixstringStreet name prefix directional (e.g., N, SW)
namestringStreet name
suffixstringStreet suffix (e.g., St, Ave, Blvd)
postfixstringStreet name postfix directional
odd_evenstringAddress range parity: O (odd), E (even), B (both)
sec_addr_abbrstringSecondary address abbreviation (e.g., APT, STE, UNIT)
sec_addr_lowstringLow end of secondary address range
sec_addr_highstringHigh end of secondary address range
sec_addr_odd_evenstringSecondary address range parity: O, E, or B
building_namestringBuilding or firm name
addressstringFormatted full address line

Example Request

cURL
curl -H "X-Api-Key: YOUR_API_KEY" \
  "https://api.zip-codes.com/v2/quick-zip?code=90210&include=timezone"
Open in Playground

Example Response

Response — US ZIP
{
  "success": true,
  "request": {
    "code": "90210",
    "include": [
      "timezone"
    ]
  },
  "results": [
    {
      "code": "90210",
      "code_type": "ZIP",
      "country": "US",
      "city": "Beverly Hills",
      "state": "CA",
      "state_name": "California",
      "state_fips": "06",
      "county": "Los Angeles",
      "county_fips": "06037",
      "classification": null,
      "classification_name": "Standard",
      "location": {
        "lat": 34.103131,
        "lon": -118.416253
      },
      "timezone": {
        "source": "timezone-boundary-builder",
        "product": "Time zone boundaries (with oceans)",
        "attribution": "Time zone boundaries © OpenStreetMap contributors, timezone-boundary-builder (ODbL); time zone rules: IANA tz database",
        "iana_name": "America/Los_Angeles",
        "name": "Pacific",
        "abbreviation": "PST",
        "abbreviation_dst": "PDT",
        "utc_offset": -8,
        "utc_offset_dst": -7,
        "observes_dst": true
      },
      "success": true
    }
  ],
  "meta": {
    "version": "2.0.0",
    "timing_ms": 8,
    "credits": {
      "used": 2,
      "remaining": 856919,
      "breakdown": {
        "base": 1,
        "enrichments": {
          "timezone": 1
        }
      },
      "source": "subscription"
    },
    "request_id": "01M4CRSJ0T2ZK0JDA9A0QFWJ8N"
  }
}
Response — Canadian FSA
{
  "success": true,
  "request": {
    "code": "M5V",
    "include": [
      "timezone"
    ]
  },
  "results": [
    {
      "code": "M5V",
      "code_type": "FSA",
      "country": "CA",
      "city": "Toronto",
      "state": "ON",
      "state_name": "Ontario",
      "location": {
        "lat": 43.638388,
        "lon": -79.40428
      },
      "timezone": {
        "source": "IANA Time Zone Database / timezone-boundary-builder",
        "product": null,
        "attribution": null,
        "iana_name": "America/Toronto",
        "name": "Eastern",
        "abbreviation": "EST",
        "abbreviation_dst": "EDT",
        "utc_offset": -5,
        "utc_offset_dst": -4,
        "observes_dst": true
      },
      "success": true
    }
  ],
  "meta": {
    "version": "2.0.0",
    "timing_ms": 15,
    "credits": {
      "used": 2,
      "remaining": 74998,
      "breakdown": {
        "base": 1,
        "enrichments": {
          "timezone": 1
        }
      },
      "source": "subscription"
    },
    "request_id": "01M3SX2V9NY9MRRAMWVJG0C09E"
  }
}
Response — Not Found
{
  "success": true,
  "request": {
    "code": "99999"
  },
  "results": [
    {
      "success": false,
      "input": {
        "code": "99999"
      },
      "error": {
        "code": "NOT_FOUND_ZIP",
        "message": "ZIP code 99999 not found.",
        "url": "https://www.zip-codes.com/api/"
      }
    }
  ],
  "meta": {
    "version": "2.0.0",
    "timing_ms": 8,
    "credits": {
      "used": 1,
      "remaining": 74999,
      "breakdown": {
        "base": 0,
        "enrichments": {},
        "note": "Not found (1 credit charged)."
      },
      "source": "subscription"
    },
    "request_id": "01M3SX2V9NY9MRRAMWVJG0C09E"
  }
}

ZIP Code Details

Unknown include flags are ignored free with a VALIDATION_UNKNOWN_ENRICHMENT notice on this single endpoint. /v2/zip/batch ignores unknown flags without that notice.

Comprehensive postal code lookup with rich data including demographics, business statistics, delivery points, and optional enrichments and historical ACS profiles. Returns detailed information for US ZIP codes and Canadian postal codes.

GET /v2/zip
POST /v2/zip

Parameters

ParameterTypeRequiredDescription
codestringRequiredUS ZIP code, Canadian postal code, or coordinates (lat,lon)
includestringOptionalComma-separated enrichment flags (see Data Enrichments)

Available Enrichments

FlagAdds to ResponseCostAvailability
timezoneIANA timezone name, UTC offsets, DST info. Multi-zone ZIPs return array with % coverage.+1US + CA
censusCounty, county subdivision, place, tract, block group, CBSA overlaps with % coverage (US Census Bureau). On a Canadian code, a legacy alias of statcan: answered under statcan with an ENRICHMENT_FLAG_RENAMED notice+1US only
cdCongressional districts with % coverage (US Census Bureau). On a Canadian code, a legacy alias of statcan (federal electoral districts are inside it), with an ENRICHMENT_FLAG_RENAMED notice+1US only
statcanStatistics Canada geography for a Canadian postal code or FSA: census divisions and subdivisions, tracts, dissemination areas and blocks (postal codes), metro areas, population centres, economic regions, consolidated subdivisions, designated places, province, and federal electoral districts, under statcan.current (latest census, 2021). See Census Boundaries+1CA only
statcan_{YYYY}An earlier Statistics Canada census under its "YYYY" key: statcan_2016, statcan_2011 (statcan_2021 is the same as statcan)+1 eachCA only
state_legState senate (upper) and house (lower) legislative district overlaps+1US only
census_{YYYY}, cd_{YYYY}, state_leg_{YYYY}An earlier year’s boundaries (2010–2025) under its "YYYY" key beside current (for example census_2022, cd_2012). The bare flags return the latest year (2025). See Census Boundaries+1 eachUS only
school_districtSchool districts (NCES) with office address, grade range, locale, area overlap+1US only
acs_demographicPopulation, sex, age distribution, race, ethnicity, citizenship (DP05 — 108 fields)+1US only
acs_socialHouseholds, education, language, ancestry, marital status, computers/internet (DP02 — 154 fields)+1US only
acs_economicEmployment, income, poverty, occupation, industry, health insurance, commute (DP03 — 137 fields)+1US only
acs_housingHousing units, occupancy, tenure, home value, rent, utilities, structure age (DP04 — 143 fields)+1US only
acs_{profile}_{YYYY}Historical ACS for any year from 2011–2024 (14 years). Append _YYYY to any ACS flag (e.g., acs_housing_2022). 528 fields in recent years, 394 in earliest. See ACS Data.+1 eachUS only
demographicsPopulation, race, age, households, income, and business establishment counts+1US only
medicareMedicare CBSA code/name/type, rating area ID, SSA state/county code+1US only
Canadian availability: Requesting US-only enrichments for Canadian codes omits unavailable model fields (explicit census/ACS nulls may remain) and adds an ENRICHMENT_UNAVAILABLE notice. No credit is charged. census, census_YYYY, cd and cd_YYYY on a Canadian code are answered under statcan with an ENRICHMENT_FLAG_RENAMED notice; statcan on a US code returns ENRICHMENT_UNAVAILABLE at no charge.

Credit Cost

ZIP Details credits = 1 + billable flags + reverse_geocode. Base is 1 for every code type, including ZIP+4 and Postal. Canadian unavailable flags cost 0 with a notice. Example: include=timezone,census,cd on a ZIP = 4 credits. On a Canadian code, include=statcan,statcan_2016,timezone = 4 credits; federal electoral districts are inside statcan at no extra charge, and census/cd there are one statcan charge.

Calculate your cost

Example Request

cURL
curl -H "X-Api-Key: YOUR_API_KEY" \
  "https://api.zip-codes.com/v2/zip?code=90210&include=timezone,demographics"
Open in Playground

Example Response

Response — Base + Timezone + Demographics
{
  "success": true,
  "request": {
    "code": "90210",
    "include": [
      "timezone",
      "demographics"
    ]
  },
  "results": [
    {
      "country": "US",
      "code": "90210",
      "code_type": "ZIP",
      "city": "Beverly Hills",
      "state": "CA",
      "state_name": "California",
      "state_fips": "06",
      "county": "Los Angeles",
      "county_fips": "06037",
      "multi_county": false,
      "location": {
        "lat": 34.102787,
        "lon": -118.416019
      },
      "area_codes": [
        "310",
        "323",
        "424",
        "738"
      ],
      "elevation": {
        "ft": 719,
        "m": 219
      },
      "metro": {
        "region": "West",
        "division": "Pacific",
        "csa": "348",
        "csa_name": "Los Angeles-Long Beach, CA",
        "msa": "4472",
        "msa_name": "Los Angeles-Riverside-Orange County, CA"
      },
      "zip_info": {
        "classification": null,
        "classification_name": "Standard",
        "facility_code": "P",
        "city_delivery_indicator": "Y",
        "intro_date": "<2004-10",
        "land_area_sq_mi": 10.748,
        "water_area_sq_mi": 0.054,
        "zip4_count": 3516,
        "delivery_points": {
          "total_active": 10643,
          "business": 1472,
          "residential": 9301,
          "po_box": 0,
          "single_family": 6786,
          "multi_family": 2419
        }
      },
      "demographics": {
        "population": {
          "total": 21134,
          "estimate": 22973,
          "male": 10099,
          "female": 11035
        },
        "race": {
          "white": 17130,
          "black": 366,
          "hispanic": 1296,
          "asian": 1406,
          "american_indian": 25,
          "hawaiian_pacific_islander": 12,
          "other": 481
        },
        "age": {
          "median": 49.3,
          "median_male": 49.3,
          "median_female": 49.2
        },
        "households": {
          "total": 8553,
          "persons_per_household": 2.47,
          "average_family_size": 3.13,
          "median_income": 187801,
          "average_home_value": 2000001
        },
        "business": {
          "establishments": 2536,
          "employees": 32353,
          "payroll_q1": 648013000,
          "payroll_annual": 2653591000
        }
      },
      "timezone": {
        "source": "timezone-boundary-builder",
        "product": "Time zone boundaries (with oceans)",
        "attribution": "Time zone boundaries © OpenStreetMap contributors, timezone-boundary-builder (ODbL); time zone rules: IANA tz database",
        "zones": [
          {
            "iana_name": "America/Los_Angeles",
            "name": "Pacific",
            "abbreviation": "PST",
            "abbreviation_dst": "PDT",
            "utc_offset": -8,
            "utc_offset_dst": -7,
            "observes_dst": true,
            "pct_of_zip": 100
          }
        ]
      },
      "success": true
    }
  ],
  "meta": {
    "version": "2.0.0",
    "timing_ms": 10,
    "credits": {
      "used": 3,
      "remaining": 856905,
      "breakdown": {
        "base": 1,
        "enrichments": {
          "timezone": 1,
          "demographics": 1
        }
      },
      "source": "subscription"
    },
    "request_id": "01M4CRSKYEX0XGT5H4VGGYQN6B"
  }
}

Base Fields (no include flag required)

FieldTypeDescription
countrystring"US" or "CA"
codestringThe postal code
code_typestringZIP, FSA, or Postal
parent_codestring (optional)Parent 5-digit ZIP (for ZIP+4 queries) or parent FSA (for CA postal queries). Omitted for base codes.
citystringPrimary city name
state / state_namestringState/province abbreviation and full name
state_fipsstring (optional)2-digit state FIPS code (US only)
county / county_fipsstringCounty name and FIPS code (US only)
multi_countyboolean (optional)Whether the ZIP spans multiple counties (US only)
municipalitystring (optional)Municipality name (where applicable)
locationobject{"lat": 34.10, "lon": -118.41}, the code’s centroid; lat and lon are null when it has no location (military ZIP Codes and a few PO Box and unique ZIP Codes)
area_codesstring[] (optional)Telephone area codes serving this postal code; null when none is known (military ZIP Codes)
elevationobject (optional){"ft": 719, "m": 219} — centroid elevation in feet and meters (USGS for a US ZIP Code); both null when the code has no location
metroobject (optional)MSA/CBSA statistical area info (US only, see sub-fields below)
zip_infoobject (optional)ZIP classification, facility code, land/water area, delivery points (US only). land_area_sq_mi and water_area_sq_mi are the Census ZIP Code Tabulation Area’s (Census gazetteer), the same basis as every Census block; both null when the ZIP Code has no ZCTA of its own (most PO Box, unique and military ZIP Codes), and water is 0 for a landlocked ZCTA
postal_infoobject (optional)Canadian postal metadata: record type, address type, FSA stats (CA only)
city_aliasesarray (optional)Alternative city names with mailing status and intro date
detailobject (optional)ZIP+4 / CA postal street-level overlay (omitted for 5-digit ZIP / FSA queries)

metro Sub-Fields (US only)

FieldTypeDescription
regionstring (optional)Census region (e.g., West, Northeast)
divisionstring (optional)Census division (e.g., Pacific, Middle Atlantic)
csa / csa_namestring (optional)Combined Statistical Area code and name
msa / msa_namestring (optional)Legacy Metropolitan Statistical Area code and name, from OMB’s 1999 definitions. OMB replaced MSAs with CBSAs in 2003, so these are not current metro areas: for the current CBSAs use the census flag’s metro_micro_cbsa_statistical_area.
pmsa / pmsa_namestring (optional)Legacy Primary MSA code and name (where applicable), from the same 1999 definitions

postal_info Sub-Fields (CA only)

FieldTypeDescription
record_typestringRecord type code
address_typestringAddress type classification
fsastringParent FSA (3-character)
delivery_postal_codestringDelivery postal code
fsa_populationnumberFSA population
fsa_dwellingsnumberTotal dwellings in FSA
fsa_dwellings_occupiednumberOccupied dwellings in FSA
fsa_postal_code_countnumberNumber of postal codes in the FSA

Radius Search

Find all postal codes within a given radius of a center point. Specify the center via a single code parameter (ZIP, FSA, Postal Code, or lat,lon coordinates). Cross-border searches include Canadian postal codes in centroid mode and FSAs in spatial mode. Supports donut searches (min radius) and result limiting.

GET /v2/radius
POST /v2/radius

Parameters

ParameterTypeRequiredDescription
codestringRequiredCenter point — US ZIP (5-digit), ZIP+4, Canadian FSA, Canadian Postal Code, or lat,lon coordinates (e.g. 41.878,-87.629)
maxnumber or stringRequiredMaximum radius in miles (e.g. 25), or "auto" (density-based) / "auto-commute" (commute-behavior-based). Numeric max must be greater than zero: centroid up to 500, spatial up to 250. Free tier: 100 centroid / 50 spatial; test keys: 10 miles. See Auto Radius.
minnumberOptionalMinimum radius in miles for donut search: nonnegative and less than max. Default: 0
includestringOptionalComma-separated enrichment flags (see table below)
modestringOptionalcentroid (default) — fast haversine on centroids, returns US ZIP + CA Postal. spatial — precise boundary analysis with pct_inside, returns US ZIP + CA FSA.
limitnumberOptionalPositive integer maximum results; when it cuts the list, summary.total_count is the number returned and summary.total_available the number in the radius. ACS statistics cover the whole radius either way. Centroid mode caps at 10,000 with RESULTS_CAPPED.

Available Enrichments

FlagAdds to ResponseCostAvailability
timezoneIANA timezone name, UTC offsets, DST abbreviations for each result+1 data layer (× base)US + CA
acs_demographicAggregate: population, sex, age, race, ethnicity (DP05)+1 data layer (× base)US only
acs_socialAggregate: households, education, language, ancestry (DP02)+1 data layer (× base)US only
acs_economicAggregate: employment, income, poverty, commute (DP03)+1 data layer (× base)US only
acs_housingAggregate: occupancy, tenure, home value, rent (DP04)+1 data layer (× base)US only
acs_{profile}_{YYYY}Historical ACS aggregate for any year from 2011–2024. Append _YYYY to any ACS flag (e.g., acs_economic_2019). See ACS Data for year-by-year coverage.+1 layer each (× base)US only
Territory enrichments not available on /radius. Boundary enrichments (census, cd, state_leg, school_district) are available on /v2/zip and /v2/address. If requested on /v2/radius, these flags are silently ignored — no credit is charged and an ENRICHMENT_UNAVAILABLE notice is included in the response. Use /v2/zip for territory enrichments on individual codes.
How Radius Credits Are Calculated

Every radius search starts with a base cost determined by how far you're searching — bigger search = more work for the server.

Each enrichment you add (timezone, demographics, etc.) multiplies that base cost, because the API has to gather that data for every result in your radius.

Think of it as: “search cost × number of data layers”

Quick worked example

Searching 25 miles with timezone: base cost is 1 (short search) × 2 data layers (the search itself + timezone) = 2 credits.

Credit Cost

Centroid mode (default, faster): Wider bands — the first 100 miles costs 1 base credit, the next 100 costs 2, and so on up to 500 miles.

Spatial mode (precise boundaries): Narrower bands — the first 50 miles costs 1 base credit, the next 50 costs 2, and so on up to 250 miles.

Cost = base × (1 + distinct timezone/ACS flags + auto_radius); auto_radius is 1 for either auto option, otherwise 0.

Calculate your cost

Auto Radius

Instead of specifying a numeric radius, pass max=auto or max=auto-commute to let the API calculate an appropriate search radius for the center point. This accepts a ZIP, ZIP+4, FSA, Postal code, or lat/lon coordinate center.

ModeBased OnRangeBest For
autoPopulation density: 341 ÷ density0.423–150 milesGeneral-purpose searches scaled to urbanity
auto-commuteACS commuting patterns: mean travel time × transport-weighted speed × 1.53–100 milesService-area and labor-market analysis
Fallback: If commute data is unavailable for a ZIP (no ACS coverage), auto-commute automatically falls back to density-based auto and includes an AUTO_COMMUTE_FALLBACK notice in the response. The resolved radius appears in results[].search.max and the resolution method in results[].search.auto_mode ("density" or "commute").

Credit cost: Using auto-radius counts as one additional data layer in the cost calculation. Example: max=auto with include=timezone = base × 3 data layers = 3 credits for a short-range search.

Example Request

cURL
curl -H "X-Api-Key: YOUR_API_KEY" \
  "https://api.zip-codes.com/v2/radius?code=48226&max=25&include=timezone"
Open in Playground

Example Response

Response — 25mi Radius
{
  "success": true,
  "request": {
    "code": "48226",
    "max": "25",
    "include": [
      "timezone"
    ],
    "mode": "centroid"
  },
  "results": [
    {
      "search": {
        "center": {
          "code": "48226",
          "code_type": "ZIP",
          "city": "Detroit",
          "state": "MI",
          "location": {
            "lat": 42.331702,
            "lon": -83.04962
          }
        },
        "max": 25,
        "min": 0,
        "mode": "centroid"
      },
      "matches": [
        {
          "country": "US",
          "code": "48226",
          "code_type": "ZIP",
          "city": "Detroit",
          "state": "MI",
          "county": "Wayne",
          "county_fips": "26163",
          "classification": null,
          "classification_name": "Standard",
          "location": {
            "lat": 42.331654,
            "lon": -83.049581
          },
          "distance_miles": 0,
          "area_sq_mi": null,
          "timezone": {
            "source": "IANA Time Zone Database / timezone-boundary-builder",
            "iana_name": "America/Detroit",
            "name": "Eastern",
            "abbreviation": "EST",
            "abbreviation_dst": "EDT",
            "utc_offset": -5,
            "utc_offset_dst": -4,
            "observes_dst": true
          }
        },
        {
          "country": "US",
          "code": "48243",
          "code_type": "ZIP",
          "city": "Detroit",
          "state": "MI",
          "county": "Wayne",
          "county_fips": "26163",
          "classification": null,
          "classification_name": "Standard",
          "location": {
            "lat": 42.329064,
            "lon": -83.039647
          },
          "distance_miles": 0.54,
          "timezone": {
            "source": "IANA Time Zone Database / timezone-boundary-builder",
            "iana_name": "America/Detroit",
            "name": "Eastern",
            "abbreviation": "EST",
            "abbreviation_dst": "EDT",
            "utc_offset": -5,
            "utc_offset_dst": -4,
            "observes_dst": true
          }
        }
      ],
      "success": true
    }
  ],
  "meta": {
    "version": "2.0.0",
    "timing_ms": 31,
    "credits": {
      "used": 2,
      "remaining": 856903,
      "breakdown": {
        "base": 1,
        "enrichments": {
          "timezone": 1
        },
        "calculation": "1 x (1 + 1) = 2"
      },
      "source": "subscription"
    },
    "request_id": "01M4CRSM8HPQJ7ARP5Z92CMH4S"
  }
}

The radius summary contains total_count, us_zip_count, ca_fsa_count and ca_postal_count, which count the matches returned, and total_available, the number of matches in the radius, present when limit cut the list. ACS statistics (stats.acs) always cover every match in the radius, whatever the limit. They carry est and pct only, with no moe or column: Census publishes no margin of error for a combined area. For one ZIP Code’s estimate with its margin of error, use /v2/zip. A coverage_pct under 100 is normal (often 40–60 in cities): PO Box, unique and other point ZIP Codes have no area of their own, so Census publishes no ACS data for them (zips_without_acs_data), and their residents are counted in the surrounding ZIP Codes, so the totals are not short. Examples are trimmed.

Search Fields

FieldTypeDescription
search.center.codestringResolved center postal code
search.center.code_typestringZIP, FSA, or Postal
search.center.citystringCenter city name
search.center.statestringCenter state/province abbreviation
search.center.locationobject{"lat": ..., "lon": ...} — center coordinates
search.maxnumberResolved maximum radius in miles (numeric value, even when auto was requested)
search.minnumberMinimum radius in miles (donut inner ring)
search.modestring"centroid" or "spatial"
search.auto_modestring (optional)"density" when max=auto, "commute" when max=auto-commute, omitted for numeric radius

Match Fields

FieldTypeDescription
countrystring"US" or "CA"
codestringPostal code
code_typestringZIP, FSA, or Postal
citystringCity name
statestringState/province abbreviation
countystring (optional)County name (US only)
county_fipsstring (optional)County FIPS code (US only)
classificationstring (optional)USPS ZIP Code type: P (PO Box) or U (unique); null for a standard ZIP Code and for Canada
classification_namestring (optional)ZIP type name: Standard, PO Box, Unique. USPS’s label for how mail is delivered: it does not mean the ZIP Code has residents, deliveries or an area of its own.
locationobject{"lat": ..., "lon": ...}
distance_milesnumberDistance from center point in miles
pct_insidenumber (optional)Spatial mode: the percentage of the postal code’s area inside the search radius, to 2 decimals. A code whose overlap rounds to 0.00 is not a match, so every spatial match is at least 0.01. null for a code with no area of its own (area_sq_mi 0) and for every match in centroid mode.
area_sq_minumber (optional)Spatial mode: total area of the postal code in square miles, to 2 decimals; 0 for a code with no area of its own (a point). null in centroid mode.

Distance Calculation

Calculate the great-circle distance between two points. Supports any combination of ZIP codes, ZIP+4 codes, FSAs, full Canadian postal codes, and lat/lon coordinates. Timezone data is always included at no extra cost. Each endpoint in the response includes an input_type field ("code" or "coordinates") indicating how the point was resolved.

Timezone (October 2026, nothing removed): each point's timezone stays one flat object with the fields it always had, and gains product and attribution; source now names the publisher ("timezone-boundary-builder"), the same three values /v2/zip returns. Field order: source, product, attribution, iana_name, name, abbreviation, abbreviation_dst, utc_offset, utc_offset_dst, observes_dst. It is the zone containing the point, from the time zone boundary file (timezone-boundary-builder, newest release). Canadian points keep their zone with /v2/zip's Canadian source; product and attribution are null.

GET /v2/distance
POST /v2/distance

Parameters

ParameterTypeRequiredDescription
fromstringRequiredOrigin point. Accepts: US ZIP (90210), ZIP+4 (90210-1234), Canadian FSA (M5V), Canadian postal code (M5V2H1), or lat,lon coordinates (34.103,-118.410)
tostringRequiredDestination point. Accepts: US ZIP (90210), ZIP+4 (90210-1234), Canadian FSA (M5V), Canadian postal code (M5V2H1), or lat,lon coordinates (40.758,-73.986)
You can mix any input types. For example: from=90210-1234&to=40.758,-73.9855

Credit Cost

Fixed 1 credit per pair. Timezone is always included at no extra cost.

Calculate your cost

Example Request

cURL
curl -H "X-Api-Key: YOUR_API_KEY" \
  "https://api.zip-codes.com/v2/distance?from=90210&to=10001"
Open in Playground

Example Response

Response
{
  "success": true,
  "request": {
    "from": "90210",
    "to": "10001"
  },
  "results": [
    {
      "from": {
        "country": "US",
        "code": "90210",
        "code_type": "ZIP",
        "city": "Beverly Hills",
        "state": "CA",
        "county": "Los Angeles",
        "location": {
          "lat": 34.102776,
          "lon": -118.416124
        },
        "timezone": {
          "source": "timezone-boundary-builder",
          "product": "Time zone boundaries (with oceans)",
          "attribution": "Time zone boundaries © OpenStreetMap contributors, timezone-boundary-builder (ODbL); time zone rules: IANA tz database",
          "iana_name": "America/Los_Angeles",
          "name": "Pacific",
          "abbreviation": "PST",
          "abbreviation_dst": "PDT",
          "utc_offset": -8,
          "utc_offset_dst": -7,
          "observes_dst": true
        },
        "input_type": "code"
      },
      "to": {
        "country": "US",
        "code": "10001",
        "code_type": "ZIP",
        "city": "New York",
        "state": "NY",
        "county": "New York",
        "location": {
          "lat": 40.750189,
          "lon": -74.000337
        },
        "timezone": {
          "source": "timezone-boundary-builder",
          "product": "Time zone boundaries (with oceans)",
          "attribution": "Time zone boundaries © OpenStreetMap contributors, timezone-boundary-builder (ODbL); time zone rules: IANA tz database",
          "iana_name": "America/New_York",
          "name": "Eastern",
          "abbreviation": "EST",
          "abbreviation_dst": "EDT",
          "utc_offset": -5,
          "utc_offset_dst": -4,
          "observes_dst": true
        },
        "input_type": "code"
      },
      "distance": {
        "miles": 2453.3,
        "km": 3948.15
      },
      "bearing": 65.9,
      "success": true
    }
  ],
  "meta": {
    "version": "2.0.0",
    "timing_ms": 6,
    "credits": {
      "used": 1,
      "remaining": 856902,
      "breakdown": {
        "base": 1,
        "enrichments": {}
      },
      "source": "subscription"
    },
    "request_id": "01M4CRSMQEFVH34GD8XJPVPW0J"
  }
}

Health Check

Returns API and database health status. No authentication required.

GET /v2/health

Three-state health: healthy (normal), degraded (DB latency >500ms), unhealthy (DB unreachable, returns HTTP 503).

ready is true once the server has finished loading after a start or restart; until then loading lists what is still loading (radius_centroid, radius_spatial, suggest, acs_cache, address, coordinates) and /v2/radius, /v2/radius/batch and /v2/suggest answer 503 SERVICE_WARMING_UP until what they need has loaded. The status code stays 200 while loading.

Response
{
  "success": true,
  "data": {
    "status": "healthy",
    "database": {
      "connected": true,
      "latencyMs": 3
    },
    "version": "2.0.0",
    "ready": true,
    "loading": []
  },
  "meta": {
    "version": "2.0.0",
    "timing_ms": 3
  }
}

Purchase Credits

Buys one credit pack, paid with an MPP wallet: for anyone with one, an agent or a person. With an API key the pack is added to that key's account, and a prepaid card's key reloads the card; sent without any key, it makes a new prepaid card. How paying works (the 402 challenge, the wallet, the Payment-Authorization header) is under Buying Credits with a Wallet (MPP).

POST /v2/purchase

Parameters

A JSON body naming one agent pack in pricing.json (its available_to lists agents; agent packs start at $5). Send at least one of the three fields; sent together they must name the same pack, and a value two packs share is refused with the matches listed.

ParameterTypeRequiredDescription
packstringOne of threeThe pack's name as pricing.json lists it, case-insensitive ("mini" = "Mini")
price_usdnumberOne of threeThe pack's price in US dollars
creditsintegerOne of threeThe pack's credits
Payment-AuthorizationheaderOptionalThe paid MPP credential (Payment followed by its base64url token, shown below), sent on the retry after a 402 PAYMENT_REQUIRED. Authorization: Payment … (the MPP default) is read the same way; a caller paying with a key then sends the key in X-Api-Key. Without a credential the call answers that 402 with the challenge.
X-Api-KeyheaderOptionalThe account (or prepaid card) the pack is for; Authorization: Bearer works too. Without any key, the purchase makes a new prepaid card.

Credit Cost

0 credits. The payment is the pack's price; a successful answer carries a Payment-Receipt header. Credits are added only after the payment completes (a card clears at once; some methods take longer), and purchased credits never expire.

Response Fields

results[0] is the purchase, in its current state. GET /v2/purchases/{id} answers the same object.

FieldTypeDescription
purchase_idstringThe order number: PP- (bought through the API) or ZC- (bought on the website). An opaque string; its length may grow.
statusstringprocessing (still clearing, no credits yet), succeeded, failed (nothing charged), refunded or disputed
api_keystring (optional)The new prepaid card's key; present only on a purchase made without a key. The same payment sent again returns it again while the card is unused, for up to 24 hours after the payment settles. Keep it secret: whoever holds it can use the card.
packobjectThe pack bought, as it was at purchase time: name, credits, price_usd
chargedobjectamount_usd; method is stripe (card or Link; new payment methods add values); reference is the Stripe PaymentIntent id (pi_...), null until the payment reached Stripe; network, asset and transaction are null for a Stripe payment. Whether the money was taken is status.
creditsobjectadded (0 until the purchase succeeds); removed (unspent credits taken back after a refund or dispute; absent until then); purchased_remaining (every purchased credit left on the account or card, excluding free and subscription credits)
receipt_urlstring or nullStripe's hosted receipt; null until the payment succeeds
createdstringWhen the purchase was made (UTC)
settledstring or nullWhen it succeeded or failed (UTC); null while processing
termsstringThe Terms of Service the payment accepted

Answers and Errors

StatusMeaning
201The payment succeeded and the credits were added (a new prepaid card's key in api_key). A Payment-Receipt header comes with it.
202The payment is still clearing (status processing, no credits yet; a new card's key comes with it). Keep purchase_id and read GET /v2/purchases/{id}; never buy again.
200The same credential again: the purchase as it stands. Nothing is charged twice.
400VALIDATION_MISSING_PARAM (no pack, price_usd or credits), PACK_NOT_AVAILABLE (no agent pack matches, more than one does, or the paid challenge is for another pack). Nothing is charged.
402PAYMENT_REQUIRED (no Payment-Authorization header), PAYMENT_FAILED (declined, nothing charged; a new challenge comes with it, except for a card declined twice today), PAYMENT_CREDENTIAL_INVALID (not our challenge, changed or expired; a new challenge comes with it), PAYMENT_PENDING (a purchase for the account or card is still clearing; error.payment.purchase_id names it). A data request answers PAYMENT_PENDING too while a purchase is clearing.
403PURCHASES_DISABLED (the account owner switched API purchases off), PURCHASES_ON_HOLD (purchases paused for this account, until a time or until reviewed), AUTH_TEST_KEY_RESTRICTED (test and demo keys cannot buy).
429RATE_LIMIT_MINUTE: more than 5 payment attempts a minute per key (20 per IP). Retry-After gives the seconds.
503SERVICE_UNAVAILABLE: purchases through the API are paused (Retry-After: 300), or the account or card was busy with another payment (Retry-After: 5). Nothing is charged.

Example Request

First without a Payment-Authorization header, to get the challenge:

cURL
curl -X POST -H "X-Api-Key: YOUR_API_KEY" -H "Content-Type: application/json" \
  -d '{"pack":"Mini"}' "https://api.zip-codes.com/v2/purchase"

It answers 402 PAYMENT_REQUIRED with a WWW-Authenticate: Payment challenge header and the offer in error.payment:

Response — 402 (abridged)
{
  "success": false,
  "request": {
    "pack": "Mini"
  },
  "error": {
    "code": "PAYMENT_REQUIRED",
    "message": "Payment required: $5.00 buys the Mini pack (6,250 credits). Pay the MPP challenge in the WWW-Authenticate header and send this same request again with a Payment-Authorization header.",
    "payment": {
      "offer": {
        "name": "Mini",
        "credits": 6250,
        "price_usd": 5
      },
      "methods": [
        {
          "method": "stripe",
          "challenge_id": "...",
          "pays_with": [
            "card",
            "link"
          ],
          "expires": "2026-10-31T18:09:12Z"
        }
      ],
      "purchase_url": "https://api.zip-codes.com/v2/purchase",
      "packs": "https://api.zip-codes.com/pricing.json",
      "terms": "https://www.zip-codes.com/api/tos"
    }
  },
  "meta": {
    "version": "2.2.0",
    "timing_ms": 3
  }
}

Pay the challenge with your wallet, then send the same request again with the credential. The credential is Payment followed by the base64url (no padding) of this JSON: the challenge's parameters exactly as the WWW-Authenticate header gave them, and the wallet's shared payment token (spt_…, limited to the challenge's amount):

Credential, decoded
{
  "challenge": {
    "id": "from the challenge",
    "realm": "api.zip-codes.com",
    "method": "stripe",
    "intent": "charge",
    "request": "from the challenge, unchanged",
    "expires": "from the challenge"
  },
  "payload": {
    "spt": "spt_..."
  }
}

Send it in Payment-Authorization (the header the challenge names) or in Authorization. A new prepaid card's key starts pp_card_; account keys start zc_live_.

cURL
curl -X POST -H "X-Api-Key: YOUR_API_KEY" -H "Content-Type: application/json" \
  -H "Payment-Authorization: Payment eyJ..." \
  -d '{"pack":"Mini"}' "https://api.zip-codes.com/v2/purchase"

Example Response

Response — 201
{
  "success": true,
  "request": {
    "pack": "Mini"
  },
  "results": [
    {
      "purchase_id": "PP-G2TH5-EKHBD",
      "status": "succeeded",
      "pack": {
        "name": "Mini",
        "credits": 6250,
        "price_usd": 5
      },
      "charged": {
        "amount_usd": 5,
        "method": "stripe",
        "network": null,
        "asset": null,
        "reference": "pi_...",
        "transaction": null
      },
      "credits": {
        "added": 6250,
        "purchased_remaining": 6250
      },
      "receipt_url": "https://...",
      "created": "2026-10-31T18:04:12Z",
      "settled": "2026-10-31T18:04:13Z",
      "terms": "https://www.zip-codes.com/api/tos"
    }
  ],
  "meta": {
    "version": "2.2.0",
    "timing_ms": 912
  }
}

Balance

Reads the credit pools of this key's account or prepaid card: what it can spend now. A request draws on free or subscription first, then purchased.

GET /v2/balance

Parameters

ParameterTypeRequiredDescription
X-Api-KeyheaderRequiredThe account's or prepaid card's key, in a header (X-Api-Key or Authorization: Bearer), never ?key=

Credit Cost

0 credits. Limit: 10 a minute per key, shared with GET /v2/purchases/{id}.

Response Fields

results[0] = {free, subscription, purchased, purchase_url, packs}. The pool names are meta.credits.source's words.

FieldTypeDescription
freeobject or nullThe free daily allowance: remaining, limit (credits per UTC day), resets (the next UTC midnight); null for a subscriber or a prepaid card
subscriptionobject or nullThe current billing period: plan, remaining (may exceed limit after a plan change), limit, resets (the period's end, UTC; null when not known); null without a subscription (always for a prepaid card)
purchasedobjectPack credits: remaining, expires (null: purchased credits never expire)
purchase_urlstringWhere to buy a pack through the API (POST /v2/purchase)
packsstringThe packs on sale, their credits and prices (pricing.json)

Errors

StatusMeaning
401AUTH_MISSING_KEY or AUTH_INVALID_KEY, or the key sent as ?key=: this call takes the key in a header.
403AUTH_TEST_KEY_RESTRICTED: test, demo and admin keys cannot read a balance.
429RATE_LIMIT_MINUTE: more than 10 lookups a minute for this key (balance and purchase lookups together). Retry-After gives the seconds.

Example Request

cURL
curl -H "X-Api-Key: YOUR_API_KEY" \
  "https://api.zip-codes.com/v2/balance"

Example Response

Response — free account with a pack
{
  "success": true,
  "results": [
    {
      "free": {
        "remaining": 2380,
        "limit": 2500,
        "resets": "2026-11-01T00:00:00Z"
      },
      "subscription": null,
      "purchased": {
        "remaining": 6250,
        "expires": null
      },
      "purchase_url": "https://api.zip-codes.com/v2/purchase",
      "packs": "https://api.zip-codes.com/pricing.json"
    }
  ],
  "meta": {
    "version": "2.2.0",
    "timing_ms": 4
  }
}

Purchase Lookup

Reads one purchase on this key's account or prepaid card by its order number, in its current state. Use it to follow a 202 from POST /v2/purchase until status reads succeeded.

GET /v2/purchases/{id}

Parameters

ParameterTypeRequiredDescription
idstring (path)RequiredThe order number as purchase_id returned it, e.g. PP-G2TH5-EKHBD (PP- through the API, ZC- on the website; dashes and case are forgiven; at most 32 characters)
X-Api-KeyheaderRequiredThe account's or prepaid card's key, in a header (X-Api-Key or Authorization: Bearer), never ?key=

Credit Cost

0 credits. Limit: 10 a minute per key, shared with GET /v2/balance; after 20 unknown purchase ids in an hour, purchase lookups answer 429 until the hour ends.

Response Fields

results[0] is the purchase, the same object POST /v2/purchase answers (see its response fields); api_key is never included here.

Errors

StatusMeaning
401AUTH_MISSING_KEY or AUTH_INVALID_KEY, or the key sent as ?key=: this call takes the key in a header.
403AUTH_TEST_KEY_RESTRICTED: test, demo and admin keys cannot read a purchase.
404PURCHASE_NOT_FOUND: no purchase with this id on this key's account or prepaid card (another account's or card's purchase answers the same).
429RATE_LIMIT_MINUTE: more than 10 lookups a minute for this key (purchase lookups and balance together), or 20 unknown purchase ids in an hour (Retry-After until the hour ends).

Example Request

cURL
curl -H "X-Api-Key: YOUR_API_KEY" \
  "https://api.zip-codes.com/v2/purchases/PP-G2TH5-EKHBD"

Example Response

Response — still clearing
{
  "success": true,
  "results": [
    {
      "purchase_id": "PP-G2TH5-EKHBD",
      "status": "processing",
      "pack": {
        "name": "Mini",
        "credits": 6250,
        "price_usd": 5
      },
      "charged": {
        "amount_usd": 5,
        "method": "stripe",
        "network": null,
        "asset": null,
        "reference": "pi_...",
        "transaction": null
      },
      "credits": {
        "added": 0,
        "purchased_remaining": 0
      },
      "receipt_url": null,
      "created": "2026-10-31T18:04:12Z",
      "settled": null,
      "terms": "https://www.zip-codes.com/api/tos"
    }
  ],
  "meta": {
    "version": "2.2.0",
    "timing_ms": 5
  }
}

Batch Requests

Five routes support batch processing via POST: quick-zip, zip, radius, distance, and address. Address accepts 250 items; the other four accept 100 items. There is no suggest batch: POST /v2/suggest is a single call. Individual items can succeed or fail independently (the overall HTTP status is 200 even if some items fail).

Subscription or purchased credits required: Batch endpoints need a paid subscription (Developer tier or higher) or purchased credits (a credit pack). Other accounts receive an AUTH_BATCH_REQUIRES_SUBSCRIPTION error.

Each batch request uses its route-specific array (codes, searches, pairs, or addresses); request echoes the corresponding count. Failed items contain {success:false,input:{...},error:{code,message,url}}. Credit totals include source, remaining, and breakdown.

Batch Response Meta

Batch Meta
{
  "meta": {
    "batch": {
      "requested": 3,
      "succeeded": 2,
      "failed": 1
    },
    "credits": {
      "used": 5,
      "source": "subscription",
      "remaining": 74995,
      "breakdown": {
        "base": 0,
        "enrichments": {},
        "note": "2 codes, 5 total credits"
      }
    },
    "request_id": "01M3SX2V9NY9MRRAMWVJG0C09E",
    "version": "2.0.0",
    "timing_ms": 1
  }
}
Duplicate detection: Duplicate items are processed and billed individually, with a BATCH_DUPLICATES_DETECTED notice. Credits are reserved up front, unused credits are refunded, and disconnect/timeout refunds all credits.

Batch Endpoints

EndpointURLBody Format
Quick ZIP POST /v2/quick-zip/batch {"codes": ["90210", "M5V"], "include": "timezone"}
ZIP Details POST /v2/zip/batch {"codes": ["90210", "10001"], "include": "timezone,census"}
Radius POST /v2/radius/batch {"searches": [{"code": "90210", "max": 25}]}
Distance POST /v2/distance/batch {"pairs": [{"from": "90210", "to": "10001"}]}
Address (250 max)POST /v2/address/batch{"addresses":["1600 Pennsylvania Ave NW, Washington DC 20500"],"include":["coordinates","acs_tract_economic"]}

All other routes allow 100 items. include accepts a comma-separated string or array of up to 50 strings. Radius searches accept {code,max,min,include,limit}; top-level include, max, and min supply defaults overridden by per-search values. mode is batch-level only; default centroid, while spatial returns HTTP 400 SPATIAL_MODE_DISABLED_BATCH. Radius item input is {index,code,max}. Missing body/fields and oversized batches return HTTP 400; subscription/test-key restrictions 403; insufficient credits 402. Valid batches return HTTP 200.

Example: Quick ZIP Batch

Request
curl -X POST "https://api.zip-codes.com/v2/quick-zip/batch" \
  -H "X-Api-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"codes": ["90210", "M5V", "99999"], "include": "timezone"}'

Partial Failure

When some items fail, the overall HTTP status is still 200. Failed items appear in the results array with success: false and a per-item error object. The meta.batch summary tells you how many succeeded and failed:

Partial Failure Response
{
  "success": true,
  "results": [
    {
      "success": true,
      "code": "90210",
      "city": "Beverly Hills",
      "state": "CA",
      "input": {
        "code": "90210"
      }
    },
    {
      "success": true,
      "code": "M5V",
      "city": "Toronto",
      "state": "ON",
      "input": {
        "code": "M5V"
      }
    },
    {
      "success": false,
      "error": {
        "code": "NOT_FOUND_ZIP",
        "message": "Code 99999 not found.",
        "url": "https://www.zip-codes.com/api/"
      },
      "input": {
        "code": "99999"
      }
    }
  ],
  "meta": {
    "batch": {
      "requested": 3,
      "succeeded": 2,
      "failed": 1
    },
    "credits": {
      "used": 5,
      "source": "subscription",
      "remaining": 74995,
      "breakdown": {
        "base": 0,
        "enrichments": {},
        "note": "2 codes, 5 total credits"
      }
    },
    "request_id": "01M3SX2V9NY9MRRAMWVJG0C09E",
    "version": "2.0.0",
    "timing_ms": 1
  },
  "request": {
    "codes_count": 3,
    "include": "timezone"
  }
}

Data Enrichments

All enrichments are opt-in. Add them to the include parameter as a comma-separated list. On /zip each billable flag adds 1 credit; quick-zip and radius multiply their base by the number of data layers. Address charges requested coordinates plus delivered boundary flags and tract tags. See Credit Costs.

About This Data

US ZIP code boundaries come from a commercial boundary provider, updated quarterly. The dataset includes ~33,400 polygon boundaries plus ~7,600 point-only entries (PO Box, Military, and Unique ZIP types that have no geographic area).

Census geographies (counties, tracts, places, etc.) are drawn independently of ZIP codes and almost never align. Where they overlap, each result includes two percentages: pct_of_zip and pct_of_area.

Understanding pct_of_zip vs. pct_of_area

pct_of_zip = intersection area ÷ ZIP area — "What percentage of this ZIP does the geography cover?" It is a share of area, not of population: if a county covers 60% of a ZIP, the share of the ZIP's residents living there can be higher or lower, since people are not spread evenly. Overlaps under a small sliver threshold are left out, so a ZIP's rows can add up to a little under 100.

pct_of_area = intersection area ÷ the part of the geography that ZIP Code polygons cover — "What percentage of the geography falls within this ZIP?" Open water and land outside every ZIP Code are not counted, so a geography's shares across all its ZIP Codes add up to about 100. Use this to gauge significance: a large county may overlap a ZIP but that ZIP is only a tiny sliver of the county. It is null for a geography with no land or less than 1% covered by ZIP Codes (mostly water). School districts use the district's total area instead.

Example: A county covers 40% of a ZIP (pct_of_zip = 40.0), but that overlap is only 2% of the county (pct_of_area = 2.0). The county is meaningful to the ZIP, but the ZIP is insignificant to the county.

shared_area_sq_mi is the overlap itself in square miles. land_area_sq_mi and water_area_sq_mi are the whole geography's Census land and water areas, not the part inside the ZIP. Both percentages and the shared area are null for a point (an address, coordinates, or a ZIP Code answered from its point).

About This Data

Canadian FSAs (Forward Sortation Areas, ~1,643): The first 3 characters of a postal code. Polygon boundaries from Statistics Canada, updated with each census cycle (every 5 years).

Full Canadian postal codes (~876,000): All 6 characters. Point coordinates only — Canada Post provides centroids but postal code polygon boundaries are not publicly available. Included in /v2/radius centroid mode (default). Not available in spatial mode.

US ZIP+4 (~40M codes): Uses the ZIP+4 centroid when available. ZIP+4 areas change monthly with carrier route reassignment and have no stable polygon boundary.

FlagDescriptionEndpointsAdded costAvailability
timezoneIANA timezone with DST infoQuick ZIP, ZIP, Radius, AddressOne data layer (× endpoint base)US + CA
censusCensus boundary overlaps (US Census Bureau; on a Canadian code, answered as statcan)ZIP, Address+1US only
cdCongressional districts (US Census Bureau; on a Canadian code, answered as statcan)ZIP, Address+1US only
statcan, statcan_{YYYY}Statistics Canada geography and federal electoral districts (2021; statcan_2016, statcan_2011)ZIP+1 eachCA only
state_legState legislature districtsZIP, Address+1US only
census_{YYYY}, cd_{YYYY}, state_leg_{YYYY}Boundary history: that year’s census, congressional or state legislative boundaries, 2010–2025 (the bare flags return 2026)ZIP, Address+1 eachUS only
school_districtSchool district boundaries (NCES)ZIP, Address+1US only
acs_demographicPopulation, age, sex, race (DP05)ZIP, RadiusOne data layer (× endpoint base)US only
acs_socialHouseholds, education, language (DP02)ZIP, RadiusOne data layer (× endpoint base)US only
acs_economicEmployment, income, industry (DP03)ZIP, RadiusOne data layer (× endpoint base)US only
acs_housingHousing units, occupancy, value (DP04)ZIP, RadiusOne data layer (× endpoint base)US only
acs_{profile}_{YYYY}Historical ACS for years 2011–2024. Append year to any ACS flag (e.g., acs_housing_2022). 4 profiles × 14 years = 56 flags. See ACS Data for year-by-year coverage.ZIP, RadiusOne layer per tag (× endpoint base)US only
demographicsPopulation, race, age, households, income, businessZIP+1US only
medicareMedicare CBSA, rating area, SSA codeZIP+1US only
coordinatesZIP+4 point, latitude/longitude (6 decimals)Address+1 requestedUS
acs_tract_{demographic|social|economic|housing}[_YYYY]Containing-tract ACS, 2011–2024; no suffix = 2024Address+1 per delivered tagUS
Canadian availability: Requesting US-only enrichments for Canadian codes omits unavailable model fields (explicit census/ACS nulls may remain) and adds an ENRICHMENT_UNAVAILABLE notice. No credit is charged for unavailable enrichments. census and cd (and their _YYYY forms) on a Canadian code are answered under statcan with an ENRICHMENT_FLAG_RENAMED notice.

Timezone

Returns IANA timezone data including standard and daylight saving abbreviations and UTC offsets. On /v2/zip and /v2/address (and their batches) it is one block: source, product, attribution and a zones array with each zone's share of the ZIP (pct_of_zip; null for a point: an address, coordinates, or a ZIP with only a centroid). /v2/quick-zip and /v2/distance (and their batches) return one flat object: the same source, product and attribution, then one zone's fields (the zone covering most of the ZIP, or containing the point). On /v2/radius, timezone is a flat object per match.

Fields Returned

FieldTypeDescription
sourcestringData source (publisher)
productstringThe data product (US blocks; see Spatial layers: full provenance)
attributionstringThe credit line to show with the data (US blocks)
iana_namestringIANA timezone identifier (e.g., America/Los_Angeles)
namestringDisplay name (e.g., Pacific Standard Time)
abbreviationstringStandard time abbreviation (e.g., PST)
abbreviation_dststringDaylight saving abbreviation (e.g., PDT)
utc_offsetnumberStandard UTC offset in hours (e.g., -8)
utc_offset_dstnumberDST UTC offset in hours (e.g., -7)
observes_dstbooleanWhether this timezone observes daylight saving time
pct_of_zipnumber or nullPercentage of the ZIP's area in this timezone (1 decimal; zones under 1% are left out); null for a point
On /v2/zip and /v2/address, the timezone object contains a zones[] array (sorted by coverage descending); there is no primary shortcut. On /v2/quick-zip, /v2/radius and /v2/distance, a single flat timezone object is returned.
Timezone Object
{
  "source": "timezone-boundary-builder",
  "product": "Time zone boundaries (with oceans)",
  "attribution": "Time zone boundaries © OpenStreetMap contributors, timezone-boundary-builder (ODbL); time zone rules: IANA tz database",
  "zones": [
    {
      "iana_name": "America/Los_Angeles",
      "name": "Pacific",
      "abbreviation": "PST",
      "abbreviation_dst": "PDT",
      "utc_offset": -8,
      "utc_offset_dst": -7,
      "observes_dst": true,
      "pct_of_zip": 100.0
    }
  ]
}

Census Boundaries

Canadian codes return Statistics Canada geography under statcan (flag statcan; census, census_YYYY, cd and cd_YYYY on a Canadian code are legacy aliases answered there with an ENRICHMENT_FLAG_RENAMED notice). statcan.current is Statistics Canada’s latest census (2021; statcan_2016 and statcan_2011 add "2016" / "2011"), opening with source, product, attribution, year and vintage. Postal-code periods list tract, census_division, census_subdivision, dissemination_area, dissemination_block, metro_area, population_centre, economic_region, consolidated_subdivision, designated_place, province and federal_electoral_district (the areas containing the postal code’s location); FSA periods list census_division, census_subdivision, tract, metro_area, economic_region, population_centre, consolidated_subdivision, designated_place, province and federal_electoral_district.

Postal codes and FSAs return the same row, in this order: uid, dguid (null for 2016 and 2011, whose files have none), name, name_en and name_fr (federal electoral districts and provinces: name is Statistics Canada’s bilingual name), type and type_description (census divisions, subdivisions, metro areas, population centres), class and class_description (population centres), area_sq_mi and area_km2 (the area’s total area, water included), land_area_sq_mi and land_area_km2 (the area’s land area, every census year; for 2016 and 2011 summed from Statistics Canada’s dissemination-block land areas in the Geographic Attribute File), then pct_of_zip (postal code: always 100) or pct_of_fsa (FSA: share of the FSA), and pct_of_area (FSA: share of the area; null for a postal code). pct_of_fsa and pct_of_area are null for an FSA without a Statistics Canada polygon.

"statcan": {
  "current": {
    "source": "Statistics Canada", "product": "...", "attribution": "...",
    "year": 2021, "vintage": "2021",
    "census_subdivision": [{
      "uid": "3506008", "dguid": "2021A00053506008", "name": "Ottawa",
      "type": "CV", "type_description": "City / Ville",
      "area_sq_mi": 1116.67, "area_km2": 2892.15,
      "land_area_sq_mi": 1076.53, "land_area_km2": 2788.2,
      "pct_of_zip": 100, "pct_of_area": null
    }],
    "federal_electoral_district": [{
      "uid": "35075", "dguid": "2013A000435075", "name": "Ottawa Centre / Ottawa-Centre",
      "name_en": "Ottawa Centre", "name_fr": "Ottawa-Centre",
      "area_sq_mi": 14.42, "area_km2": 37.35,
      "land_area_sq_mi": 12.52, "land_area_km2": 32.43,
      "pct_of_zip": 100, "pct_of_area": null
    }]
  },
  "2016": { ... }
}

US sections are under results[i].census.current: county, county_subdivision, place, tract, block_group, metro_micro_cbsa_statistical_area and metropolitan_cbsa_division are arrays, largest pct_of_zip first. Empty lookups are null.

Boundary families. Each boundary flag returns its own top-level family: census, congressional_district (cd), state_legislative (state_leg), and school_district; Canadian codes return one family, statcan, with federal_electoral_district inside each period. Each family holds its latest year under current; census_YYYY, cd_YYYY and state_leg_YYYY (2010–2025) add that year under its own "YYYY" key beside current, newest first (school_district serves current only). A requested family with nothing found is present with current: null; an unrequested family is absent.

Returns all census geographic boundaries that overlap the ZIP code with percentage overlap calculations. Data sourced from the U.S. Census Bureau’s full-resolution TIGER/Line Shapefiles.

About This Data

Census boundaries are hierarchical areas defined for statistical reporting — they are not drawn to follow ZIP code boundaries. A single ZIP almost always overlaps multiple tracts, places, and other geographies.

FIPS code structure and geography details
GeographyFIPS LengthExample
State2 digits06 (California)
County5 digits06037 (Los Angeles Co.)
County Subdivision10 digits0603765000
Place7 digits0644000 (Los Angeles city)
Census Tract11 digits06037264000
Block Group12 digits060372640001
Congressional District4 digits0634

Counties (~3,200): Primary legal division of every state. Connecticut replaced its 8 counties with 9 Planning Regions in 2022 (Census Bureau adopted these starting with 2024 data). Louisiana has parishes, Alaska has boroughs, and some cities in Virginia and Maryland are independent of any county.

County Subdivisions: Minor Civil Divisions (MCDs) in ~29 states represent legal townships and towns. In other states, Census-defined County Divisions (CCDs) serve as statistical equivalents.

Places: Incorporated places (cities, towns, villages with legal boundaries) and Census Designated Places (CDPs — statistical areas for unincorporated communities).

Census Tracts (~85,000): Designed for 2,500–8,000 people with socioeconomic homogeneity. The tract code has an implied decimal: 046300 = tract 463.00.

Block Groups: The smallest geography with ACS data, containing 600–3,000 people. GEOID = parent tract code + 1 digit.

CBSAs: Metropolitan Statistical Areas (core city 50K+ population) or Micropolitan (10K–50K). Rural areas have no CBSA. Metropolitan Divisions subdivide the largest metros into component areas.

Included Boundary Types

BoundaryDescription
countyCounty boundaries with FIPS codes
county_subdivisionCounty subdivisions (CCDs, MCDs)
placeCensus-designated places and incorporated cities
tractCensus tracts with GEOID
block_groupCensus block groups
metro_micro_cbsa_statistical_areaEvery metropolitan or micropolitan statistical area the ZIP overlaps, largest pct_of_zip first
metropolitan_cbsa_divisionEvery metro division the ZIP overlaps, largest pct_of_zip first (where applicable)

Boundary identifiers use fips for counties, cbsa_code for CBSAs, or geoid as applicable; other fields include, name, land_area_sq_mi, water_area_sq_mi, pct_of_zip (% of ZIP this boundary covers), and pct_of_area (% of boundary this ZIP covers).

Census Boundary Object (excerpt)
{
  "census": {
    "current": {
      "county": [
        {
          "name": "Los Angeles",
          "land_area_sq_mi": 4060.29,
          "water_area_sq_mi": 689.15,
          "pct_of_zip": 100,
          "pct_of_area": 0.27,
          "fips": "06037"
        }
      ],
      "place": [
        {
          "geoid": "0606308",
          "name": "Beverly Hills",
          "land_area_sq_mi": 5.71,
          "water_area_sq_mi": 0,
          "pct_of_zip": 37.16,
          "pct_of_area": 71.42
        }
      ],
      "tract": [
        {
          "geoid": "06037261102",
          "name": "2611.02",
          "land_area_sq_mi": 3.07,
          "water_area_sq_mi": 0,
          "pct_of_zip": 27.96,
          "pct_of_area": 99.98
        }
      ],
      "metro_micro_cbsa_statistical_area": [
        {
          "name": "Los Angeles-Long Beach-Anaheim, CA",
          "land_area_sq_mi": 4853.54,
          "water_area_sq_mi": 845.63,
          "pct_of_zip": 100,
          "pct_of_area": 0.22,
          "cbsa_code": "31080"
        }
      ],
      "source": "U.S. Census Bureau",
      "product": "TIGER/Line Shapefiles",
      "attribution": "Source: U.S. Census Bureau, TIGER/Line Shapefiles 2025",
      "year": 2025,
      "vintage": "2025"
    }
  }
}

Congressional Districts

Returns congressional district overlaps for the current Congress session, with primary district identification and area percentages. Fields sit under congressional_district.current (cd_YYYY adds an earlier year, 2010–2025, under its "YYYY" key), beside the boundary file's source, product, attribution, year, and vintage.

Fields Returned (US)

FieldTypeDescription
congressnumberCongressional session number (e.g., 119)
primary.geoidstringGEOID of the primary district (see primary.basis)
primary.statestringState abbreviation
primary.district_numberintegerDistrict number
primary.basisstringHow the primary district was chosen. On /v2/zip: the district covering the largest share of the ZIP Code’s area (by area, not population), or the district containing the ZIP Code point. On /v2/address: the district containing the matched location, except in the newest year, where it is the district USPS assigns to the ZIP+4 when USPS assigns one
districts[]arrayAll overlapping districts with geoid, name, state, district_number, land_area_sq_mi, water_area_sq_mi, pct_of_zip, pct_of_area

Fields Returned (CA)

Canadian codes return federal electoral districts inside each statcan period instead, as the list statcan.current.federal_electoral_district (and statcan["2016"].federal_electoral_district for statcan_2016); there is no separate top-level family and no extra charge. cd on a Canadian code is answered as statcan with an ENRICHMENT_FLAG_RENAMED notice. Full postal codes return the district containing their location; FSA boundary overlaps include pct_of_fsa. Each district is the census row above, with name in both languages ("Ottawa Centre / Ottawa-Centre"), name_en and name_fr. Canadian codes never return congressional_district, state_legislative, or school_district.

Congressional District Object
{
  "congressional_district": {
    "current": {
      "source": "U.S. Census Bureau",
      "product": "TIGER/Line Shapefiles",
      "attribution": "Source: U.S. Census Bureau, TIGER/Line Shapefiles 2025",
      "year": 2025,
      "vintage": "2025",
      "congress": 119,
      "districts": [
        {
          "geoid": "0632",
          "state": "CA",
          "district_number": 32,
          "name": "Congressional District 32",
          "land_area_sq_mi": 296.24,
          "water_area_sq_mi": 92.38,
          "pct_of_zip": 62.76,
          "pct_of_area": 2.32
        },
        {
          "geoid": "0636",
          "state": "CA",
          "district_number": 36,
          "name": "Congressional District 36",
          "land_area_sq_mi": 97.11,
          "water_area_sq_mi": 97.51,
          "pct_of_zip": 37.16,
          "pct_of_area": 4.19
        }
      ],
      "primary": {
        "geoid": "0632",
        "state": "CA",
        "district_number": 32
      }
    }
  }
}

State Legislature

Returns state senate (upper chamber) and assembly/house (lower chamber) district overlaps with area percentages, under state_legislative.current (state_leg_YYYY adds an earlier year, 2010–2025, under its "YYYY" key) beside source, product, attribution, year, and vintage. US only

Fields Returned

FieldTypeDescription
legislative_yearintegerLegislative year of the district plan (e.g., 2024)
upper[]arrayState Senate district overlaps
lower[]arrayState House/Assembly district overlaps

Each district includes: geoid, name, district_code, state, state_fips, legislative_session_year, land_area_sq_mi, water_area_sq_mi, pct_of_zip, pct_of_area.

State Legislature Object
{
  "state_legislative": {
    "current": {
      "source": "U.S. Census Bureau",
      "product": "TIGER/Line Shapefiles",
      "attribution": "Source: U.S. Census Bureau, TIGER/Line Shapefiles 2025",
      "year": 2025,
      "vintage": "2025",
      "legislative_year": 2024,
      "upper": [
        {
          "geoid": "06024",
          "name": "24",
          "name_full": "State Senate District 24",
          "state": "CA",
          "state_fips": "06",
          "district_code": "024",
          "legislative_session_year": "2024",
          "land_area_sq_mi": 327.55,
          "pct_of_zip": 92.81
        }
      ],
      "lower": [
        {
          "geoid": "06051",
          "name": "51",
          "name_full": "Assembly District 51",
          "state": "CA",
          "state_fips": "06",
          "district_code": "051",
          "legislative_session_year": "2024",
          "land_area_sq_mi": 56.4,
          "pct_of_zip": 63.49
        }
      ]
    }
  }
}

School Districts

Returns NCES school district data with boundary overlaps, under school_district.current with source, product, attribution, school_year, and a districts array. US only

About This Data

District boundaries are from the National Center for Education Statistics (NCES). A single ZIP can overlap multiple school districts because different district types serve the same geographic area at different grade levels.

District type breakdown

Unified: Serves all grades K–12. The most common type nationwide.

Elementary / Secondary: Grade-banded districts that overlap geographically. Elementary districts cover lower grades (K–6 or K–8), while secondary districts cover upper grades (7–12 or 9–12).

Administrative: Supervisory unions (52 records, all in Vermont). The leaid field is a 7-character NCES Local Education Agency ID.

Locale codes: Range from 11 (City—Large) to 43 (Rural—Remote). Returned in the locale object as both a numeric code and description.

Fields Returned per District

FieldTypeDescription
leaidstringNCES Local Education Agency ID
namestringDistrict name
typestringDistrict type: unified, elementary, secondary, administrative, other
grade_low / grade_highstringGrade range served (e.g., PK to 12)
officeobjectOffice address, city, state, ZIP, county, lat/lon
localeobjectLocale classification code and description (city/suburb/town/rural)
pct_of_zip / pct_of_areanumberOverlap percentages
land_area_sq_minumberDistrict land area
School District Object
{
  "school_district": {
    "current": {
      "source": "National Center for Education Statistics (NCES)",
      "product": "EDGE School District Boundaries",
      "attribution": "Source: U.S. Department of Education, National Center for Education Statistics (NCES), EDGE School District Boundaries, SY 2024-25 (TIGER 2025)",
      "school_year": "2024-2025",
      "districts": [
        {
          "leaid": "0604830",
          "name": "Beverly Hills Unified School District",
          "type": "unified",
          "grade_low": "KG",
          "grade_high": "12",
          "state_fips": "06",
          "land_area_sq_mi": 6.02,
          "pct_of_zip": 39.95,
          "pct_of_area": 72.86,
          "centroid": {
            "lat": 34.07923,
            "lon": -118.4024374
          },
          "office": {
            "address": "624 N. Rexford Dr.",
            "city": "Beverly Hills",
            "state": "CA",
            "zip": "90210",
            "county": "Los Angeles County",
            "location": {
              "lat": 34.078986,
              "lon": -118.40392
            }
          },
          "locale": {
            "code": "21",
            "description": "Suburb - Large"
          },
          "nces_codes": {
            "mtfcc": "G5420",
            "mtfcc_desc": "Unified School District",
            "funcstat": "E",
            "funcstat_desc": "Active"
          }
        }
      ]
    }
  }
}

ACS Data

American Community Survey 5-year estimates from the Census Bureau. Four separate data profiles available, each activated by its own flag. US only

ACS metrics include an estimate (est) and margin of error (moe). Percentage fields also include pct and pct_moe. Suppressed Census values are returned as null.

pct is what Census prints in the field’s percent column, which is not always a percentage: for most fields it is the share of the field’s total (race.one_race.asian.chinese.pct, a share of the total population); on a total row it is the count itself (households.total.pct equals households.total.est); a rate is in whichever column Census prints it: employment.unemployment_rate and the poverty fields are published only as a percent, so est is null and the rate is in pct, while the housing vacancy rates (occupancy.homeowner_vacancy_rate, occupancy.rental_vacancy_rate) are published in the estimate column, so the rate is in est and pct is null; and it is null where Census gives no percent (income.per_capita, fertility and vacancy rates). The /v2/radius aggregates follow the same convention field by field: a share is recomputed from the aggregated sums of the field and the total Census divides it by, a total row’s pct is its aggregated count, the unemployment rate is recomputed from the aggregated labor force, and the poverty rates are averages weighted by population.

Data freshness: Every ACS response includes a year field and a vintage field indicating the exact dataset. Current ACS flags (e.g., acs_demographic) return the most recent year (2020–2024). Historical flags (e.g., acs_demographic_2022) return the specified year's dataset. You can request current and historical simultaneously — they appear under acs.current and acs["2022"] respectively. 14 years available: 2011–2024.

acs_demographic — DP05 (108 fields)

SectionKey Fields
Sex and AgeTotal population, male/female counts, age brackets (under 5 through 85+), median age
RaceWhite, Black, American Indian, Asian, Pacific Islander, other, two or more races
Hispanic or Latino and RaceHispanic/Latino totals, breakdown by race within Hispanic/non-Hispanic
Citizen, Voting Age PopulationCitizens 18+, total voting age population
Total Housing UnitsHousing unit count

acs_social — DP02 (154 fields)

SectionKey Fields
Households by TypeTotal households, family/nonfamily, married-couple, single-parent, average size
RelationshipHouseholder, spouse, child, other relatives, nonrelatives, unmarried partner
Marital StatusNever married, now married, separated, widowed, divorced
FertilityWomen 15–50 who had a birth in the past 12 months
GrandparentsGrandparents living with grandchildren, responsible for care
School EnrollmentNursery through graduate school, public/private breakdown
Educational AttainmentLess than 9th grade through doctorate, high school+, bachelor's+
Veteran StatusCivilian veterans 18+
Disability StatusCivilian noninstitutionalized population with a disability
Residence 1 Year AgoSame house, different house (same county, state, abroad)
Place of BirthNative vs. foreign-born, state of birth, world region
Language Spoken at HomeEnglish only, Spanish, Asian/Pacific Island, other, English proficiency
AncestryTop ancestral origins reported
Computers and Internet UseHouseholds with computer, broadband, smartphone, no internet

acs_economic — DP03 (137 fields)

SectionKey Fields
Employment StatusLabor force, employed, unemployed, unemployment rate, armed forces
Commuting to WorkDrove alone, carpooled, public transit, walked, work from home, mean travel time
OccupationManagement, service, sales, construction, production, transportation
IndustryAgriculture, manufacturing, retail, healthcare, education, finance, tech
Class of WorkerPrivate wage, government, self-employed, unpaid family
Income and BenefitsMedian household/family income, per capita, with earnings/Social Security/retirement
Health Insurance CoverageWith/without coverage, private, public, by employment status
Poverty LevelFamilies and people below poverty, by age, family type, education

acs_housing — DP04 (143 fields)

SectionKey Fields
Housing OccupancyTotal units, occupied, vacant, homeowner vacancy rate, rental vacancy rate
Units in StructureSingle-family detached/attached, 2–4 units, 5–19, 20+, mobile home, boat/RV
Year Structure Built2020+, 2010–2019, 2000–2009, through 1939 or earlier
Rooms / Bedrooms1 room through 9+, median rooms, bedroom count distribution
Housing TenureOwner-occupied, renter-occupied, average household size by tenure
Year Householder Moved In2021+, 2018–2020, 2010–2017, through 1989 or earlier
Vehicles AvailableNo vehicle, 1, 2, 3+ vehicles
House Heating FuelGas, electric, fuel oil, coal, wood, solar, other, no fuel
Selected CharacteristicsLacking plumbing, lacking kitchen, no telephone service
ValueOwner-occupied home value distribution, median value
Mortgage StatusWith/without mortgage, second mortgage, home equity loan
Monthly Owner CostsCost distribution, median, as percentage of household income
Gross RentRent distribution, median rent, as percentage of household income

Historical ACS Data (2011–2024)

Append _YYYY to any ACS profile flag to request a specific year. Example: include=acs_housing_2022 returns the 2018–2022 5-Year Estimates for housing. You can request multiple years simultaneously (e.g., include=acs_economic,acs_economic_2019). Each flag adds one data layer: +1 credit on /zip, multiplied by the distance-band base on /radius. The current year's suffix is the same request as the unsuffixed flag (acs_economic and acs_economic_2024 return one block under current and are charged once) — on every endpoint, including the address tract flags. When Census publishes the next release, current moves to it and 2024 is requested as _2024. On /v2/zip, a year Census published no data for at that ZCTA comes back with published: false and is not charged.

YearACS 5-Year EstimatesFieldsCoverage
2024 (current)2020–202452897.4%
20232019–2023528100%
20222018–202252198.7%
20212017–202151898.1%
20202016–202051898.1%
20192015–201951797.9%
20182014–201848692.0%
20172013–201748391.5%
20162012–201646387.7%
20152011–201546387.7%
20142010–201444183.5%
20132009–201344183.5%
20122008–201239474.6%
20112007–201139474.6%
Coverage is relative to the 528-field canonical maximum. Earlier years have fewer fields because Census added new questions over time (e.g., cohabiting couples in 2019, tribal detail in 2022). Fields not available for a given year return null. 394 “universal” fields are available across all 14 years.
On /v2/address, tract profiles use data.acs.tract.current or data.acs.tract["2023"]/["2022"], with the same profile field names. On /v2/zip, ACS returns per-ZIP data under acs.current.{section} (or acs["2022"].{section} for historical), newest year first. Each /v2/zip year block carries published: false means Census published no data for that ZIP's ZCTA in that year (ZCTAs are redrawn each decade), so its profiles are null and a note says so. On /v2/radius, returns aggregates (counts summed, medians and means averaged by each field’s own count, so a radius median is an estimate) under stats.acs.current.{section} — the stats object appears at the top level of each radius result alongside search, matches, and summary. Current dataset: 2020–2024 5-Year Estimates. Historical: 2011–2024 (14 years total).
ACS Data Structure
{
  "acs": {
    "current": {
      "geo_id": "860Z200US90210",
      "published": true,
      "year": 2024,
      "product": "ACS 5-Year Estimates Data Profiles",
      "vintage": "2020-2024",
      "geography_level": "zcta",
      "tables": {
        "demographic": "DP05"
      },
      "demographic": {
        "sex_and_age": {
          "male": {
            "est": 9417,
            "moe": 800,
            "pct": 49.6,
            "pct_moe": 2.3
          },
          "female": {
            "est": 9587,
            "moe": 585,
            "pct": 50.4,
            "pct_moe": 2.3
          },
          "median_age": {
            "est": 51.9,
            "moe": 2.3
          },
          "total_population": {
            "est": 19004,
            "moe": 1101,
            "column": "DP05_0001"
          }
        },
        "race": {
          "one_race": {
            "white": {
              "est": 15147,
              "moe": 1106,
              "pct": 79.7,
              "pct_moe": 3.7
            },
            "black": {
              "est": 403,
              "moe": 193,
              "pct": 2.1,
              "pct_moe": 1
            },
            "asian": {
              "total": {
                "est": 1628,
                "moe": 467,
                "pct": 8.6,
                "pct_moe": 2.4
              }
            }
          }
        }
      },
      "source": "U.S. Census Bureau"
    }
  }
}

Demographics

Returns a compact population, race, age, household and business summary for a US ZIP Code. Each figure comes from the latest release of its source: population, race, age and household counts from the latest decennial Census (tabulated for the ZIP Code Tabulation Area); median household income and median home value from the latest ACS 5-year estimates; business counts and payroll from the latest County Business Patterns; and a current population estimate from USPS delivery points.

Fields Returned

FieldTypeDescription
population
population.totalnumberPopulation from the latest decennial Census; use this for an official count
population.estimatenumberCurrent population estimate, not a Census count: the ZIP Code’s residential delivery points (zip_info.delivery_points.residential, latest USPS month) × its persons per household (households.persons_per_household; where the ZIP Code has none, the average of ZIP Codes within 15 miles, else its county’s). Higher than total where the area has grown since the Census, and where many homes are seasonal or vacant. 0 when there is nothing to estimate from: no residential delivery points, or no persons per household (a PO Box, unique or military ZIP Code without its own, and territories such as the U.S. Virgin Islands)
population.malenumberMale population (latest decennial Census)
population.femalenumberFemale population (latest decennial Census)
race
race.whitenumberWhite population (latest decennial Census)
race.blacknumberBlack or African American population (latest decennial Census)
race.hispanicnumberHispanic or Latino population (latest decennial Census); Hispanic origin is an ethnicity, counted alongside race, so the race counts are not meant to add up to population.total
race.asiannumberAsian population (latest decennial Census)
race.american_indiannumberAmerican Indian and Alaska Native population (latest decennial Census)
race.hawaiian_pacific_islandernumberNative Hawaiian and Other Pacific Islander population (latest decennial Census)
race.othernumberPopulation of all other races (latest decennial Census)
age
age.mediannumberMedian age (latest decennial Census)
age.median_malenumberMedian age of males (latest decennial Census)
age.median_femalenumberMedian age of females (latest decennial Census)
households
households.totalnumberHouseholds (latest decennial Census)
households.persons_per_householdnumberAverage persons per household (latest decennial Census)
households.average_family_sizenumberAverage family size
households.median_incomenumberMedian household income (USD), from the latest ACS 5-year estimates
households.average_home_valuenumberMedian value of owner-occupied homes (USD), from the latest ACS 5-year estimates: a median, despite the field name. Census’s top category ($2,000,000 or more) shows as 2000001
business
business.establishmentsnumberBusiness establishments, from the latest County Business Patterns
business.employeesnumberPaid employees, from the latest County Business Patterns
business.payroll_q1numberFirst-quarter payroll in full USD, from the latest County Business Patterns (published in thousands)
business.payroll_annualnumberAnnual payroll in full USD, from the latest County Business Patterns (published in thousands)
business.employment_flagstringSet when Census withholds the employee count for confidentiality; null when the count is published
This enrichment is a compact summary of the latest figures. For the full ACS profiles by year, with margins of error, use the ACS enrichments.
Demographics Object
{
  "demographics": {
    "population": {
      "total": 21134,
      "estimate": 22973,
      "male": 10099,
      "female": 11035
    },
    "race": {
      "white": 17130,
      "black": 366,
      "hispanic": 1296,
      "asian": 1406,
      "american_indian": 25,
      "hawaiian_pacific_islander": 12,
      "other": 481
    },
    "age": {
      "median": 49.3,
      "median_male": 49.3,
      "median_female": 49.2
    },
    "households": {
      "total": 8553,
      "persons_per_household": 2.47,
      "average_family_size": 3.13,
      "median_income": 187801,
      "average_home_value": 2000001
    },
    "business": {
      "establishments": 2536,
      "employees": 32353,
      "payroll_q1": 648013000,
      "payroll_annual": 2653591000
    }
  }
}

Medicare

Returns Medicare geographic identifiers used in healthcare pricing, insurance rating areas, and CMS administrative boundaries.

Fields Returned

FieldTypeDescription
cbsa_codestringCore Based Statistical Area code used by CMS for Medicare payment
cbsa_namestringCBSA name (e.g., “Los Angeles-Long Beach-Anaheim, CA”)
cbsa_typestringMetropolitan or Micropolitan (omitted if rural)
rating_area_idstringACA marketplace rating area identifier; null when the ZIP Code has none on file. Every Connecticut ZIP Code is null: CMS has not assigned rating areas to the planning regions that replaced Connecticut’s counties in 2022
ssa_state_county_codestringSSA state/county code used in Medicare administrative systems
multi_county_noticestringPresent only when a ZIP spans multiple counties with different Medicare assignments
Medicare Object
{
  "medicare": {
    "cbsa_code": "31084",
    "cbsa_name": "Los Angeles-Long Beach-Glendale, CA",
    "cbsa_type": "Metro",
    "rating_area_id": "16",
    "ssa_state_county_code": "05200"
  }
}

Data sources

Every dataset the API answers from: the authority behind it, the edition we serve where the source publishes editions, how often we take a new release, and a ready-made citation. Cite the edition shown; a dataset updated on a cadence (the USPS files, for example) has no edition. The same list is served free, without a key, by the MCP server's about_data tool.

SourceAuthorityProvidesCadenceEditionCitation
USPS ZIP CodesUSPSZIP Codes, preferred and acceptable city names, counties, classificationsMonthly—USPS, ZIP Codes, via zip-codes.com - https://www.zip-codes.com/
USPS City State fileUSPSpreferred city name per ZIP Code, acceptable alternates, mailabilityMonthly—USPS, City State file, via zip-codes.com - https://www.zip-codes.com/
USPS ZIP+4USPSZIP+4 records: street and unit ranges, record types, carrier routesMonthly—USPS, ZIP+4, via zip-codes.com - https://www.zip-codes.com/
USPS Delivery StatisticsUSPSactive and possible delivery points and carrier routes, by ZIP Code and routeMonthly—USPS, Delivery Statistics, via zip-codes.com - https://www.zip-codes.com/
Postal CodesCanada PostCanadian postal codes and FSAs, cities, provinces, locationsMonthly—Canada Post, Postal Codes, via zip-codes.com - https://www.zip-codes.com/
US ZIP Code Boundarieszip-codes.comZIP Code polygons and centroidsQuarterly—zip-codes.com, US ZIP Code Boundaries - https://www.zip-codes.com/
US Carrier Route Boundarieszip-codes.comUSPS carrier route polygonsQuarterly—zip-codes.com, US Carrier Route Boundaries - https://www.zip-codes.com/
TIGER/Line ShapefilesU.S. Census Bureaucounties, metro areas, places, tracts, block groups, congressional and state legislative districts: names, codes, land and water area, and their overlap with each ZIP CodeYearly2026U.S. Census Bureau, TIGER/Line Shapefiles 2026, via zip-codes.com - https://www.zip-codes.com/
ZIP Code Tabulation AreasU.S. Census Bureauthe official land and water area behind zip_info.land_area_sq_miEvery ten years, with each US census2020U.S. Census Bureau, ZIP Code Tabulation Areas 2020, via zip-codes.com - https://www.zip-codes.com/
Census Demographic and Housing Characteristics (DHC)U.S. Census Bureaupopulation, sex, race and Hispanic origin, median age, households and persons per household (base /zip fields and the demographics include)Every ten years, with each US census2020U.S. Census Bureau, Census Demographic and Housing Characteristics (DHC) 2020, via zip-codes.com - https://www.zip-codes.com/
American Community Survey 5-Year EstimatesU.S. Census Bureaudemographic, social, economic and housing estimates for ZIP Code areas and tracts, and the median household income and average home value in the demographics includeYearly, usually in January2020-2024U.S. Census Bureau, American Community Survey 5-Year Estimates 2020-2024, via zip-codes.com - https://www.zip-codes.com/
County Business PatternsU.S. Census Bureaubusiness establishments, employees and payroll by ZIP Code (demographics.business)Yearly—U.S. Census Bureau, County Business Patterns, via zip-codes.com - https://www.zip-codes.com/
ANSI codesU.S. Census Bureaustate, county and place codesWhen the source publishes—U.S. Census Bureau, ANSI codes, via zip-codes.com - https://www.zip-codes.com/
Metropolitan and Micropolitan Statistical Area DelineationsU.S. Office of Management and Budgetwhich metro and micro areas exist, their type and their countiesWhen the source publishesBulletin 23-01U.S. Office of Management and Budget, Metropolitan and Micropolitan Statistical Area Delineations Bulletin 23-01, via zip-codes.com - https://www.zip-codes.com/
Medicare Labor Market AreasCenters for Medicare & Medicaid ServicesMedicare CBSA codes (a different geography from the OMB metro areas), ACA rating areas and SSA state and county codesYearly—Centers for Medicare & Medicaid Services, Medicare Labor Market Areas, via zip-codes.com - https://www.zip-codes.com/
ElevationU.S. Geological SurveyZIP Code centroid elevationWhen the source publishes—U.S. Geological Survey, Elevation, via zip-codes.com - https://www.zip-codes.com/
Area codesNorth American Numbering Plan Administration (NANPA)telephone area codes per ZIP CodeQuarterly—North American Numbering Plan Administration (NANPA), Area codes, via zip-codes.com - https://www.zip-codes.com/
EDGE School District BoundariesNational Center for Education Statistics (NCES)unified, elementary and secondary school districtsYearlySY 2024-25National Center for Education Statistics (NCES), EDGE School District Boundaries SY 2024-25, via zip-codes.com - https://www.zip-codes.com/
Time zone boundariestimezone-boundary-buildertime zone polygons, with the IANA tz database's rules and namesWhen the source publishes—timezone-boundary-builder, Time zone boundaries, via zip-codes.com - https://www.zip-codes.com/
Boundary FilesStatistics Canadastatistical areas: Canadian census divisions, subdivisions, tracts, metropolitan areas, federal electoral districts and FSA polygonsEvery five years, with each Canadian census2021 CensusStatistics Canada, Boundary Files 2021 Census, via zip-codes.com - https://www.zip-codes.com/
Census of PopulationStatistics CanadaFSA population and dwelling counts (fsa_population, fsa_dwellings, fsa_dwellings_occupied)Every five years, with each Canadian census2021Statistics Canada, Census of Population 2021, via zip-codes.com - https://www.zip-codes.com/

Spatial layers: full provenance

Every spatial block in a response (each census, congressional_district and state_legislative period, school_district, timezone) states source, product and attribution. Show attribution wherever you display the data. The full provenance of each source is below; it is generated from the data files themselves.

SourceLicenseAttributionRefresh
U.S. Census Bureau
TIGER/Line Shapefiles
Public domain (U.S. Government work)Source: U.S. Census Bureau, TIGER/Line Shapefiles 2026 (newest; each vintage names its own edition)yearly (new TIGER/Line each autumn)
Vintages held: 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022, 2023, 2024, 2025, 2026
National Center for Education Statistics (NCES)
EDGE School District Boundaries
Public domain (U.S. Government work)Source: U.S. Department of Education, National Center for Education Statistics (NCES), EDGE School District Boundaries, SY 2024-25 (TIGER 2025) (newest; each vintage names its own edition)yearly (NCES releases each school year's districts on the matching TIGER/Line year)
Vintages held: SY1516-TL16, SY1516-TL17, SY1718-TL18, SY1819-TL19, SY1920-TL20, SY2021-TL21, SY2122-TL22, SY2223-TL23, SY2324-TL24, SY2425-TL25
Statistics Canada
Boundary Files (catalogue 92-160-X): every census geographic level, digital (DBF) and cartographic (CBF) boundary files, English Shapefile editions
Statistics Canada Open Licence (https://www.statcan.gc.ca/en/terms-conditions/open-licence)Source: Statistics Canada, Boundary Files, 2021 Census. Reproduced and distributed on an "as is" basis with the permission of Statistics Canada. (newest; each vintage names its own edition)every 5 years with the census (boundary files ~November of the census year, more levels in the following months); never revised in place except by notice
Vintages held: 2011, 2016, 2021
timezone-boundary-builder
Time zone boundaries (with oceans)
Open Data Commons Open Database License (ODbL) 1.0; IANA tz database: public domainTime zone boundaries © OpenStreetMap contributors, timezone-boundary-builder (ODbL); time zone rules: IANA tz databasewith each timezone-boundary-builder release (it follows the IANA tzdata releases)
Vintages held: 2016j, 2017c, 2018g, 2019b, 2020d, 2022g, 2023d, 2024b, 2025b, 2026d
ZIP-Codes.com
Carrier Route Boundary Data
Licensed data — ZIP-Codes.comSource: ZIP-Codes.comquarterly
Vintages held: 2026-08
ZIP-Codes.com
ZIP Code Boundary Data
Licensed data — ZIP-Codes.comSource: ZIP-Codes.comquarterly
Vintages held: 2026-08

Credit Costs

How Credits Work

Lookup calls use credits from your plan; health and demo calls are not billed. Simple lookups cost 1. Adding enrichments (extra data like demographics or timezone) adds to the cost. The radius endpoint scales with search distance. This section breaks down exactly what each call costs — no surprises.

EndpointBase Cost+ EnrichmentsPlain English
Address1 per request+ delivered coordinates and boundary/tract tags1 base; no match costs 1
Quick ZIP1 (ZIP/FSA/Postal), 2 (ZIP+4)× (1 + timezone + reverse_geocode)ZIP+4 + timezone = 4; coordinates to ZIP+4 + timezone = 6
ZIP Details1 (all code types)+ billable flags + reverse_geocodeZIP+4 + timezone = 2
Distance1 per pairtimezone freeAlways 1 credit
Suggest1–5 (by limit)N/A1 credit for ≤15 results
Radius1–5 (by distance)× (1 + flags + auto_radius)Distinct timezone/ACS requests, including historical years (the current year's suffix and the unsuffixed flag count once)
BatchPer-item costSame as singleSum of individual items
Health0N/AAlways free
Address Matching

1 base + delivered coordinates + each delivered boundary flag + each delivered tract tag. No match costs 1; coordinates and boundary/tract flags that are not returned are not charged.

Quick ZIP & ZIP Details

Quick ZIP credits = base × (1 + timezone + reverse_geocode). Base is 2 for ZIP+4 and 1 for ZIP, FSA, or Postal. Coordinates use the resolved type and add a reverse-geocode layer. ZIP+4 with timezone costs 4; coordinates resolving to ZIP+4 cost 4, or 6 with timezone. Unknown flags have no effect. ZIP Details credits = 1 + billable flags + reverse_geocode. Base is 1 for every code type, including ZIP+4 and Postal. Canadian unavailable flags cost 0 with a notice.

Examples: quick-zip ZIP+4 with timezone = 4. Full /zip ZIP+4 with timezone + demographics = 3.

Radius Search

Base cost scales with distance (banded pricing). Each enrichment multiplies the base rather than adding to it. Auto-radius counts as one additional data layer.

Examples: 25mi with no flags = 1. 25mi with timezone = 2. 200mi centroid with 4 ACS flags = 10.

Distance

Fixed 1 credit per pair. Timezone data is always included at no extra cost. Mix any input types freely.

Suggest

Cost scales with the requested limit: 1–15 results = 1 credit, 16–50 = 2, 51–150 = 3, 151–500 = 5. Billed on requested limit, not results returned.

Radius Credit Costs

Formula: base × (1 + enrichment_count + auto_radius). Base bands differ by mode.

Centroid Mode (default) — ceil(radius / 100), max 500mi
BandRadius RangeBaseNo Flags+ 1 flag+ 2 flags
11–100 mi1123
2101–200 mi2246
3201–300 mi3369
4301–400 mi44812
5401–500 mi551015
Spatial Mode — ceil(radius / 50), max 250mi
BandRadius RangeBaseNo Flags+ 1 flag+ 2 flags
11–50 mi1123
251–100 mi2246
3101–150 mi3369
4151–200 mi44812
5201–250 mi551015

Band boundaries are inclusive at the top: max=50 = band 1, max=51 = band 2. Decimal values are accepted: max=50.5 = band 2.

Formula Examples

  • 25mi centroid, no flags → ceil(25/100) × (1 + 0) = 1 credit
  • 25mi centroid, timezone → 1 × (1 + 1) = 2 credits
  • 100mi centroid, acs_demographic + timezone → 1 × (1 + 2) = 3 credits
  • 200mi centroid, 4 ACS flags → 2 × (1 + 4) = 10 credits
  • 500mi centroid, 4 ACS + timezone → 5 × (1 + 5) = 30 credits
  • 50mi spatial, no flags → ceil(50/50) × (1 + 0) = 1 credit
  • 100mi spatial, 2 ACS flags → 2 × (1 + 2) = 6 credits
  • 250mi spatial, 4 ACS + timezone → 5 × (1 + 5) = 30 credits

Credit Estimator

Select an endpoint and configure options to estimate credits assuming the selected enrichments are delivered. Actual usage appears in meta.credits.

Your Estimate
Base credit1

Total per call1 credit
calls/day = 100 credits/day

Free accounts receive 2,500 daily credits. Subscribers receive their monthly allowance and no daily free credits.

Subscription Plans

PlanMonthly CreditsPrice
Free2,500 / dayFree
Developer100,000$49/mo
Professional350,000$149/mo
Business1,500,000$499/mo

Credit Packs

Pre-purchase credits that never expire. Works alongside any plan — free or paid. If you exceed your daily free allowance or monthly subscription, purchased credits kick in automatically.

PackCreditsPricePer 1,000
Starter25,000$19$0.76
Standard65,000$49$0.75
Growth150,000$99$0.66
Pro (best value)300,000$179$0.60
Enterprise2,000,000$799$0.40

Purchased credits are used only after your free or subscription allowance is exhausted. Need a custom volume? Contact us.

Format validation failures are free. If input doesn't match a recognized format (e.g., not a 5-digit ZIP, 9-digit ZIP+4, or Canadian ANA NAN pattern), no credit is charged (HTTP 400). If the format is valid but the code doesn't exist in our database, 1 credit is charged (2 for quick-zip coordinate input). Address no-match costs 1, plus 1 if coordinates was requested.

Error Codes

Every error, including per-item failures, includes a public url for next steps. AUTH_MISSING_KEY, AUTH_INVALID_KEY, AUTH_INACTIVE_KEY, AUTH_INACTIVE_ACCOUNT, AUTH_TEST_KEY_RESTRICTED, AUTH_TEST_KEY_RADIUS_EXCEEDED, AUTH_IP_RESTRICTED, AUTH_ORIGIN_RESTRICTED, and CREDIT_INSUFFICIENT link to your account. AUTH_BATCH_REQUIRES_SUBSCRIPTION, AUTH_FREE_TIER_RADIUS_EXCEEDED, and RATE_LIMIT_MINUTE link to pricing. All VALIDATION_* errors, MISSING_VERSION, UNKNOWN_VERSION, ROUTE_NOT_FOUND, BAD_REQUEST, SPATIAL_MODE_DISABLED_BATCH, and NOT_FOUND_COORDS_NO_BOUNDARY link to documentation. Other errors link to the general API page. The missing-key response for a key sent in the Authorization header without the Bearer scheme links to authentication documentation.

Whole-request failures use {success:false,request?,error:{code,message,retryable?,url,get_started?},meta}; results is omitted. Middleware echoes request:{method,path} and marks 429/503/504 as retryable. Braces in messages are placeholders; validation can return additional parameter-specific messages.

HTTPCodeMessage / variants
401AUTH_MISSING_KEY"API key is required. Send it in the X-Api-Key header or the ?key= query parameter. Try the API now with the demo key in error.get_started, see prices and credit packs at https://api.zip-codes.com/pricing.json, or get a key at https://www.zip-codes.com/api/account."
401AUTH_MISSING_KEY (key in Authorization without Bearer)"API key detected in the Authorization header without the Bearer scheme. Send it as Authorization: Bearer zc_... or in the X-Api-Key header."
401AUTH_INVALID_KEY"Invalid API key format. Keys are 32 characters. Manage your keys at https://www.zip-codes.com/api/account." (<16 chars) / "Invalid API key. Manage your keys at https://www.zip-codes.com/api/account."
403AUTH_INACTIVE_KEY"API key has been deactivated." / "API key has expired."
403AUTH_INACTIVE_ACCOUNT"Account has been suspended. Visit https://www.zip-codes.com/api/account for assistance."
403AUTH_IP_RESTRICTED / AUTH_ORIGIN_RESTRICTED"Access denied: IP address not in allowlist." / "Access denied: origin domain not in allowlist."
403AUTH_IP_BLOCKED"Too many invalid authentication attempts. Try again later."
403AUTH_BATCH_REQUIRES_SUBSCRIPTION"Batch endpoints need a subscription or purchased credits. Buy a pack with POST https://api.zip-codes.com/v2/purchase (see https://api.zip-codes.com/pricing.json) or subscribe at https://www.zip-codes.com/api/pricing."
403AUTH_FREE_TIER_RADIUS_EXCEEDED"Free tier is limited to {n}-mile radius in {mode} mode. Upgrade at https://www.zip-codes.com/api/pricing."
402CREDIT_INSUFFICIENT"Insufficient credits. This request costs {n} credits but only {m} available. Buy credits at https://www.zip-codes.com/api/account." (batch: "Insufficient credits. This batch costs ~{n} credits but only {m} available. Buy credits at https://www.zip-codes.com/api/account.")
429RATE_LIMIT_MINUTE"Rate limit exceeded. Please retry after 60 seconds." + meta.rate_limit {limit, window:"minute", retry_after:60}
429RATE_LIMIT_MINUTE (unauthenticated flood)"Too many unauthenticated requests. Retry after 60 seconds."
400VALIDATION_MISSING_PARAM"Missing required parameter: {p}" (alias variant adds ". Found '{alias}' in query — use '{p}' instead.")
400VALIDATION_INVALID_PARAM"Parameter '{p}' cannot be empty."
400VALIDATION_INVALID_ZIP / VALIDATION_INVALID_FSA / VALIDATION_INVALID_POSTAL"Invalid postal code format: '{raw}'."
400VALIDATION_INVALID_COORDINATES"Coordinates out of range: lat must be -90..90, lon must be -180..180."
400VALIDATION_INVALID_REQUEST"Malformed or unreadable request. Check that the request body is valid JSON and all fields have the correct types."
400MISSING_VERSION"API version required. Use /v2/ prefix." (a path outside /v2)
400UNKNOWN_VERSION"Unknown API version. Use /v2/ prefix." (/v3 and later)
404ROUTE_NOT_FOUNDNo /v2 route matches this path. Then the call it most likely meant, when the path can mean only one: Use GET /v2/zip?code=10977&include=census (code takes a ZIP, ZIP+4, FSA, postal code or lat,lon coordinates; values go in the query string, not the path). Every route: https://api.zip-codes.com/llms.txt. Otherwise the public routes, each with its query (GET|POST /v2/zip?code={code}) or its batch body key (POST /v2/zip/batch {"codes": [...]}). A /v2 path no route matches (/v2/zip/10977, /v2/zipcode/90210). Only a value that parses as a code or coordinates is repeated back; a number whose meaning the path does not settle, an address or search text stay placeholders ({code}, {miles}, {address}, {text}). Never run, never charged.
500INTERNAL_ERROR"An unexpected error occurred. Please try again later."
403AUTH_TEST_KEY_RESTRICTEDTest API keys are restricted to demo codes only. Code '{code}' is not a demo code. (Demo help suffix appended.)
403AUTH_TEST_KEY_RADIUS_EXCEEDEDTest API keys are limited to a {n}-mile radius. Requested: {r} miles. (Auto variant: Auto-resolved: {r} miles.)
400VALIDATION_BATCH_TOO_LARGEBatch size {n} exceeds maximum of {max}
400VALIDATION_INPUT_TOO_LONGAddress input exceeds maximum length of 500 characters. / Address input exceeds maximum length of 300 characters after cleanup.
400VALIDATION_INPUT_TOO_SHORTAddress input is too short to parse (minimum 9 characters after cleanup).
400NOT_FOUND_COORDS_NO_BOUNDARY (auto-radius)No ZIP or FSA boundary found for these coordinates. Auto radius requires coordinates within a known boundary.
400SPATIAL_MODE_DISABLED_BATCHSpatial mode is not available for batch radius searches. Use mode=centroid (default) or make individual /v2/radius requests with mode=spatial.
503SERVICE_POOL_EXHAUSTEDService temporarily at capacity. Please retry in a few seconds. (Retry-After: 5)
503SERVICE_WARMING_UPRadius search is starting up: the {centroid|spatial} engine is loading after a restart. Retry after {n} seconds. / Suggest is starting up: the suggest index is loading after a restart. Retry after {n} seconds. (/v2/radius, /v2/radius/batch and /v2/suggest only, right after a server start or restart, usually under a minute; Retry-After: the expected seconds until ready, default 30; retryable; no credits charged; no partial or fallback answer.)
503SERVICE_UNAVAILABLEService temporarily unavailable (default configurable maintenance message; Retry-After: 300). Address variant: Address matching service became unavailable during request.
504SERVICE_TIMEOUTThe request timed out. Please try again or reduce the search radius.
405METHOD_NOT_ALLOWED{method} is not supported for {route}. It takes {methods}. (A batch route adds: with a JSON body {"codes": [...]}. For one item, use GET /v2/zip?code={code}.) Every route: https://api.zip-codes.com/llms.txt. Allow header: the methods.
413REQUEST_TOO_LARGERequest body exceeds the maximum allowed size.
415UNSUPPORTED_MEDIA_TYPEContent-Type must be application/json for this endpoint. (A body that is not valid JSON returns 400.)

Test-key restriction messages append this suffix: Demo US ZIPs: 90210, 10001, 10118, 32504, 00601, 96950, 33139, 60601, 98101, 30301, 02872, 06103. Demo CA: M5V, K1A, V6B, T2P, H3A, M1R0E9, V5K0A1, K1A0A1, T1X0L3, H1A0A1. Address restrictions read Test API keys are restricted to demo addresses only. Use a real API key to look up any address. followed by a suffix naming demo addresses. The full demo address list (matched ignoring case, commas, periods and extra spaces) is in llms-full.txt; every address example on this page is on it.

Address service-not-ready messages are Address matching service is not ready. Binary files are still loading, please retry shortly. or Address matching service is not ready. Address matching is not enabled on this server.

401 responses include WWW-Authenticate: ApiKey realm="zip-codes-api". Missing-key, invalid-key, and insufficient-credit messages also include the account link in their text. VALIDATION_UNKNOWN_ENRICHMENT is a notice on HTTP 200, not an error.

Per-Item Not Found (HTTP 200)

CodeMessage
NOT_FOUND_ZIPZIP code {zip} not found.
NOT_FOUND_FSAFSA {fsa} not found.
NOT_FOUND_POSTALPostal code {postal} not found.
NOT_FOUND_COORDS_NO_BOUNDARYNo ZIP or postal code boundary found for coordinates {lat},{lon}.
NOT_FOUND_FROM / NOT_FOUND_TOCode '{input}' not found in any boundary table
INSUFFICIENT_INPUTNot enough information to locate the address. Include a city and state, or a ZIP code.
AMBIGUOUSMore than one address matches. Add detail such as a unit number, directional or ZIP code.
NOT_FOUNDThe address could not be matched.

quick-zip coordinate variant: No ZIP or FSA boundary found for coordinates {coordinates}. /zip can also report Coordinates {lat},{lon} landed in FSA {fsa} but no postal code could be resolved. Use /quick-zip for FSA-level lookups.

Postal lookup envelopes keep success:true with {success:false,input:{code},error:{code,message,url}} in results. Codes: NOT_FOUND_ZIP, NOT_FOUND_FSA, NOT_FOUND_POSTAL, NOT_FOUND_COORDS_NO_BOUNDARY, and distance NOT_FOUND_FROM/NOT_FOUND_TO. ZIP: ZIP code {zip} not found.; quick-zip batch: Code {code} not found.; FSA: FSA {fsa} not found.; radius: Center code '{code}' not found.; distance: Code '{input}' not found in any boundary table. Unknown ZIP+4 with a known parent ZIP succeeds with detail:{zip4,valid:false}.

See Address Matching for INSUFFICIENT_INPUT, AMBIGUOUS, and NOT_FOUND, also HTTP 200. Address batch validation messages: Address exceeds maximum length of 500 characters., Address exceeds maximum length of 300 characters after cleanup., Address too short to parse (minimum 9 characters).; item INTERNAL_ERROR: Address matching failed for this item.

Error Response Format

401 Response
{
  "success": false,
  "request": {
    "method": "GET",
    "path": "/v2/quick-zip"
  },
  "error": {
    "code": "AUTH_MISSING_KEY",
    "message": "API key is required. Send it in the X-Api-Key header or the ?key= query parameter. Try the API now with the demo key in error.get_started, see prices and credit packs at https://api.zip-codes.com/pricing.json, or get a key at https://www.zip-codes.com/api/account.",
    "retryable": false,
    "url": "https://www.zip-codes.com/api/account",
    "get_started": {
      "demo_key": "zc_test_DEMOAPIKEY000000000000",
      "demo_scope": "the demo ZIPs, postal codes and addresses listed in https://api.zip-codes.com/llms-full.txt",
      "pricing": "https://api.zip-codes.com/pricing.json",
      "llms_txt": "https://api.zip-codes.com/llms.txt",
      "openapi": "https://api.zip-codes.com/v2/openapi.json",
      "signup": "https://www.zip-codes.com/api/account"
    }
  },
  "meta": {
    "request_id": "01M3SX2VHP0M9TXH8WFHW7ZM3G",
    "version": "2.0.0",
    "timing_ms": 0
  }
}

Rate Limiting

Limits use a one-minute sliding window (six 10-second segments). There are no hourly or daily request limits; daily free credits are a separate allowance.

Allowed authenticated API requests carry X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset (Unix timestamp, now + 60 seconds), and X-RateLimit-Window: minute. These headers are absent on health, docs, swagger, OPTIONS and auth-rejected requests. A 429 adds Retry-After: 60. X-Request-Id is a ULID; X-Api-Version is 2.0.

Limits by Rate Tier

TierRequests per minute
Free (default)60
Restricted20
Developer300
Professional300
Business600
Enterprise1800
Demo (per IP)5
Prepaid (a free account holding purchased credits, while they last; a plan keeps its own limit), and a prepaid card holding credits300
Prepaid card with no credits left (until a reload credits it)10
429 Response (Free tier)
{
  "success": false,
  "request": {
    "method": "GET",
    "path": "/v2/quick-zip"
  },
  "error": {
    "code": "RATE_LIMIT_MINUTE",
    "message": "Rate limit exceeded. Please retry after 60 seconds.",
    "retryable": true,
    "url": "https://www.zip-codes.com/api/pricing"
  },
  "meta": {
    "request_id": "01M3SX2VHP0M9TXH8WFHW7ZM3G",
    "version": "2.0.0",
    "timing_ms": 0,
    "rate_limit": {
      "limit": 60,
      "window": "minute",
      "retry_after": 60
    }
  }
}

Buying Credits with a Wallet (MPP)

This API accepts payment programmatically. Anyone with an MPP wallet (a shared payment token paid through Stripe — card or Link), an agent or a person, can buy credits and keep working with no signup, no account and no person on our side. The wallet authorizes each payment (Link, for example, asks its human to approve every payment in the Link app). The scheme is the Machine Payments Protocol (MPP, draft-httpauth-payment): the API answers 402 with a WWW-Authenticate: Payment challenge, you pay it with your wallet, and you send the same request again with the credential in a Payment-Authorization header. A successful answer carries a Payment-Receipt header. Credits are added only after the payment completes (a card clears at once; some methods take longer).

Two moments raise a challenge:

  • Out of credits. A data request that runs out of credits answers 402 CREDIT_INSUFFICIENT; when one pack would make that request succeed, it carries the offer (error.payment: the smallest such pack, its price and the payment methods) and the challenge. Pay it and send the request again: the pack is credited and the request answered.
  • Buying on purpose. POST /v2/purchase sent without a Payment-Authorization header answers 402 PAYMENT_REQUIRED with the challenge.

Three endpoints, each with its parameters, response fields, examples and errors in the endpoint reference:

  • POST /v2/purchase buys one pack (with a key, for that key's account or prepaid card; without one, a new prepaid card).
  • GET /v2/balance reads the credit pools of this key's account or prepaid card.
  • GET /v2/purchases/{id} reads one purchase by its order number; use it to follow a 202 until status reads succeeded.

Prepaid cards. Sent without any API key, POST /v2/purchase makes a prepaid card: an API key with no account that holds the pack's credits and works on every endpoint until they run out (it has no free daily credits, and runs at 10 requests a minute while empty). Its key is in results[0].api_key. A purchase sent with that key reloads the card. Keep the key: the same payment sent again returns it again only while the card is unused, and for at most 24 hours after the payment settles. From zero, go straight to POST /v2/purchase: a data request without a key answers 401 AUTH_MISSING_KEY, not a purchase offer.

Limits and keys

  • GET /v2/purchases/{id} and GET /v2/balance take the key in a header (X-Api-Key or Authorization: Bearer), never ?key= (401). They share a limit of 10 a minute per key; after 20 unknown purchase ids in an hour, purchase lookups answer 429 until the hour ends.
  • POST /v2/purchase: 5 payment attempts a minute per key, 20 per IP.
  • Test, demo and admin keys cannot read a purchase or a balance (403 AUTH_TEST_KEY_RESTRICTED); test and demo keys cannot buy and get no payment offers.

A pack bought with a key is added to that key's account or prepaid card. The names, credits and prices of the agent packs are live at pricing.json. Paying accepts the Terms of Service.

Supported Countries

CountryCode TypesCensus BoundariesACS DataPolitical Districts
United States ZIP (5-digit), ZIP+4 (9-digit) Full (county, tract, block group, place, CBSA) Full (DP02–DP05) Congressional, State Senate, State House
Canada FSA (3-char), Postal (6-char) Statistics Canada census geographies (FSA overlaps or the areas containing a postal code) Not available Federal electoral districts inside statcan

HTTP Status Codes

Summary of all HTTP status codes the API can return:

CodeMeaningWhen
200OKProcessed lookup, including not-found. Check result errors; single address no-match has success:false. Batch item failures also return 200.
400Bad RequestMissing or invalid parameters, malformed JSON body, empty batch
401UnauthorizedMissing or invalid API key
402Payment RequiredInsufficient credits or daily limit reached
403ForbiddenInactive key/account, allowlist restriction, or plan/demo restriction
405Method Not AllowedWrong HTTP method (e.g., PUT on a GET/POST endpoint)
413Payload Too LargeRequest body exceeds the maximum allowed size
415Unsupported Media TypeUnsupported media type; /v2 POST requests are normalized to JSON
429Too Many RequestsRate limit exceeded. Check Retry-After header.
500Internal Server ErrorUnexpected server error. Include X-Request-Id when reporting.
504Gateway TimeoutRequest timed out
503Service UnavailableServer maintenance or temporary overload; on /v2/radius, /v2/radius/batch and /v2/suggest, also while the radius engine or suggest index loads after a restart (SERVICE_WARMING_UP, not charged). Retry after the Retry-After header's seconds.

Versioning

The API uses path-based versioning. All current endpoints are under /v2/. The version is also returned in API response metadata as meta.version.

  • Backward-compatible changes (new fields, new enrichments, new endpoints) are added without a version bump.
  • Breaking changes (removed fields, changed response structure) will only occur in a new version (e.g., /v3/).
  • Nothing is removed from v2. An include flag, year, field or endpoint that is retired keeps working: it is still accepted and answered with an ENRICHMENT_FLAG_RETIRED notice (with its data while it exists, otherwise not charged).
  • The current version (v2) is stable and actively maintained. There are no plans to deprecate it.
  • If a future version is released, v2 will continue to operate for a minimum of 12 months after deprecation notice.
  • See our Service Level Agreement and Terms of Service for uptime and usage commitments.

OpenAPI Specification

A machine-readable OpenAPI 3.0 specification is auto-generated from the API and always reflects the current endpoints, parameters, and response schemas.

ResourceURLUse For
Postman Workspace /postman One-click fork in Postman — live, versioned collection
Swagger UI /swagger Interactive exploration and testing
OpenAPI JSON /swagger/v2/swagger.json Generate SDKs, import into Insomnia/Bruno/AI coding tools
Download Spec /v2/openapi.json Download the OpenAPI spec file directly
Using Postman? Click Open in Postman to land directly in our live workspace — fork the collection into your own workspace in one click, with every endpoint, parameter, and example pre-configured. If you use a different tool (Insomnia, Bruno, an AI coding assistant), import the OpenAPI spec URL above instead.

Changelog

Notable API changes, data updates, and new features.

November 3, 2026

  • Tract ACS for every year, 2011–2024: /v2/address and its batch accept acs_tract_{profile}_{YYYY} for every ACS release from 2011 (was 2022–2024), in the same per-year block under data.acs.tract and at the same price, one credit per profile and year delivered. Each year’s tract comes from that year’s Census tracts, so an address’s geo_id can differ between years.
  • Private mailboxes printed as PMB n: a unit at a commercial mail receiving agency (a mail drop such as a UPS Store) that does not confirm is read as the renter’s private mailbox, as USPS reads it, and printed as PMB n, with a new UNIT_READ_AS_PMB notice. The ZIP+4, street and house number are unchanged.
  • Address notices: MILITARY_ADDRESS now says where USPS puts the parts of an APO/FPO/DPO address (street holds the unit, number the box); HOUSE_NUMBER_CORRECTED is now sent when a house number is read differently from how it was written; ZIP4_WITHHELD on a phantom carrier route says why.
  • A wrong /v2 route names the right call: a /v2 path that matches no route answers 404 ROUTE_NOT_FOUND (was 400 MISSING_VERSION) with the call it most likely meant, such as GET /v2/zip?code=10977&include=census for /v2/zip/10977?include=census. A route asked with the wrong method answers 405 METHOD_NOT_ALLOWED with an Allow header. Neither is charged. See Error codes.
  • Faster ACS: tract ACS on /v2/address and ACS on /v2/zip are built faster and with less memory. Responses are unchanged.

November 1, 2026

  • Buying credits through the API: POST /v2/purchase buys a credit pack with an MPP wallet (a shared payment token paid through Stripe), and an out-of-credits 402 CREDIT_INSUFFICIENT can carry a payment offer to pay and retry. Sent without an API key, a purchase makes a prepaid API key holding the pack’s credits. GET /v2/balance reads your credit pools and GET /v2/purchases/{id} reads one purchase. Purchased credits never expire. The account owner can switch API purchases off on the dashboard’s Settings page. Purchases through the API may answer 503 “paused” with Retry-After while they are switched off on our side.
  • Batch with purchased credits: the batch routes now answer for an account holding purchased credits, not only for a subscriber. A free account holding purchased credits gets 300 requests a minute (was 60) while they last.
  • API key as Authorization: Bearer: every route accepts the key as a Bearer token as well as X-Api-Key (recommended).
  • The payment credential in Authorization: Payment (2.3.1): the paid credential is read from Authorization: Payment …, the MPP default, as well as from Payment-Authorization. The credential's format is shown under POST /v2/purchase.
  • TIGER/Line 2026 is current: bare census, cd and state_leg answer from the 2026 Census boundaries under current; 2025 is an earlier year, under its "2025" key (census_2025), and every year from 2010 stays available.
  • MCP: pages on www.zip-codes.com and api.zip-codes.com can call the MCP server from a browser (CORS); other sites cannot. MCP clients are unaffected.
  • Documentation: the Data sources section lists every dataset the API answers from, and the address notice lists name NO_HOUSE_NUMBER and ZIP4_WITHHELD.

October 19, 2026

  • Metro areas are lists (response shape change): in every census period on /v2/zip, /v2/address and their batches, metro_micro_cbsa_statistical_area and metropolitan_cbsa_division are now arrays of every area the ZIP Code overlaps, largest pct_of_zip first (each was a single object, so a ZIP Code in two metro areas lost the second). Read [0] where you read the object.
  • Address coordinates charged only when returned: on /v2/address, coordinates costs a credit only when a location is returned.
  • ZIP+4 street detail: on /v2/quick-zip and /v2/zip, detail.street[] now sends what USPS publishes. A blank house or unit number is null (it was "0", giving "350 5TH AVE, 0" and "0 GENERAL DELIVERY"); name is uppercase like the rest of the address (5TH, not 5Th); unit numbers such as K01 keep their zeros. record_type H reads Highrise (it read “Highway Contract”) and F reads Firm. Where several USPS records cover one ZIP+4, the same one is always returned.
  • Land and water area: /v2/zip zip_info.land_area_sq_mi and water_area_sq_mi now come from the Census ZIP Code Tabulation Area (land area was the boundary polygon’s, water included), and are null for a ZIP Code with no ZCTA of its own.
  • No placeholder values: ZIP Codes stored without a location (military and a few PO Box and unique ones) return location, area_codes and elevation as null instead of 0,0, ["000"] and 0; medicare.rating_area_id "0" (none) is null.
  • Congressional districts carry their own state on /v2/zip and /v2/address: a ZIP crossing a state line no longer shows the other state’s district under its own state.
  • Radius: /v2/radius ACS statistics cover every match in the radius whatever the limit, and each field’s pct follows what Census prints in its percent column (shares use Census’s own totals). Spatial mode no longer lists codes that only graze the circle (pct_inside under 0.01). County names are spelled as on /v2/zip (DeKalb, not Dekalb), on /v2/distance too.
  • Suggest: /v2/suggest finds every ZIP Code, PO Box and military ones included; metro areas carry their two-letter state; places, counties and metro areas follow the 2025 Census boundaries, with location at Census’s internal point.
  • Documentation: the OpenAPI spec describes nearly every response field, among them the /v2/address fields, confidence levels and codes; the address input guidance asks for one line in mailing order, with any unit before the city.

October 17, 2026

  • MCP server: the API’s data as Model Context Protocol tools for AI agents at https://mcp.zip-codes.com/mcp (Claude, Cursor, VS Code and other MCP clients). Without a key every tool answers in demo mode (real values, deeper fields withheld, not charged); with your key in X-Api-Key answers are complete and a tool call costs what its endpoint costs. Connect instructions and the tool list: mcp.zip-codes.com.
  • Boundary history: on /v2/zip, /v2/address and their batches, add a year to census, cd or state_leg (for example census_2018, cd_2012, state_leg_2020; 2010–2024) to get that year’s boundaries under its own "YYYY" key beside current. The bare flags return each family’s latest year; asking for the latest year both ways (census and census_2025) returns one block, charged once. A year we do not hold is ignored with VALIDATION_UNKNOWN_ENRICHMENT at no charge.
  • Full-resolution Census boundaries: census, congressional and state legislative boundaries now come from the U.S. Census Bureau’s full-resolution TIGER/Line files instead of the simplified cartographic files, and current moves from 2024 to 2025. Overlap percentages shift, most visibly along coasts, and tiny slivers no longer appear. pct_of_area now measures land only: the share of the area’s land, as covered by ZIP boundaries. source reads "U.S. Census Bureau" (was "Census Bureau TIGER/Line Cartographic Boundaries").
  • Shared area: /v2/zip boundary rows gain shared_area_sq_mi. /v2/address boundary rows now carry pct_of_zip, pct_of_area and shared_area_sq_mi as null (an address is a point), so both endpoints return the same shape.
  • ZIPs without a boundary polygon: about 7,600 ZIPs that have a location but no boundary polygon (for example 06101) now return boundaries on /v2/zip, answered from their point, with the overlap fields null. They previously returned none.
  • 2022 metro areas: Census published no CBSA or metropolitan division boundaries for 2022, so census_2022 returns the 2021 TIGER/Line delineation (OMB Bulletin 20-01, the one Census’s 2022 data products use) for both.
  • Missing boundary data: a requested family or year whose data is not available returns ENRICHMENT_UNAVAILABLE and is not charged. If a single layer within a delivered year is missing, its keys are null and a DATA_UNAVAILABLE notice names it.
  • Time zone for an address: /v2/address and its batch accept timezone and return the same block as /v2/zip, with pct_of_zip null. One credit, charged only when returned.
  • Quick ZIP and Distance timezone gain attribution: /v2/quick-zip, /v2/distance and their batches keep timezone as one flat object with every field it had, and add product and attribution; source now names the publisher ("timezone-boundary-builder"), as on /v2/zip. The zone comes from the time zone boundary file: for a ZIP crossing a zone line, the zone covering most of it. Credits are unchanged.
  • Data sources: spatial blocks now carry source, product and attribution; each source's license, product page, vintages and refresh cadence are listed under Spatial layers: full provenance.
  • Radius: /v2/radius spatial mode now returns pct_inside for about 100 ZIPs that returned null, and ACS aggregates weight them by their real share. county_fips now follows the ZIP data vendor, which changes it for about 100 ZIPs, mostly Connecticut planning regions.
  • Fixed: overlap percentages far above 100%, from overlaps that failed to compute, no longer occur.
  • Canadian boundaries from Statistics Canada’s boundary files: on /v2/zip and its batch, a Canadian postal code’s or FSA’s census and cd (federal_electoral_district) now come from Statistics Canada’s Boundary Files (full extent) and census FSA file, with the same row fields. A postal code’s areas are those containing its location. An FSA’s polygon includes its coastal and Great Lakes water, so pct_of_fsa is a share of the whole FSA (shares move in about 470 coastal FSAs). FSAs Statistics Canada has no polygon for (for example M5K) are answered from their location, with pct_of_fsa and pct_of_area null; large rural FSAs that timed out (for example X0A) now return. Rows with shares far above 100% no longer occur.
  • Canadian census rows unified: on /v2/zip, a Canadian postal code’s and an FSA’s census and federal_electoral_district rows now have the same fields: uid, dguid, areas in square miles and km² (area_sq_mi, area_km2, land_area_sq_mi, land_area_km2), pct_of_area, and type descriptions on FSAs. Federal electoral district and province names are in both languages (name bilingual, plus name_en and name_fr; an FSA’s name was English only). Postal codes also return province. No field was removed.
  • Canadian census years: census and cd on a Canadian code return the latest Statistics Canada census (2021); census_2016 / cd_2016 and census_2011 / cd_2011 return that census under its "YYYY" key, one credit each, charged only when returned. A year without a Canadian census (for example census_2024) returns ENRICHMENT_UNAVAILABLE at no charge. Each Canadian period opens with source, product and attribution, then year and vintage (was "source": "Statistics Canada 2021 Census").
  • Canadian geography is now statcan (response shape change): on /v2/zip and its batch, a Canadian postal code’s or FSA’s Statistics Canada geography moves from census to statcan, requested with statcan (latest census, 2021; statcan_2021 is the same), statcan_2016 or statcan_2011, one credit each. Federal electoral districts are now a list inside each period, statcan.current.federal_electoral_district, at no extra charge; the top-level federal_electoral_district family is gone. Periods and rows are unchanged. census, census_YYYY, cd and cd_YYYY on a Canadian code still work: they are answered under statcan with an ENRICHMENT_FLAG_RENAMED notice, and asking for an alias and statcan together returns one block, charged once (so census plus cd is now one credit). statcan on a US code returns ENRICHMENT_UNAVAILABLE at no charge. US census and cd are unchanged.
  • Canadian time zones: come from the same time zone boundaries as US ZIPs, with source, product and attribution. A postal code’s zone has pct_of_zip null (it is a point); an FSA crossing a zone line lists each zone covering at least 1% of it. About 1% of postal codes had a wrong zone (for example Saskatoon returned America/Adak); they are corrected.
  • ACS fields are the same in every year: every ACS field now appears in every year’s response on /v2/zip, /v2/radius and /v2/address. When Census published no item with that meaning in a year, the field is present with column null and every value null, instead of being left out (a 2011 response carried 394 of the 542 fields). Fields that held another item’s value are corrected, among them year_moved_in and year_built bins, households.male_householder.living_alone before 2019, and repeated DP05 rows whose percent could be a count; 74 fields were added for items Census published under no name.
  • Server start-up (503 with Retry-After): right after a server start or restart, while their data is still loading (usually under a minute), /v2/radius, its batch and /v2/suggest answer HTTP 503 SERVICE_WARMING_UP with a Retry-After header, not charged. They previously answered meanwhile from a slower fallback whose matches could differ. Once loaded, answers are unchanged. /v2/health reports ready and lists what is still loading.
  • Credits: under heavy load a funded account no longer gets a false 402 CREDIT_INSUFFICIENT; when the service is genuinely at capacity the answer is HTTP 503 SERVICE_POOL_EXHAUSTED with Retry-After, not charged. A not-found answer on /v2/zip, /v2/quick-zip, /v2/distance or /v2/radius that cannot be paid now returns 402 CREDIT_INSUFFICIENT instead of a 200 saying a credit was charged, and an uncharged one reads "Not found (no credit charged).". The unused part of a batch reservation is now also returned to your free daily credits, on the day it was charged.
  • OpenAPI specification: response schemas are open (a response may gain fields, so clients should ignore fields they do not know) and have permanent names (ZipResponse, ZipBatchItem, AddressResponse, …, replacing generated names such as ZipResultBatchItemApiResponse; the old names stay as deprecated aliases, so SDKs built from them keep working). Fields sent in every response are marked required. Nothing is removed from v2: see Versioning. No response changed.
  • Faster: Canadian /v2/radius spatial searches over large areas (worst cases from 6–8 s to under 0.4 s), repeat /v2/zip calls on Canadian FSAs with statcan, and batches, which now run several items at a time while keeping input order and credits. No response changed.
  • Address matching: /v2/address now matches city-only addresses (no state or ZIP, for example 123 Main St, Boise) when the street identifies one town, with the notice “State 'XX' inferred from the city.”, and handles more edge cases (recipient lines, unit prefixes, wrong ZIPs, alternate street names).

October 4, 2026

  • Boundary families (response shape change): on /v2/zip and /v2/address, congressional and state legislative districts moved out of census.current into their own top-level objects, congressional_district.current and state_legislative.current (with upper and lower), and school districts are now school_district.current. Canadian cd returns federal_electoral_district.current. Each family is keyed so historical periods can be added beside current. A requested family with nothing found returns {"current": null}. Flags and prices are unchanged.
  • Congressional primary basis: congressional_district.current.primary now includes basis. On /v2/zip the primary is the district with the largest share of the ZIP’s area; on /v2/address it is the USPS ZIP+4 congressional district.
  • Autocomplete notices: /v2/suggest now says “Showing results for” only when the results come from a spelling correction, not on correctly spelled words.
  • Agent documentation: a redesigned llms.txt and llms-full.txt, an AGENTS.md with credit-safety notes for AI agents, plus robots.txt and sitemap.xml.
  • Faster ACS enrichments: ACS profiles on /v2/zip, /v2/address and /v2/radius are now served from in-memory Census data files instead of database queries, so requests with many ACS years and profiles return significantly faster. Every historical year on /v2/radius now aggregates as quickly as the current year.
  • ACS values match Census exactly: every ACS value is now taken directly from the Census Bureau’s published files. Open-ended medians return the Census threshold (for example 250000 for “250,000+”), and values Census could not compute return null rather than 0.
  • ACS year availability on /v2/zip: each ACS year block now includes published. When Census published no data for the ZIP’s ZCTA in a requested year (ZCTAs are redrawn every decade), published is false, the profiles are null, and a note says so. Year blocks are returned newest first.

September 2026

  • New address engine: /v2/address now runs on a new matching engine that is faster and more accurate, with the same request and response format.
  • Address enrichments: coordinates, census, congressional and state legislative boundaries, school districts, and tract ACS profiles for 2011–2024, charged only for the boundary and tract data actually delivered.
  • Error next steps: every error includes a public url; FREE_TIER_LIMIT_CAPPED notices link to pricing, and key and credit messages include account links.
  • Reference corrections: batch formats and limits, HTTP 200 not-found behavior, emitted errors, per-minute rate limits, and credit formulas in this reference now match the API.

August 2026

  • Improved address matching: /v2/address now runs on a rebuilt matching engine that resolves more real-world input to a deliverable ZIP+4. Notably better on addresses that were previously hard to place: misspelled or heavily abbreviated street names, missing or incorrect ZIP codes, alternate and historical street names, and apartment or suite designators.
  • Relocated addresses: an address whose delivery point has been moved by the USPS now returns its current ZIP+4 instead of the retired one.
  • No charge for undelivered responses: if a request is cancelled or times out before a response reaches you, no credits are charged.

March 2026

  • New endpoint: /v2/address — Address matching. Parse and validate raw US street addresses against USPS ZIP+4 delivery-point data. Returns standardized address, ZIP+4, parsed components, and delivery metadata. Launched with a 1-credit base; see September 2026 for enrichments.

March 12, 2026 — ZIP Codes API v2 launched

  • Launch: ZIP Codes API v2 went live with ZIP, radius, distance and autocomplete lookups for US and Canadian postal codes, opt-in enrichments, and credit-based pricing.

Frequently Asked Questions

Do you use ZCTA boundaries?
No. Our ZIP code boundaries are a commercial product built from USPS carrier routes and delivery data. Boundaries are updated quarterly and reflect current USPS delivery routes, not Census-derived approximations. ZCTAs (ZIP Code Tabulation Areas) are Census Bureau constructs used only for geographic joins when mapping ACS demographic data to ZIP codes. The boundaries you see in our API are real, current ZIP code boundaries.
How often is the data updated?
Different data layers update on different schedules: US ZIP base data is updated monthly, ZIP boundaries quarterly, ACS census data annually (each December/January release), Canadian postal codes monthly, and school districts yearly. When data is updated, it is reflected in the API without any action needed on your part.
Can I try the API without signing up?
Yes. Our interactive demo let's you see the functionality of the API. Get a free API Key for 2,500 credits/day, no credit card, no committment. Finally, our Interactive Playground lets you work with all functionality of the API with full access to every endpoint and enrichment so you can evaluate the API before committing.
Do you support Canadian postal codes?
Yes. We support both FSA (Forward Sortation Area, 3 characters like M5V) and full postal codes (6 characters like M5V2H1) on postal lookup, radius, distance, and suggest routes; address matching is US-only. Canadian codes work with timezone enrichment, and radius searches automatically include cross-border results. Some enrichments (ACS demographic data, state legislative districts, school districts) are US-only and unavailable for Canadian codes, with a notice and no credit charged; statcan returns Statistics Canada geography and federal electoral districts (census and cd on a Canadian code are answered as statcan).
What is the difference between Quick ZIP and ZIP Details?
Quick ZIP (/v2/quick-zip) is a lightweight lookup returning core postal code info (city, state, coordinates, code type) with only timezone as an optional enrichment. It is optimized for fast, high-volume lookups like form validation or address autocomplete. ZIP Details (/v2/zip) returns comprehensive data including demographics, business statistics, delivery points, and multiple enrichments (census boundaries, ACS data, congressional districts, school districts, and more).
How are credit costs calculated?
Quick ZIP credits = base × (1 + timezone + reverse_geocode). Base is 2 for ZIP+4 and 1 for ZIP, FSA, or Postal. Coordinates use the resolved type and add a reverse-geocode layer. ZIP+4 with timezone costs 4; coordinates resolving to ZIP+4 cost 4, or 6 with timezone. Unknown flags have no effect. ZIP Details credits = 1 + billable flags + reverse_geocode. Base is 1 for every code type, including ZIP+4 and Postal. Canadian unavailable flags cost 0 with a notice. Address: 1 + coordinates + delivered boundary/tract tags. Radius: distance-band base × (1 + flags + auto_radius). Distance: 1 per pair. Suggest limit bands: 1/2/3/5 credits. See Credit Costs.
What happens if I exceed my rate limit?
You will receive an HTTP 429 response with a Retry-After header indicating how many seconds to wait. Rate limits are enforced on a per-minute window and vary by subscription tier. Rate limit headers (X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset) are present on allowed authenticated API responses; health, docs, swagger, OPTIONS and auth-rejected requests do not carry them.
Can I do reverse geocoding with coordinates?
Yes. Pass coordinates as lat,lon in the code parameter (e.g., code=34.1030,-118.4105) on any lookup endpoint. The API uses a two-step spatial search: first, it identifies which ZIP or FSA boundary polygon contains the coordinate (not just the nearest centroid — a point in rural Montana will correctly resolve to the ZIP that actually covers that location, not a distant one). Then, it finds the closest ZIP+4 or full postal code centroid within that parent boundary for precise sub-code resolution. Coordinates add one data layer on quick-zip (× its resolved base), and one credit on /zip. This works in both the US and Canada.
What ACS data years are available?
On /zip and /radius we offer 14 years of ACS 5-Year Estimates (2011–2024) across all four profiles: Demographic (DP05), Social (DP02), Economic (DP03), and Housing (DP04). The default acs_* flags return the most recent year (2024, covering 2020–2024 estimates). To request a specific historical year, append _YYYY to any ACS flag (e.g., include=acs_demographic_2022). You can request multiple years in the same call. Address tract ACS supports the same 2011–2024 with acs_tract_* flags. Field availability varies by year; see the ACS coverage table, as Census added new questions over time. New ACS releases are added annually, typically within weeks of the Census Bureau publication.
Is there a batch/bulk endpoint?
Yes: Quick ZIP, ZIP Details, Radius, Distance (100 items each), and Address (250). A paid subscription or purchased credits are required. There is no suggest batch. Duplicates are processed and billed individually. See Batch Requests for body formats.
Why does one ZIP return multiple counties, tracts, or school districts?
ZIP code boundaries and census/administrative boundaries are drawn independently — they almost never align. A single ZIP frequently spans multiple counties (even states), dozens of census tracts, and several school districts. The pct_of_zip and pct_of_area fields on each overlap quantify how much of each area is shared.
What is the difference between centroid and spatial mode on /radius?
Centroid (default) uses haversine distance between centroids — fast, supports up to 500mi, returns US ZIP codes + full Canadian postal codes. pct_inside is omitted; area_sq_mi is present when available. Spatial uses precise polygon intersection (STBuffer/STIntersects) — slower but includes pct_inside and area_sq_mi, supports up to 250mi, returns US ZIP codes + Canadian FSAs (no full postal codes). Use mode=spatial when you need overlap precision.