Metadata-Version: 2.4
Name: zip-codes-api
Version: 0.1.0
Summary: Python client for the ZIP Codes API: US ZIP and Canadian postal data with 14 years of US Census ACS demographics, radius search, address validation, and distance.
Project-URL: Homepage, https://www.zip-codes.com/api/
Project-URL: Documentation, https://www.zip-codes.com/api/docs
Project-URL: Source, https://github.com/ZIP-Codes-API/zip-codes-api-python
Author-email: "Zip-Codes.com" <jharris@zip-codes.com>
License: MIT
License-File: LICENSE
Keywords: acs,address validation,api,canada,census,demographics,geocoding,postal code,zip code
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: Science/Research
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Topic :: Scientific/Engineering :: GIS
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.8
Requires-Dist: requests>=2.20
Provides-Extra: pandas
Requires-Dist: pandas>=1.0; extra == 'pandas'
Description-Content-Type: text/markdown

# zip-codes-api

Python client for the [ZIP Codes API](https://www.zip-codes.com/api/) — US ZIP and
Canadian postal data with **14 years of US Census ACS demographics (2011–2024)**, radius
search (centroid + spatial coverage-weighting), address validation with ZIP+4, distance,
and typo-tolerant autocomplete.

```bash
pip install zip-codes-api          # core
pip install 'zip-codes-api[pandas]'  # + DataFrame helpers for research
```

## Quickstart

```python
from zip_codes_api import ZipCodesClient

# Public demo key works for demo ZIPs (90210, 10001, ...). Any ZIP: get a free
# key at https://www.zip-codes.com/api/signup (2,500 credits/day, no card).
client = ZipCodesClient("zc_test_DEMOAPIKEY000000000000")

bh = client.zip("90210", include=["acs_demographic", "timezone"])
print(bh["city"])  # Beverly Hills
print(client.last_meta["credits"]["used"])  # credits this call cost
```

Single-target methods (`zip`, `quick_zip`, `radius`, `distance`, `address`, `suggest`)
return the first result object. Batch methods return the list of per-item results.
Pass `raw=True` for the full response envelope. `client.last_meta` always holds the
most recent response's `meta` (including `credits`).

## 14 years of demographics as a DataFrame

The reason most researchers reach for this API — a longitudinal ZIP-level series in one
call, with the year-to-year Census variable drift and the 2020 `GEO_ID` change already
normalized:

```python
df = client.acs_timeseries("90210", profile="demographic", years=range(2011, 2025))
print(df[["sex_and_age.total_population", "sex_and_age.median_age"]])
#       sex_and_age.total_population  sex_and_age.median_age
# year
# 2011                        21719                    45.7
# ...
# 2024                        19004                    51.9
```

`profile` is one of `demographic` (DP05), `social` (DP02), `economic` (DP03),
`housing` (DP04).

## Coverage-weighted radius aggregation (no GIS)

`/radius` in spatial mode intersects your radius with actual ZIP/FSA boundary polygons
and weights the ACS aggregate by each ZIP's `pct_inside` — a ZIP 30% inside contributes
30% of its population, not all-or-nothing:

```python
r = client.radius("90210", max_radius=10, mode="spatial", include="acs_demographic")
pop = r["stats"]["acs"]["current"]["demographic"]["sex_and_age"]["total_population"]["est"]
print(f"{pop:,} people within 10 miles (coverage-weighted)")

matches = client.radius_dataframe("90210", max_radius=10, mode="spatial")  # pandas
print(matches[["code", "city", "pct_inside", "distance_miles"]].head())
```

## Other endpoints

```python
client.quick_zip("M5V")                       # Canadian FSA: city/province/coords
client.distance("90210", "10001")             # great-circle distance + bearing
client.address("200 N Spring St, Los Angeles, CA 90012")  # validate + ZIP+4
client.suggest("9021")                         # autocomplete -> result["matches"]

# Batch (up to 100; requires a paid subscription key)
client.zip_batch(["90210", "10001", "M5V"], include="timezone")
```

## Errors

Failures raise typed exceptions carrying the API's `code`, `status`, and `request_id`:

```python
from zip_codes_api import InsufficientCreditsError, RateLimitError, NotFoundError

try:
    client.zip("90210", include=["acs_economic"])
except RateLimitError as e:
    print("retry after", e.retry_after, "seconds")
except InsufficientCreditsError:
    ...
except NotFoundError:
    ...
```

`ZipCodesClient` retries 429 / 5xx automatically (honoring `Retry-After`), up to
`max_retries` (default 2).

## Links

- API docs: <https://www.zip-codes.com/api/docs>
- Get a free key: <https://www.zip-codes.com/api/signup>
- Sample data + notebooks: <https://github.com/ZIP-Codes-API/acs-by-zip>
- Research/journalism/nonprofit credits: <https://www.zip-codes.com/api/research-credits>

## License

MIT. Underlying demographic data is US Census Bureau ACS (public domain). Suggested
acknowledgement:

> Demographic data: US Census Bureau ACS 5-Year Estimates, accessed via ZIP Codes API
> (Zip-Codes.com), https://www.zip-codes.com/api/
