Census Loader
A Python wrapper around the U.S. Census Bureau API that abstracts away
variable codes, dataset endpoints, and FIPS identifiers behind a clean,
human-readable interface. Installs as census-loader, for anyone who pulls
Census data into pandas.
Instead of this:
data = census.acs5.get(
("B19013_001E",),
{"for": "county:*", "in": "state:25"},
year=2022
)
You write this:
cfg = Config(
"output.pkl", "./data",
year=2022,
geo="county_in_state",
state="Massachusetts",
series=["MEDIAN_HH_INCOME", "TOTAL_POP", "MEDIAN_RENT"],
)
result = pull_census(cfg)
Setup
pip install census-loader
Or from a checkout of this repository: pip install -e ..
Dependencies (installed automatically by pip; requirements.txt is kept
for reference): pandas, numpy, census, python-dotenv, requests.
Get a free key at
api.census.gov/data/key_signup.html,
then add it to a .env file in your project root:
CENSUS_API_KEY=your_key_here
Quickstart
from census_loader import Config, pull_census
cfg = Config(
"ma_counties.pkl", "./output",
year=2022,
geo="county_in_state",
state="Massachusetts",
series=["TOTAL_POP", "MEDIAN_HH_INCOME", "MEDIAN_RENT"],
)
result = pull_census(cfg) # dict of DataFrames keyed by friendly name
result["Median_Household_Income"]
Exploring the catalog
You never need to open series.py. Three discovery functions browse,
search, and inspect everything from a REPL or notebook:
available(): browse categories;available("INCOME")drills into a category or subcategory.search("poverty"): search by keyword.info("MEDIAN_HH_INCOME"): full details (name, category, dataset, underlying variable codes).geos(): browse geography templates.
Output format
pull_census returns a dict[str, pd.DataFrame] and saves a pickle to
your output path. Each DataFrame has geo columns on the left (FIPS codes +
human-readable names) and data columns on the right. Column naming depends
on the series type (single variable, multi-variable, group table); labels
are fetched automatically from the Census metadata API. pickle_loader
flattens a saved pickle to one DataFrame.
| Series type | Example key | Column names |
|---|---|---|
| Single variable | TOTAL_POP |
Total_Population |
| Multi-variable | HOMEOWNERSHIP_RATE |
Homeownership_Rate__Owner occupied, Homeownership_Rate__Total |
| Group table | HH_INCOME_BRACKETS |
Household_Income_Distribution__Less than $10,000, Household_Income_Distribution__$10,000 to $14,999, ... |
API limits
Free Census API keys allow 500 requests per day with a batch size of 50 variables per request. The loader sleeps 0.5s between calls to stay well under rate limits. Metadata label fetches are not rate-limited and are cached per session.
Reference
The sections below hold the full reference: Config options, series selection, geography templates, flattening into a single DataFrame, worked examples, and the complete category table.
Project layout
Census_Loader/
pyproject.toml
requirements.txt
README.md
.env-example
src/census_loader/
__init__.py # public API re-exports
utils.py # Config, loader, discovery tools
load.py # pull_census entry point
series.py # series catalog, GEO templates, FIPS codes
Config Reference
Config(
filename, # Output filename (auto-corrects to .pkl)
output_path, # Directory for output files
year=2022, # ACS/PEP vintage year
geo=..., # Geography level (see below)
state=..., # State name or FIPS code
county=..., # County FIPS (3-digit)
tract=..., # Tract code
series=..., # What to pull (see below)
batch_size=50, # Max series per API batch
)
Series selection
The series parameter accepts any granularity: individual series,
subcategories, categories, or a mix:
series=None # everything (169 series)
series="TOTAL_POP" # one series
series="INCOME" # whole category (18 series)
series="HOUSEHOLD_INCOME" # subcategory (9 series)
series=["TOTAL_POP", "INCOME", "MEDIAN_RENT"] # mix & match
series=["POVERTY", "HOUSEHOLD_INCOME", "TOTAL_POP"] # category + subcategory + series
Case-insensitive. Overlapping selections deduplicate automatically.
Geography
Pass a template name and fill in the required parameters:
# All states (default)
Config(..., geo="state_all")
# All counties in one state
Config(..., geo="county_in_state", state="Massachusetts")
# Tracts in one county
Config(..., geo="tract_in_county", state="Massachusetts", county="017")
# School districts in a state
Config(..., geo="school_district_in_state", state="Massachusetts")
# All ZCTAs
Config(..., geo="zcta_all")
State names resolve automatically, so "Massachusetts" and "25" both work.
For edge cases not covered by templates, pass a raw dict:
Config(..., geo={"for": "county:017", "in": "state:25"}).
Flattening into a single DataFrame
Since every series in a pull shares the same geography, you can merge on the shared geo columns:
from functools import reduce
import pandas as pd
frames = list(data.values())
shared = set(frames[0].columns)
for df in frames[1:]:
shared &= set(df.columns)
merge_keys = [c for c in shared
if all(not pd.api.types.is_numeric_dtype(df[c]) for df in frames)]
flat = reduce(
lambda left, right: pd.merge(left, right, on=merge_keys, how="outer"),
frames,
)
Examples
# County-level dashboard data for one state
cfg = Config(
"ma_dashboard.pkl", "./output",
year=2022, geo="county_in_state", state="Massachusetts",
series=["TOTAL_POP", "MEDIAN_HH_INCOME", "MEDIAN_RENT", "MEDIAN_HOME_VALUE",
"HOMEOWNERSHIP_RATE", "HH_INCOME_BRACKETS"],
)
pull_census(cfg)
# Tract-level deep dive
cfg = Config(
"middlesex_tracts.pkl", "./output",
year=2022, geo="tract_in_county", state="Massachusetts", county="017",
series=["TOTAL_POP", "MEDIAN_HH_INCOME", "POP_65_PLUS"],
)
pull_census(cfg)
# Full category pull, all states
cfg = Config("national_income.pkl", "./output", year=2022, series="INCOME")
pull_census(cfg)
# School district comparison
cfg = Config(
"ma_schools.pkl", "./output",
year=2022, geo="school_district_in_state", state="Massachusetts",
series=["TOTAL_POP", "MEDIAN_HH_INCOME", "HH_INCOME_BRACKETS"],
)
pull_census(cfg)
Available Categories
| Category | Series | Subcategories |
|---|---|---|
| POPULATION | 13 | TOTAL_POPULATION, AGE_DETAIL |
| RACE_ETHNICITY | 13 | RACE, HISPANIC_ORIGIN |
| NATIVITY_MIGRATION | 11 | NATIVITY, MIGRATION |
| LANGUAGE | 6 | none |
| EDUCATION | 10 | SCHOOL_ENROLLMENT, EDUCATIONAL_ATTAINMENT |
| HOUSEHOLDS | 12 | HOUSEHOLD_TYPE, MARITAL_STATUS, FERTILITY |
| INCOME | 18 | HOUSEHOLD_INCOME, EARNINGS, PUBLIC_ASSISTANCE |
| POVERTY | 12 | ACS_POVERTY, SAIPE_POVERTY |
| HEALTH_INSURANCE | 6 | ACS_HEALTH_INSURANCE, SAHIE_HEALTH_INSURANCE |
| EMPLOYMENT | 15 | EMPLOYMENT_STATUS, OCCUPATION_INDUSTRY, COMMUTING |
| HOUSING | 25 | HOUSING_UNITS, TENURE, HOME_VALUE, HOUSING_INFRASTRUCTURE |
| DISABILITY | 5 | none |
| VETERANS | 4 | none |
| PEP_POPULATION | 6 | none |
| DECENNIAL | 9 | DECENNIAL_REDISTRICTING, DECENNIAL_DHC |
| DATA_PROFILES | 4 | none |
| Total | 169 |
License
Start here
src/census_loader/series.py is the heart of this project: the catalog that
maps every friendly series name onto its Census variable codes, and the
reason the wrapper exists at all. src/census_loader/load.py then shows how
a Config becomes batched API calls and labelled DataFrames, and
tests/test_catalog.py shows the catalog's invariants.
Metadata
Release files for census-loader 0.1.2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| census_loader-0.1.2.tar.gz | 42.9 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| census_loader-0.1.2-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 70.9 kB
Release files / census_loader-0.1.2.tar.gz
| Download URL | census_loader-0.1.2.tar.gz |
|---|---|
| Size | 42.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
aace4321c994439ed92f41e47eda9da3a1994b568db95b1f1d85bbd5e87ea5f5
|
|
BLAKE2b-256 checksum How to use checksums |
592049f96f472ee5da2ead73631c84122b0652f656deb9d4df51380a5471b57c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
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 Sep 18, 2026.
Transparency logRelease files / census_loader-0.1.2-py3-none-any.whl
| Download URL | census_loader-0.1.2-py3-none-any.whl |
|---|---|
| Size | 28.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
c31facbddf145980774cf55d48690fb197f590b3c0e4125ec72910d11cd040e3
|
|
BLAKE2b-256 checksum How to use checksums |
5c0a3aba7dcd92071da9767cb76faaa1d73e4654351fc9448ec08eaee14afc36
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
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 Sep 18, 2026.
Transparency log