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.
Here's the full list with states:
02861 Rhode Island
59221 North Dakota
81137 New Mexico
86515 New Mexico
88063 New Mexico
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(
# 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(
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