ZIP Codes MCP Server
Validate an address and get the neighborhood behind it (census tract demographics, income, housing, districts, schools) in one call.
Real addresses come back standardized with their ZIP+4. An address that doesn't exist never gets an invented one: it comes back as no match, or as a low-confidence match with no ZIP+4.
https://mcp.zip-codes.com/mcp
Models invent ZIP Codes, ZIP+4s and counties. These tools answer from current USPS data, updated monthly, and Census boundaries, so your agent looks the answer up instead of making it up.
Send a US street address, even a messy one-line one, and get back its standard form and ZIP+4, plus what you ask for about the area around it: the census tract's survey estimates, its county, place and metro area, its congressional and state legislative districts, its school district and its time zone.
ZIP and Canadian postal code lookups, radius search, distance and fuzzy place search are there too. It works over the Model Context Protocol (MCP), so any MCP-capable agent can call it directly.
Built for
The statistics describe the census tract an address sits in, never the person at the address.
Try it instantly, no key. Without one, every tool answers in demo mode, with real, current values and some deeper fields withheld. It works in every MCP client.
Add a key for complete answers. They cost the same credits as the REST API. A free key includes 2,500 credits a day.
"Validate 350 5th Ave, New York, NY 10118, the Empire State Building, and tell me about its census tract: median household income, median rent and school district, with margins of error."
One validate_address call with include: ["census", "school_district", "acs_tract_economic", "acs_tract_housing"] answers it, for 5 credits:
| Field | Answer |
|---|---|
| Address | 350 5TH AVE, NEW YORK, NY 10118-0110 (confidence: medium; a unit number may be needed) |
| Census tract | Census Tract 76, New York County, NY (36061007600) |
| Median household income | $193,644 ± $15,300 |
| Median rent | $3,304 ± $249 |
| School district | New York City Department Of Education (PK–12) |
Income and rent are Census Bureau estimates for the whole tract (American Community Survey 5-Year Estimates, 2020–2024), labelled geography_level: "tract", each with its margin of error. They describe the area, not any household in it. The Empire State Building is one of our demo addresses, so demo mode answers this in full. For other addresses, demo mode returns the matched address and ZIP Code and withholds the ZIP+4, the tract, the school district and most estimates.
Start in demo mode: no key, no sign-up. Add your key when you want complete answers. Send it in the X-Api-Key header, the only header the server reads a key from.
claude mcp add --transport http zip-codes https://mcp.zip-codes.com/mcp
claude mcp add --transport http zip-codes https://mcp.zip-codes.com/mcp --header "X-Api-Key: YOUR_KEY"
Claude Code accepts any value here without checking it, so replace YOUR_KEY, then run /mcp to see the server's status. In a shared .mcp.json, write the header as "X-Api-Key": "${ZIPCODES_API_KEY}" and set that environment variable.
The keyed button adds the header with YOUR_KEY in it: replace that with your key in mcp.json. Or add it by hand:
{
"mcpServers": {
"zip-codes": {
"url": "https://mcp.zip-codes.com/mcp"
}
}
}
{
"mcpServers": {
"zip-codes": {
"url": "https://mcp.zip-codes.com/mcp",
"headers": { "X-Api-Key": "${env:ZIPCODES_API_KEY}" }
}
}
}
The keyed button asks for your key once and stores it securely; the key is never in the link. Or add it by hand:
{
"servers": {
"zip-codes": {
"type": "http",
"url": "https://mcp.zip-codes.com/mcp"
}
}
}
{
"servers": {
"zip-codes": {
"type": "http",
"url": "https://mcp.zip-codes.com/mcp",
"headers": { "X-Api-Key": "${input:zipcodes-api-key}" }
}
},
"inputs": [
{
"type": "promptString",
"id": "zipcodes-api-key",
"description": "ZIP Codes API key",
"password": true
}
]
}
Add a custom connector in Claude's connector settings, choose No sign-in, and enter the server URL:
https://mcp.zip-codes.com/mcp
That runs in demo mode. With a key: if your connector form has a Request headers section, add x-api-key with your key as the value. Request headers are a beta available to a limited set of organizations; without that section the connector runs in demo mode. Authentication can't be edited after a connector is added, so remove it and add it again to change it.
For a key in Claude Desktop without the Request headers beta, bridge with mcp-remote (Node 18 or later) in claude_desktop_config.json, then quit Claude Desktop completely and reopen it.
{
"mcpServers": {
"zip-codes": {
"command": "npx",
"args": ["-y", "mcp-remote", "https://mcp.zip-codes.com/mcp", "--transport", "http-only"]
}
}
}
{
"mcpServers": {
"zip-codes": {
"command": "npx",
"args": ["-y", "mcp-remote", "https://mcp.zip-codes.com/mcp", "--transport", "http-only",
"--header", "X-Api-Key:${ZIPCODES_API_KEY}"],
"env": { "ZIPCODES_API_KEY": "YOUR_KEY" }
}
}
}
Keep the header without a space after the colon: Claude Desktop on Windows mangles spaces inside arguments.
[mcp_servers.zip-codes]
url = "https://mcp.zip-codes.com/mcp"
[mcp_servers.zip-codes]
url = "https://mcp.zip-codes.com/mcp"
env_http_headers = { "X-Api-Key" = "ZIPCODES_API_KEY" }
Add a custom MCP server in ChatGPT's settings, enter the server URL and choose no authentication:
https://mcp.zip-codes.com/mcp
ChatGPT's custom connectors support OAuth or no authentication, not an API key header, so ChatGPT runs in demo mode.
Add the server to Cascade's mcp_config.json (open it from the Cascade panel's menu, under MCPs).
{
"mcpServers": {
"zip-codes": {
"serverUrl": "https://mcp.zip-codes.com/mcp"
}
}
}
{
"mcpServers": {
"zip-codes": {
"serverUrl": "https://mcp.zip-codes.com/mcp",
"headers": { "X-Api-Key": "${env:ZIPCODES_API_KEY}" }
}
}
}
{
"mcpServers": {
"zip-codes": {
"url": "https://mcp.zip-codes.com/mcp",
"headers": { "X-Api-Key": "YOUR_KEY" }
}
}
}
Leave out headers to run in demo mode. A client that can't send the X-Api-Key header runs in demo mode.
Check that it works: ask your agent "What county is ZIP 02872 in?" With a key it gives two answers that disagree, both correct: USPS assigns this Prudence Island PO Box ZIP to Bristol County, where its mail is handled, while 100% of the island lies in Newport County, Rhode Island. In demo mode the county comes back withheld (*****).
YOUR_KEY without checking it. Run /mcp in Claude Code to see the server's status, and re-add the server with your real key.
* to *****, withheld numbers and true/false as null. A key returns them.
mcp-remote.
--transport http-only. Without it, mcp-remote can fall back to the older SSE transport, which this server doesn't use.
ANTHROPIC_API_KEY, in remote headers. Use a name of your own: ZIPCODES_API_KEY.
Limits. Each tool answers for one place per call, for an agent answering questions. With a key, credits are the only limit. In demo mode there's no call limit, but radius_search lists the nearest 10 matches and suggest_places 5. For long lists, use the REST API's batch endpoints (paid plan).
These work without a key:
"Clean up this address and tell me what you changed: 1600 pennsylvania ave nw washingtn dc"
It fixes the city's spelling and adds the missing ZIP+4: 1600 PENNSYLVANIA AVE NW, WASHINGTON, DC 20500-0005.
"How far is the Empire State Building's ZIP Code (10118) from Beverly Hills (90210)?"
About 2,454 miles in a straight line.
"Is Chicago's 60601 within 500 miles of Toronto's M5V postal area?"
Yes, 433.8 miles; US and Canadian codes mix freely.
"Find the ZIP Codes for 'beverley hils'."
It finds Beverly Hills, CA and its ZIP Codes 90210, 90211 and 90212 despite the spelling.
"How many ZIP Codes are there in the US, by type?"
41,541: 29,807 standard, 9,122 PO Box, 2,023 unique and 589 military.
Ask about then and now. These work without a key:
"How has median rent in ZIP 10001 changed since 2011?"
$1,580 ± $127 in the 2007–2011 survey, $3,361 ± $268 in 2020–2024, each in its own release's dollars. Same field both years, though Census moved it from column DP04_0132 to DP04_0134.
"What congressional district was ZIP 33139 in in 2012, and what is it now?"
On the 2012 map (112th Congress), mostly Florida's 18th (87% of its area) with a slice in the 20th; today (119th Congress), all of it is in the 24th.
"Which census tracts does ZIP 90210 cover, and what changed since 2010?"
13 tracts on the 2010 map, 14 now: tract 2611.01, about 18% of the ZIP in 2010, was redrawn as 2611.03 and 2611.04 for the 2020 Census.
With a key:
"What county is ZIP 06103 in, and what was it in 2021?"
Hartford County on the 2021 map; Capitol Planning Region from 2022, when Connecticut's counties became planning regions in the Census maps.
"Give me a neighborhood brief for Wrigley Field, 1060 W Addison St, Chicago IL 60613."
The address works without a key; comparing it with the ZIP Code needs one.
Our test run, with a key (Claude Code, October 2026), not a guarantee: the agent validated the address, pulled the census tract's survey profiles, compared them with ZIP 60613's, and flagged the tract's wide margins: median household income $117,330 ± $50,774 for Census Tract 611, against $95,572 ± $6,037 for the ZIP.
Each API tool takes the same include flags as its endpoint and returns the same JSON, so the API documentation describes every field. Each response's meta.withheld lists what demo mode held back.
| Tool | What it does | Without a key | REST endpoint |
|---|---|---|---|
validate_address | Standardizes a US street address, even messy one-line input, and returns its ZIP+4. Optional appends: coordinates, census geographies, congressional and state legislative districts, school district, time zone, and Census survey profiles for the address's census tract | Returns the standardized address and ZIP Code. Withholds the ZIP+4 add-on and delivery details (record type, carrier route, county, congressional district), and from the appends the geography names and codes, school district, time zone and most survey estimates | /v2/address |
zip_lookup | Everything about a ZIP Code, ZIP+4, Canadian postal code or lat/long: city, county, area codes, and optional Census, district, school and demographic data | Returns city, state and whether the ZIP spans counties. Withholds county, coordinates, area codes, metro area, city aliases and delivery counts, and from the optional data the geographies, districts, school district, time zone and most survey estimates | /v2/zip |
radius_search | Every ZIP or postal area within a radius, with distances and optional area demographics | Returns codes, cities and distances for the nearest 10 matches, with the total count. Withholds county and coordinates | /v2/radius |
distance | Straight-line distance and bearing between any two places | Returns the distance in miles and kilometers. Withholds bearing, coordinates, county and time zone | /v2/distance |
quick_zip | The fast, small answer for a code, or the code for a lat/long | Withholds county and coordinates | /v2/quick-zip |
suggest_places | Finds places from partial or misspelled names | Returns the first 5 matches with name, type and state. Withholds associated ZIP Codes, coordinates and codes | /v2/suggest |
zip_facts | Statistics about US ZIP Codes and Canadian postal codes: how many exist, how they break down by type, delivery point and carrier route totals, per-state tables, and ranked lists such as the ZIP Codes with the most carrier routes. | Nothing withheld | Built-in reference data (free) |
about_data | Where this server's data comes from: for each dataset the authority behind it, the edition served where the source publishes editions (TIGER/Line, ACS, ZCTA), how often a new release is taken, and a ready-made citation string. | Nothing withheld | Built-in reference data (free) |
city_name_rules | How USPS names a ZIP Code, and which of those names may be used on mail. | Nothing withheld | Built-in reference data (free) |
county_reference | US counties and county equivalents. | Nothing withheld | Built-in reference data (free) |
metro_area_reference | US metropolitan and micropolitan statistical areas (CBSAs). | Nothing withheld | Built-in reference data (free) |
Boundaries back to 2010: what it was then, and what it is now. Every Census TIGER/Line year from 2010 to 2025, 16 in all: census_YYYY, cd_YYYY and state_leg_YYYY. Connecticut's counties became planning regions on the 2022 map: Hartford's ZIP 06103 is in Hartford County in 2021 and Capitol Planning Region from 2022. The 2020 Census redrew tracts: tract 99, nearly a third of ZIP 10001 in 2010, is now 99.01, 99.02 and 99.03. New places appear: ZIP 30038 (Lithonia, GA) was in no census place in 2010; today 95% of it is in Stonecrest city.
Fourteen years of Census survey data, already normalized. 2011–2024 in one schema with plain names, so your agent asks for housing.rent.median, not a column code. Census renumbers its columns when it adds lines, and everything after them shifts: median rent was DP04_0132 in the 2011 release and is DP04_0134 in 2024; median age moved from DP05_0017 to DP05_0018. We already did the mapping across releases, and every value still names its source column. Each estimate comes with its margin of error. A ZIP Code's figures are the Census estimates for the ZCTA with the same code; census tracts cover 2022–2024.
Messy address in, neighborhood out. One line, even with a typo or no ZIP Code, and one call returns the standard address, its ZIP+4, the boundaries it falls inside and the census tract's estimates. Leave out the state and most of the time it still matches: in our test, 20 of 22 well-known addresses typed without a state matched ("1 Infinite Loop, Cupertino" came back as 1 INFINITE LOOP, CUPERTINO, CA 95014-2083). Each answer says what was filled in or corrected, with a confidence level, and a failed match says so rather than guessing.
Real overlaps. ZIP Codes follow mail routes, not political lines. About one in four (9,687 of 41,541) is flagged as spanning more than one county, and 112 reach into a second state. Your agent gets the USPS assignment and how much of the ZIP falls in each county, district or metro area, even when they disagree: USPS files Prudence Island’s ZIP 02872 under Bristol County, yet the island is 100% in Newport County.
ZIP Codes as USPS actually uses them, including PO Box and single-organization ZIPs that Census ZIP-area files leave out.
Compare the tract with its ZIP Code. validate_address with acs_tract_* gives the address's census tract; zip_lookup with acs_* gives its ZIP Code's (the same-code ZCTA's). Side by side they show whether an address sits in a more or less affluent, younger or more renter-heavy part of its ZIP: Wrigley Field's tract 611 reports a median household income of $117,330, ZIP 60613 $95,572.
Check the margins before you rank. A tract is small: tract 611 has about 1,368 residents (± 228), and its income estimate is ± $50,774 against the ZIP's ± $6,037, so the $21,758 gap above is within the margin. Read moe before comparing, and use the ZIP figure when precision matters.
Trends are one comparison. Field names don't change between releases (2011–2024), so a change over time is two numbers from one path: ZIP 10001's housing.rent.median went from $1,580 ± $127 (2007–2011) to $3,361 ± $268 (2020–2024). See the history prompts above.
Two answers to "which county?" The USPS county is where the ZIP is assigned; the census counties are where its area lies. USPS files ZIP 02872 under Bristol County, while 100% of it is in Newport County (pct_of_zip 100, pct_of_area 5.13). Use pct_of_zip to split a ZIP across counties or districts by area: ZIP 33139 was 87% in Florida's 18th district and 13% in the 20th on the 2012 map.
| Feature | Demo (no key) | With a key |
|---|---|---|
| Cost | Free | Same credits as the REST API; reference tools are free |
| Values | Real and current; some deeper fields withheld | Complete |
| Withheld fields | Text shows as *****; numbers and true/false show as null |
Use MCP when an agent is answering questions about places, one at a time. Use the REST API for bulk work: its batch endpoints take up to 100 items per request (250 for addresses), on a paid plan. See the API documentation.
geography_level: "zcta"), an address's from its census tract ("tract").
pct_of_zip (the share of the ZIP's area inside that geography) and pct_of_area (the share of the geography inside the ZIP). Shares are by area, not by population or addresses, and slivers under a small threshold are left out. An address is a point, so it gets the geographies that contain it, with no shares.
about_data tool for each source's edition, cadence and citation.
success: false, error NOT_FOUND, "The address could not be matched.", and no address at all (in our test, 742 Zebrafinch Moonbeam Ln, Chicago, IL 60613 and 4410 Glimmerquartz St, Denver, CO 80203). A house number beyond a real street's range is different: the street is found, so it comes back as a partial match with confidence: "low", only the 5-digit ZIP Code and the notice "The ZIP+4 was not assigned because a specific delivery point could not be established." (99999 5th Ave, New York, NY 10118 came back as 99999 5TH AVE, NEW YORK, NY 10118). Check success, confidence and notices before treating an address as real.
X-Api-Key header: Claude Code, Cursor, VS Code, Codex, Windsurf, Claude Desktop through mcp-remote, and Claude connectors where your organization has the Request headers beta. ChatGPT's custom connectors support OAuth or no authentication, so ChatGPT runs in demo mode, as do Claude connectors without Request headers.
* to *****, withheld numbers and true/false as null, and each response's meta.withheld lists what was held back. The JSON is the same key for key, apart from the demo notes in meta, so add your key in the X-Api-Key header, call the same tool with the same arguments, and the values arrive with nothing else to change. Demo codes and addresses, such as ZIP 90210 and the Empire State Building, come back in full.