Skip to main content

Zipcodes

Python: PyPI - Python Version PyPI Python downloads (all time) Python downloads (monthly)

Rust: Minimum Rust version crates.io Rust downloads (all time) Rust downloads (recent)

Zipcodes is a simple library for querying U.S. zipcodes. No SQLite, no network, no runtime data files — the full dataset is embedded in the package. Contributors

Since 2.0, the library is implemented in Rust and published from a single codebase as both the zipcodes Python package and the zipcodes Rust crate. The Python API is a drop-in replacement for 1.x — same functions, same dicts, same exceptions — just faster:

Operation 1.x (pure Python) 2.0 (Rust)
import zipcodes ~330 ms (loads dataset) ~5 ms (dataset loads lazily on first query, ~200 ms)
is_real("06903") ~4.2 ms ~0.03 ms
matching("77429") ~4.2 ms ~0.3 ms
similar_to("1018") ~7.2 ms ~0.3 ms
filter_by(state="TX") ~9.7 ms ~3.7 ms
>>> import zipcodes
>>> assert zipcodes.is_real('77429')
>>> assert len(zipcodes.similar_to('7742')) != 0
>>> exact_zip = zipcodes.matching('77429')[0]
>>> filtered_zips = zipcodes.filter_by(city="Cypress", state="TX")
>>> assert exact_zip in filtered_zips
>>> pprint.pprint(exact_zip)
{'acceptable_cities': [],
 'active': True,
 'area_codes': ['281', '346', '713', '832'],
 'city': 'Cypress',
 'country': 'US',
 'county': 'Harris County',
 'lat': '29.9766',
 'long': '-95.6358',
 'state': 'TX',
 'timezone': 'America/Chicago',
 'unacceptable_cities': [],
 'world_region': 'NA',
 'zip_code': '77429',
 'zip_code_type': 'STANDARD'}

The zipcode data is refreshed automatically every month — see Zipcode Data.

Installation

Python

$ python -m pip install zipcodes

Zipcodes 2.x supports Python 3.9+ and ships prebuilt wheels for Linux (x86_64, aarch64, musl), macOS, and Windows. Installing from the source distribution requires a Rust toolchain. Python 2.6+/3.2+ users are automatically served the pure-Python 1.3.0 release by pip.

Rust

$ cargo add zipcodes

New in 2.0

  • Implemented in Rust; the dataset is compiled into the extension module and decompressed lazily on first query, so import zipcodes is effectively free.
  • New functions: contains, filter_by_state, filter_by_city, filter_by_county, filter_by_timezone, filter_by_zip_code_type, filter_by_coordinates, and haversine.
  • is_valid now actually emits its DeprecationWarning (in 1.x it raised AttributeError); use is_real.
  • PyInstaller users no longer need --add-data for zips.json.bz2 — there is no data file anymore.
  • Behavioral notes for upgraders: query results are fresh dicts (mutating a result no longer mutates the shared database list), and filter_by(active=1) no longer matches active=True (pass a bool).
  • Unified validation. Input validation now lives once, in the Rust core, instead of a separate Python validator. This changes a few edge cases:
    • Breaking: is_real/matching now raise ValueError for input shorter than five characters (after trimming), instead of returning False/[].
    • Relaxations: surrounding whitespace (" 06903 ") and space-separated Zip+4 ("12345 6789") are now accepted; any input of five or more characters is normalized to its first five digits, generalizing the existing #####-#### handling.
    • Private API removal: the undocumented helpers zipcodes._clean and zipcodes._contains_nondigits (and the _digits/_valid_zipcode_length module attributes) no longer exist.

Zipcode Data

The embedded dataset (crates/zipcodes/src/zips.json.bz2) is assembled from three sources:

  • unitedstateszipcodes.org — the rich base data (city aliases, zip type, area codes, county, timezone), committed in-repo as scripts/data/zip_code_database.csv. Its bot protection prevents automated downloads, so this file is refreshed manually on occasion.
  • GeoNames (download.geonames.org/export/zip/US.zip) — GPS coordinates, fetched fresh on every update. Licensed CC BY 4.0.
  • USPS ZIP Locale Detail (postalpro.usps.com) — the authoritative list of active delivery ZIPs, fetched fresh on every update and used to add zipcodes missing from the base CSV (#23). Public domain.

A GitHub Actions workflow (update-data.yml) rebuilds the dataset on the 1st of every month (#7). If anything changed, it opens a pull request with a summary of added/removed/modified records; merging that PR tags a patch release, which publishes to PyPI and crates.io automatically.

To rebuild the dataset manually:

$ pip install -r scripts/requirements.txt
$ curl -fsSL https://download.geonames.org/export/zip/US.zip -o /tmp/geonames_us.zip
$ # download the .xlsx linked from https://postalpro.usps.com/ZIP_Locale_Detail
$ python scripts/update_zipcode_dataset.py \
    --base scripts/data/zip_code_database.csv \
    --gps scripts/data/zip-codes-database-FREE.csv \
    --geonames-zip /tmp/geonames_us.zip \
    --usps-file /tmp/usps_zip_locale.xlsx \
    --output-bz2 crates/zipcodes/src/zips.json.bz2 \
    --summary-output /tmp/change_summary.json

Tests

CI runs Rust checks and Python tests on pushes to master and pull requests. Rust API docs are built automatically on docs.rs after each crate release; this README is published to PyPI with each Python release.

The tests are defined in a declarative, table-based format that generates test cases.

$ cargo test                    # Rust unit tests
$ python tests/__init__.py      # Python suite (or: pytest tests/)

Examples

>>> from pprint import pprint
>>> import zipcodes

>>> # Simple zip-code matching.
>>> pprint(zipcodes.matching('77429'))
[{'acceptable_cities': [],
  'active': True,
  'area_codes': ['281', '346', '713', '832'],
  'city': 'Cypress',
  'country': 'US',
  'county': 'Harris County',
  'lat': '29.9766',
  'long': '-95.6358',
  'state': 'TX',
  'timezone': 'America/Chicago',
  'unacceptable_cities': [],
  'world_region': 'NA',
  'zip_code': '77429',
  'zip_code_type': 'STANDARD'}]


>>> # Handles Zip+4 zip-codes nicely. :)
>>> pprint(zipcodes.matching('77429-1145'))
[{'acceptable_cities': [],
  'active': True,
  'area_codes': ['281', '346', '713', '832'],
  'city': 'Cypress',
  'country': 'US',
  'county': 'Harris County',
  'lat': '29.9766',
  'long': '-95.6358',
  'state': 'TX',
  'timezone': 'America/Chicago',
  'unacceptable_cities': [],
  'world_region': 'NA',
  'zip_code': '77429',
  'zip_code_type': 'STANDARD'}]

>>> # Will try to handle invalid zip-codes gracefully...
>>> print(zipcodes.matching('06463'))
[]

>>> # Until it cannot.
>>> zipcodes.matching('0646a')
Traceback (most recent call last):
  ...
ValueError: Invalid characters, zipcode may only contain digits and "-".

>>> zipcodes.matching('0646')
Traceback (most recent call last):
  ...
ValueError: Invalid format, zipcode must be of the format: "#####" or "#####-####"

>>> zipcodes.matching(None)
Traceback (most recent call last):
  ...
TypeError: Invalid type, zipcode must be a string.

>>> # Whether the zip-code exists within the database.
>>> print(zipcodes.is_real('06463'))
False

>>> # How handy!
>>> print(zipcodes.is_real('06469'))
True

>>> # Search for zipcodes that begin with a pattern.
>>> pprint(zipcodes.similar_to('1018'))
[{'acceptable_cities': [],
  'active': False,
  'area_codes': ['212'],
  'city': 'New York',
  'country': 'US',
  'county': 'New York County',
  'lat': '40.71',
  'long': '-74',
  'state': 'NY',
  'timezone': 'America/New_York',
  'unacceptable_cities': ['J C Penney'],
  'world_region': 'NA',
  'zip_code': '10184',
  'zip_code_type': 'UNIQUE'},
 {'acceptable_cities': [],
  'active': True,
  'area_codes': ['212'],
  'city': 'New York',
  'country': 'US',
  'county': 'New York County',
  'lat': '40.7808',
  'long': '-73.9772',
  'state': 'NY',
  'timezone': 'America/New_York',
  'unacceptable_cities': ['Manhattan', 'New York City', 'NY', 'Ny City', 'Nyc'],
  'world_region': 'NA',
  'zip_code': '10185',
  'zip_code_type': 'PO BOX'}]

>>> # Use filter_by to filter a list of zip-codes by specific attribute->value pairs.
>>> pprint(zipcodes.filter_by(city="Old Saybrook"))
[{'acceptable_cities': [],
  'active': True,
  'area_codes': ['860', '959'],
  'city': 'Old Saybrook',
  'country': 'US',
  'county': 'Middlesex County',
  'lat': '41.2913',
  'long': '-72.385',
  'state': 'CT',
  'timezone': 'America/New_York',
  'unacceptable_cities': ['Fenwick'],
  'world_region': 'NA',
  'zip_code': '06475',
  'zip_code_type': 'STANDARD'}]

>>> # Arbitrary nesting of similar_to and filter_by calls, allowing for great precision while filtering.
>>> pprint([z['zip_code'] for z in zipcodes.similar_to('2', zips=zipcodes.filter_by(active=True, city='Windsor'))])
['23487', '27983', '29856']

>>> # Find zipcodes within a radius (miles) of a coordinate.
>>> pprint([z['zip_code'] for z in zipcodes.filter_by_coordinates(41.3015, -72.3879, 5)])
['06371', '06409', '06426', '06442', '06475', '06498']

>>> # Have any other ideas? Make a pull request and start contributing today!
>>> # Made with love by Sean Pianka

Repository layout

This repository builds both packages from one Rust core:

  • crates/zipcodes — the core library, published to crates.io.
  • crates/zipcodes-py — PyO3 bindings (not published to crates.io).
  • python/zipcodes — the thin Python compatibility layer; together with the bindings it forms the PyPI package, built with maturin.

Metadata

Release files for zipcodes 3.0.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for zipcodes 3.0.1
File Size Uploaded Provenance Actions
zipcodes-3.0.1.tar.gz 734.8 kB With provenance Details for zipcodes-3.0.1.tar.gz

Built distributions (wheels)

Table of built distributions (wheels) for zipcodes 3.0.1
File Actions
zipcodes-3.0.1-cp39-abi3-win_amd64.whl CPython 3.9 abi3 Windows x86-64 Details for zipcodes-3.0.1-cp39-abi3-win_amd64.whl
zipcodes-3.0.1-cp39-abi3-musllinux_1_2_x86_64.whl CPython 3.9 abi3 Linux musl 1.2+ x86-64 Details for zipcodes-3.0.1-cp39-abi3-musllinux_1_2_x86_64.whl
zipcodes-3.0.1-cp39-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl CPython 3.9 abi3 Linux glibc 2.17+ x86-64 Details for zipcodes-3.0.1-cp39-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl
zipcodes-3.0.1-cp39-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl CPython 3.9 abi3 Linux glibc 2.17+ ARM64 Details for zipcodes-3.0.1-cp39-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl
zipcodes-3.0.1-cp39-abi3-macosx_10_12_universal2.macosx_10_12_x86_64.macosx_11_0_arm64.whl CPython 3.9 abi3 macOS 11.0+ ARM64, macOS 10.12+ universal2 (ARM64, x86-64), macOS 10.12+ x86-64 Details for zipcodes-3.0.1-cp39-abi3-macosx_10_12_universal2.macosx_10_12_x86_64.macosx_11_0_arm64.whl

Total release size: 7.0 MB

Release files / zipcodes-3.0.1.tar.gz

Download URL zipcodes-3.0.1.tar.gz
Size 734.8 kB
Tags Source
SHA-256 checksum
How to use checksums
6e3798254039063df8d0ac13dc6514893d1ba991070577079db056e36b022ca1
BLAKE2b-256 checksum
How to use checksums
2f8507fcdff10102d240b0df9ca2cae2768ee889eb87594a5c4cf8e8c4805f94
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 2, 2026.

Transparency log

Release files / zipcodes-3.0.1-cp39-abi3-win_amd64.whl

Download URL zipcodes-3.0.1-cp39-abi3-win_amd64.whl
Size 928.8 kB
Tags CPython 3.9 Windows x86-64 abi3
SHA-256 checksum
How to use checksums
304b660ba91e3d455736166f5a36e92832d9273d0508e8a63f816dc619bd7725
BLAKE2b-256 checksum
How to use checksums
879275892b57bcb6db037b509437814415da25c546399a820375c4f7c10934ae
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 2, 2026.

Transparency log

Release files / zipcodes-3.0.1-cp39-abi3-musllinux_1_2_x86_64.whl

Download URL zipcodes-3.0.1-cp39-abi3-musllinux_1_2_x86_64.whl
Size 1.2 MB
Tags CPython 3.9 Linux musl 1.2+ x86-64 abi3
SHA-256 checksum
How to use checksums
946dd0e3d131da4aa06ed75f07cd861f2a5a1f360cf14cfe703e912ce7c190cc
BLAKE2b-256 checksum
How to use checksums
4ae4c97ba5e7247c2816a1c932c9c474bb8b8b149bba2b853d5b4ed727cbd771
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 2, 2026.

Transparency log

Release files / zipcodes-3.0.1-cp39-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl

Download URL zipcodes-3.0.1-cp39-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl
Size 1.0 MB
Tags CPython 3.9 Linux glibc 2.17+ x86-64 abi3
SHA-256 checksum
How to use checksums
9bc4a6a39f2f1ee7a106e9b35058afebd0048f83286f9c933c033888c3c00dc4
BLAKE2b-256 checksum
How to use checksums
2943057aa26ae6da1cfc8ae280a148d631b77eca25351620b955b70ec37d2ea1
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 2, 2026.

Transparency log

Release files / zipcodes-3.0.1-cp39-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl

Download URL zipcodes-3.0.1-cp39-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl
Size 1.0 MB
Tags CPython 3.9 Linux glibc 2.17+ ARM64 abi3
SHA-256 checksum
How to use checksums
9ca0d3a17cec753ca648dc6c9563120425ceaafd1fdbe9f6bcc667da6e8bb025
BLAKE2b-256 checksum
How to use checksums
31bf80b4343cacfe56cb4d273ff2cbb628731e675af246569b91fcd707d6e7d8
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 2, 2026.

Transparency log

Release files / zipcodes-3.0.1-cp39-abi3-macosx_10_12_universal2.macosx_10_12_x86_64.macosx_11_0_arm64.whl

Download URL zipcodes-3.0.1-cp39-abi3-macosx_10_12_universal2.macosx_10_12_x86_64.macosx_11_0_arm64.whl
Size 2.0 MB
Tags CPython 3.9 abi3 macOS 10.12+ universal2 (ARM64, x86-64) macOS 10.12+ x86-64 macOS 11.0+ ARM64
SHA-256 checksum
How to use checksums
ed5710da31e9712c623d1f07269e784f56f01b8116cf512f2b72df3c946751c5
BLAKE2b-256 checksum
How to use checksums
9ace0fa69c93548ea24c4d354c5837806a90058dc590b1dfd00617df69804c6b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 2, 2026.

Transparency log

Security for zipcodes 3.0.1

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 6 files in this release have been uploaded with provenance metadata.

Select from 6 files

Attestation for zipcodes-3.0.1.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 2, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page