36

I'm using a zip code listing, and I am curious to know how many (or which) zip codes map to more than one US state or US city?

For instance I know that Zip Code 42223 resolves to US Army, Fort Campbell which straddles the KY-TN state line. Oddly, the google API only returns TN for that state corresponding to that zip.

1
  • How are you defining "city", and "zip code"? Commented Jan 5, 2017 at 20:10

9 Answers 9

31

There are 13 multi-state US Census' ZIP Code Tabulation Areas (ZCTAs): 02861, 42223, 59221, 63673, 71749, 73949, 81137, 84536, 86044, 86515, 88063, 89439 & 97635.

As others have mentioned, there are a few different ways to figure out the area covered by a ZIP Code, but ZCTAs are the easiest, and the only official version that I know of.

So your example of 42223 does span a state border, but it looks like it is actually between Maryland and Virginia. that between Kentucky and Tennessee.

Here's the full list with states:

 02861  Massachusetts
 02861   Rhode Island
 42223       Kentucky
 42223      Tennessee
 59221        Montana
 59221   North Dakota
 63673       Illinois
 63673       Missouri
 71749       Arkansas
 71749      Louisiana
 73949       Oklahoma
 73949          Texas
 81137       Colorado
 81137     New Mexico
 84536        Arizona
 84536           Utah
 86044        Arizona
 86044           Utah
 86515        Arizona
 86515     New Mexico
 88063     New Mexico
 88063          Texas
 89439     California
 89439         Nevada
 97635     California
 97635         Oregon

Here's how I generated it (with Pandas in Python):

import pandas as pd

zcta_to_place_url = 'http://www2.census.gov/geo/docs/maps-data/data/rel/zcta_place_rel_10.txt'

# load relevant data
df = pd.read_csv(
  zcta_to_place_url,
  dtype={'ZCTA5': str},
  usecols=['ZCTA5', 'STATE'])

# the data often repeats the same (ZCTA, state) pair. Remove these
df = df.drop_duplicates()

# get number of times each ZCTA appears (most are only 1)
counts = df['ZCTA5'].value_counts()

# get those listed more than once
multi_state_zips = df[df.ZCTA5.isin(counts[counts > 1].index)]


# the census uses numeric state codes
# replace these with state names

census_codes_to_names_url = 'http://www2.census.gov/geo/docs/reference/state.txt'

states = pd.read_csv(census_codes_to_names_url, sep='|')
merged = pd.merge(
  multi_state_zips, states,
  on='STATE'
  )[['ZCTA5', 'STATE_NAME']]
print merged.sort(['ZCTA5', 'STATE_NAME']).to_string(index=False)

Edit: It seems the Census has two different two-digit codings for states. Both are numbers assigned based on the state's alphabetical ordering, but one seems to apply the numbers directly from 1-51 (50 states + DC), while the other skips some numbers. I was using the first, while I should have been using the second, so the state names I listed were wrong. I've updated the code and results with the correct list.

Edit: new state mapping confirmed by the OpenCongress API: https://gist.github.com/gabrielgrant/89f883d093e2abf129ad

9
  • 2
    Thanks a lot for catching this @JesseCrocker - It seems the Census (confusingly) has two different two-digit codings for states. Both are numbers assigned based on the state's alphabetical ordering, but one seems to apply the numbers directly from 1-51 (50 states + DC), while the other skips some numbers. I was using the first, but I should have been using the second, so the state names I listed were wrong (though the ZCTAs were good). I've fixed the code and results with the correct list. Commented Oct 22, 2015 at 10:42
  • 2
    Regarding the gaps in the FIPS codes, the skipped numbers were reserved in the 1970s for outlying territories (American Samoa, Canal Zone, Guam, Puerto Rico, and Virgin Islands), but then didn't end up being used for them. en.wikipedia.org/wiki/…
    – neuhausr
    Commented Jan 7, 2016 at 14:41
  • 4
    Don't forget zipcode 57717 which spans three states six, counties, and multiple cities: 57717 Aurora, SD 57717 Butte, SD 57717 Carter, MT 57717 Crook, WY 57717 Harding, SD 57717 Lawrence, SD
    – Jeffrey
    Commented Apr 22, 2016 at 21:58
  • 2
    This listing isn't near complete. Check out my answer for a far better approximation. gis.stackexchange.com/a/223445/6052 Commented Jan 5, 2017 at 19:08
  • 1
    That's odd. Google Maps shows 02861 as literally forming part of the border between MA and RI, and the entire zipcode firmly in RI google.com/maps/place/Pawtucket,+RI+02861/… Commented Dec 25, 2019 at 1:13
13

There really isn't a way to tell this; since there is not a ZipCode boundary shape that is defined by the USPS. ZipCodes are defined by a bounding box of Streets delivered to by carriers from a particular distribution center.

So you would need to take the USPS AIS data and extract by ZipCodes the streets that are delivered by a given Post Office, then Join these a street grid. This is what all the commercial vendors do (Nokia/TomTom) to create the Psuedo shape that they use to show postal boundaries.

This inexact process is the reason why the USPS does not provide spatial data.

2
11

2016 TIGER Data with PostGIS

As a special caveat, ZCTA data isn't USPS Zip Codes. It's an approximation of it. USPS Zip Codes are really horrible and not useful except to approximate. Everyone, including every governmental entity other than USPS, and (the Census for making ZCTA) ignores them entirely. If USPS wanted to a grow up a bit, they'd just convert to the latest ZCTA and provide authoritative GIS polygons.

Then... Here we query for intersections between TIGER State and TIGER ZCTA datasets. Note, we qualify states by 1% of the total ZCTA area. If 1% of the ZCTA area isn't in the state, we assume it's a rounding error, or someone fat fingering something at the Census. Check out 56168 or even 83832 for a zip code that we're pruning with this added selectivity.

SELECT zcta5ce10, array_agg(state.name ORDER BY state.name) AS states
FROM census.state AS state
JOIN census.zcta AS zcta ON (
  ST_Intersects(state.geog::geometry, zcta.geog::geometry)
  AND NOT ST_Touches(state.geog::geometry, zcta.geog::geometry)
  AND ST_Area(ST_Intersection(state.geog, zcta.geog)) > (ST_Area(zcta.geog)*0.01)
)
GROUP BY zcta.zcta5ce10
HAVING count(*) > 1
ORDER BY zcta5ce10;

Here is the resulset

 zcta5ce10 |            states            
-----------+---------------------------------
 03579     | {Maine,"New Hampshire"}
 20135     | {Virginia,"West Virginia"}
 24604     | {Virginia,"West Virginia"}
 31905     | {Alabama,Georgia}
 38079     | {Kentucky,Tennessee}
 38769     | {Arkansas,Mississippi}
 38852     | {Alabama,Mississippi}
 42223     | {Kentucky,Tennessee}
 51001     | {Iowa,"South Dakota"}
 51023     | {Iowa,"South Dakota"}
 51360     | {Iowa,Minnesota}
 51557     | {Iowa,Nebraska}
 51640     | {Iowa,Missouri}
 52542     | {Iowa,Missouri}
 52573     | {Iowa,Missouri}
 52626     | {Iowa,Missouri}
 54554     | {Michigan,Wisconsin}
 56027     | {Iowa,Minnesota}
 56144     | {Minnesota,"South Dakota"}
 56164     | {Minnesota,"South Dakota"}
 56219     | {Minnesota,"South Dakota"}
 56744     | {Minnesota,"North Dakota"}
 57026     | {Minnesota,"South Dakota"}
 57030     | {Minnesota,"South Dakota"}
 57068     | {Minnesota,"South Dakota"}
 57078     | {Nebraska,"South Dakota"}
 57638     | {"North Dakota","South Dakota"}
 57641     | {"North Dakota","South Dakota"}
 57642     | {"North Dakota","South Dakota"}
 57645     | {"North Dakota","South Dakota"}
 57648     | {"North Dakota","South Dakota"}
 57660     | {"North Dakota","South Dakota"}
 57717     | {"South Dakota",Wyoming}
 57724     | {Montana,"South Dakota"}
 58225     | {Minnesota,"North Dakota"}
 58439     | {"North Dakota","South Dakota"}
 58623     | {"North Dakota","South Dakota"}
 58649     | {"North Dakota","South Dakota"}
 58653     | {"North Dakota","South Dakota"}
 59221     | {Montana,"North Dakota"}
 59270     | {Montana,"North Dakota"}
 59275     | {Montana,"North Dakota"}
 59847     | {Idaho,Montana}
 63673     | {Illinois,Missouri}
 65729     | {Arkansas,Missouri}
 65733     | {Arkansas,Missouri}
 65761     | {Arkansas,Missouri}
 66541     | {Kansas,Nebraska}
 67950     | {Kansas,Oklahoma}
 68325     | {Kansas,Nebraska}
 68719     | {Nebraska,"South Dakota"}
 68978     | {Kansas,Nebraska}
 69201     | {Nebraska,"South Dakota"}
 69212     | {Nebraska,"South Dakota"}
 69216     | {Nebraska,"South Dakota"}
 71749     | {Arkansas,Louisiana}
 72338     | {Arkansas,Tennessee}
 72644     | {Arkansas,Missouri}
 73949     | {Oklahoma,Texas}
 75556     | {Arkansas,Texas}
 79837     | {"New Mexico",Texas}
 80758     | {Colorado,Nebraska}
 81137     | {Colorado,"New Mexico"}
 81324     | {Colorado,Utah}
 82063     | {Colorado,Wyoming}
 82082     | {Nebraska,Wyoming}
 82701     | {"South Dakota",Wyoming}
 82801     | {Montana,Wyoming}
 82930     | {Utah,Wyoming}
 83111     | {Idaho,Wyoming}
 83120     | {Idaho,Wyoming}
 83312     | {Idaho,Utah}
 83342     | {Idaho,Utah}
 84034     | {Nevada,Utah}
 84531     | {Arizona,Utah}
 84536     | {Arizona,Utah}
 86044     | {Arizona,Utah}
 86504     | {Arizona,"New Mexico"}
 86514     | {Arizona,Utah}
 86515     | {Arizona,"New Mexico"}
 87328     | {Arizona,"New Mexico"}
 88220     | {"New Mexico",Texas}
 88430     | {"New Mexico",Texas}
 89010     | {California,Nevada}
 89019     | {California,Nevada}
 89060     | {California,Nevada}
 89421     | {Nevada,Oregon}
 89439     | {California,Nevada}
 89832     | {Idaho,Nevada}
 97635     | {California,Oregon}
 97910     | {Idaho,Oregon}
 99128     | {Idaho,Washington}
 99362     | {Oregon,Washington}
(93 rows)

You should be able to spot check all of these in Google Maps. However, Google Maps is also not authoritative.

7

The US Census Bureau derives approximate boundaries for ZIP codes based on the addresses contained within them, called ZIP Code Tabulation Areas (ZCTAs).

They publish relationship files that describe how their ZCTAs map to various other geographies. If you examine the ZCTA to Place relationship file you can see how they map to cities and towns. You can infer how they map to states from the ZCTA to Counties relationship file.

The relationship files use Census geography IDs, so you'll want to grab a gazetteer file to help you convert the numeric IDs into the place or county names you're expecting.

As other answers have stated, any mapping of ZIP codes to places is likely to be approximate, but I've had good luck with the Census data files.

0
2

Good news - the ZCTA file for 2020 Census, national, recently came out. You could check for yourself here: 2020 TIGER/Line® Shapefiles: ZIP Code Tabulation Areas.

You would want to intersect this file with the state shapefile, pull the attribute table, and look for ZIP codes (ZCTAs) that show up more than once (attached to different states).

Note: ZCTAs do cross state boundaries! (Understanding Geographic Relationships: Counties, Places, Tracts and More)

1

With ArcGIS you can use the spatial join tool (or in a script) to find which zip code polygons intersect with more than one state polygons. In the output feature class, there will be a Join_Count field that will indicate multiple states. You could do a similar thing with zips and cities. There will likely be false positives where the zips unintentionally overlap more than one because of border inaccuracies/lack or resolution. You could possibly do a negative -100m buffer of the zips before the spatial join and see what that does.

import arcpy

target_features = "C:/data/usa.gdb/states"
join_features = "C:/data/usa.gdb/zips"
out_feature_class = "C:/data/usa.gdb/states_zips"

arcpy.SpatialJoin_analysis(target_features, join_features, out_feature_class, "JOIN_ONE_TO_MANY")

http://help.arcgis.com/en/arcgisdesktop/10.0/help/index.html#/Spatial_Join/00080000000q000000/
"Two new fields, Join_Count and TARGET_FID, are always added to the output feature class. Join_Count indicates how many join features match each target feature (TARGET_FID). Another new field, JOIN_FID, is added to the output when JOIN_ONE_TO_MANY is specified in the Join Operation parameter."

1

State Overlaps mentioned in 1994 Census Document

In June 1994, according to the following U.S. Census Bureau site there are 153 zip codes that cross state boundries.

As mentioned earlier, there are a few ZIP Codes that deliver across state lines, and there are a few ZIP/sectors that cross county lines. There are 153 ZIP Codes in more than one state. There are 9,000 ZIP Codes in more than one county. There were 11,331 (out of the total 857,400) ZIP/sectors that were split by county. All states had some split sectors, with Virginia, Michigan and Ohio having an especially larger dosage. The rural route sectors, as expected, contained (relatively) the lion's share of split sectors. Most of the other cases are in the lower sector range (reserved for post office boxes) and in Sector 99 (reserved for the postmaster and business mail return). There must be some non-standard county code assignment occurring for these selected cases. We will have to further investigate these at a later date.

0

You can do a spatial intersect in PostGIS and get a list back of every State or City and the Zip Codes that they intersect, which would return multiple zip codes where multiple states intersect, and for each city that intersected the same zip, you would see that result as well.

1
0

It appears that the US Census Bureau has not updated its ZIP Code Tabulation Areas since 2010. So the list you are looking for should be the same as in previous answers, such as this one.

Not the answer you're looking for? Browse other questions tagged or ask your own question.