A Python toolkit for extracting American Community Survey (ACS) 5-year data from the US Census Bureau API into research-ready files. It handles multi-year, multi-geography batches. It keeps estimates paired with their margins of error and derives proportions, ratios, sums, medians for custom areas, and significance tests with multiple-comparison correction, using Census replicate weights for exact variances where Census publishes them. It converts dollar values to constant dollars with the Census Bureau's index (R-CPI-U-RS), harmonizes tract panels across the 2010/2020 boundary change, and writes tidy panels, Parquet files, codebooks, and replayable provenance metadata.
Calculations are checked against values the Census Bureau publishes itself (profile percentages, comparison-profile inflation adjustments and significance flags, replicate-based MOEs, medians). See Validation.
Multi-year panels are built from concepts, not raw variable codes. ACS codes are renumbered, reused, and redefined between releases, so every (year, code) is matched through a checked concordance, and anything unverifiable stops the run instead of silently mixing measures. See Cross-release concordance.
| Interface | Entry point | Intended user |
|---|---|---|
| MCP server | mcp_server.py (57 tools; --profile core gives 14 for local models) |
Agents in Claude Code, Claude Desktop, other MCP clients |
| Command line | python -m census <command> --json |
Agents and scripts without MCP |
| Python library | census package (plan_extraction, run_batch) |
Notebooks, pipelines |
| Desktop GUI | acs_downloader.py (Tkinter) |
Legacy point-and-click use; maintenance mode |
All four interfaces share one pipeline and the same defaults.
Tested on Python 3.10–3.12 (Linux and Windows CI).
git clone https://github.com/cafeTechne/Census-Research-Engine
cd Census-Research-Engine
pip install -r requirements.txtOr install it as an editable package from the clone (pip install -e ., or pip install -e ".[dev]" for pytest and ruff; the toolkit reads its metadata archive and data files from the repository), which adds the census-acs command (same as python -m census) and census-acs-mcp (the MCP server; census-acs-mcp --profile core). Releases are listed in CHANGELOG.md; each tagged release is archived on Zenodo with a DOI to cite.
Price-index data (R-CPI-U-RS, CPI-U, and C-CPI-U, 2000–2024) ships with the repository, so constant-dollar conversion works out of the box.
Refreshing R-CPI-U-RS (once a year, after BLS publishes a new annual average). BLS publishes the series only as an Excel file and blocks anonymous scripted downloads. Download https://www.bls.gov/cpi/research-series/r-cpi-u-rs-allitems.xlsx in a browser, save it over data/price_indexes/source/r-cpi-u-rs-allitems.xlsx, and run:
python scripts/update_price_indexes.py --r-cpi-u-rs-xlsx data/price_indexes/source/r-cpi-u-rs-allitems.xlsxAlternatively, set BLS_USER_AGENT to an identifying User-Agent that includes a contact email (BLS's requirement for automated clients) and run the script without arguments. A year the index does not cover fails with PRICE_INDEX_UNAVAILABLE.
| Variable | Purpose |
|---|---|
CENSUS_API_KEY |
Census API key (request one); requests without a key are rate-limited |
CENSUS_CACHE_PATH / CENSUS_CACHE_DISABLED=1 |
SQLite query cache location (default .cache/census_cache.db), or disable it |
CENSUS_METADATA_DIR |
Alternate per-vintage metadata archive (default metadata/) |
CENSUS_CROSSWALK_DIR |
Alternate override directory (default crosswalks/) |
CENSUS_PRICE_INDEX_DIR |
Alternate price-index directory (default data/price_indexes/); not checksum-verified, its CSV's SHA-256 is recorded instead |
CENSUS_PRICE_PARITY_DIR |
Alternate Regional Price Parity directory (default data/price_parities/); same treatment |
BLS_USER_AGENT |
Identifying User-Agent for scripts/update_price_indexes.py |
CENSUS_VRE_DIR |
Cache for downloaded Census Variance Replicate Estimate files (default .cache/vre/) |
CENSUS_WWW2_MIN_INTERVAL / CENSUS_WWW2_ATTEMPTS / CENSUS_WWW2_BACKOFF_BASE / CENSUS_WWW2_REJECTED_RETRY_DELAY |
census.gov file downloads (census/http.py): seconds between requests to one host (default 1), attempts on HTTP 429/5xx or connection errors (4), backoff base in seconds (2; doubled per retry, capped at 60, honours Retry-After), and the wait before the single retry of a firewall "Request Rejected" page (30). A rejection that persists names the cache path where a browser-downloaded copy of the file is picked up; for a rejected (or 404) B01003 VRE file, fetch_vre instead reads the identical B01001 line 1 and records the substitution (attrs["substitution"], with a warning) |
CENSUS_LIVE_TESTS=1 |
Enable live API contract tests and the live validation battery |
from census import ResearchOptions, build_entities, plan_extraction, run_batch
entities = build_entities("tract", "48", ["453"]) # all tracts in Travis County, TX
options = ResearchOptions(compute_reliability=True, inflation_base_year=2022, include_codebook=True)
plan = plan_extraction("B19013", entities, range(2015, 2023), "parquet", options,
variables=["B19013_001"]) # name the variables you analyze
print(plan["would_block"], plan["selection"], plan["issues"][:3])
result = run_batch("B19013", entities, range(2015, 2023), "out", "parquet", options,
variables=["B19013_001"])
print(result["success"], result.get("error_code"), result.get("panel_path"))Results are dictionaries. Failures carry success: False, an error_code, and error; nothing raises on API failure.
python -m census plan --table DP02 --geo state --state 36 --years 2018-2022 --variables DP02_0016 --json
python -m census extract --table DP02 --geo state --state 36 --years 2018-2022 --variables DP02_0016 --out out --json
python -m census concordance --table B27001 --years 2016-2017 --variables B27001_006 --json
python -m census describe --code DP02_0002PE --year 2018
python -m census extract --table B19013 --geo county --state all --years 2022 --format parquet --out out
python -m census replay out/B19013_extraction_metadata.json --json
python -m census derive proportion --estimates 4500,30000 --moes 400,900 --json
python -m census aggregate --input out/B17001_tract_panel_2023.csv --groups groups.csv --year 2023 \
--derive '[{"name": "poverty_rate", "op": "proportion", "num": "B17001_002E", "den": "B17001_001E"}]'
python -m census validate --offlineExit codes: 0 success, 2 invalid input, 3 concordance review required, 4 API or data failure.
claude mcp add census -- python mcp_server.pyThe server sends instructions that describe the extraction and review workflow, and get_research_protocol returns the full protocol. See docs/claude_setup.md for client configuration and the tool list.
ACS variable meanings drift between releases. The offline audit (docs/audits/concordance_audit.md) covers every profile, subject, and comparison-profile table for 2009–2024. It finds thousands of renumbered lines, reused codes, and changed definitions. Real examples:
| Case | Example |
|---|---|
| Renumbered | DP02 "average household size": DP02_0015 (2018) became DP02_0016 (2019) |
| Code reused | DP02_0002: "family households" (2018), then "married couple family" (2019) |
| Definition changed | B27001_006: "6 to 17 years" (2016), then "6 to 18 years" (2017) |
How it is handled:
- Per-vintage metadata.
metadata/<dataset>/<year>/holds every release'svariables.jsonandgroups.json(gzipped, SHA-256 inmetadata/manifest.json), so labels and matching never depend on a single vintage. - Concordance. Labels are normalized by tested, versioned rules (measurement prefixes, the dollar year, punctuation, part order). Only exact matches merge automatically. Renumbered lines become one column, and reused codes become separate columns.
- Continuity check. Every concept that spans years is compared at the national level. A change that is both significant and implausible blocks the run.
- Selection.
variables=names what the analysis uses. A bare code resolves in the latest requested year,YEAR:CODEpins a vintage, and concept IDs are exact. - Modes.
fail(default) stops when unresolved issues touch selected variables,strictproceeds and reports, andcodeis the legacy raw-code mode. - Reviewed overrides.
crosswalks/<dataset>/<table>.csvrecordsmerge,split, andexcludedecisions, each drafted and then independently reviewed (by an agent or a human) with generated evidence and a census.gov source. The rules are in docs/agent_protocol.md and enforced in code. - Table-scoped rules.
crosswalks/<dataset>/<table>_rules.csvholds reviewed regex rewrites for systematic redesigns (two rules resolve 165 of the 171 S1701 lines changed in 2017). Rules that would change any number in a label are rejected, and merged concepts are still continuity-checked. See docs/rule_overrides.md.
Each multi-year run writes <table>_concordance.csv (every output column traced to its code in each year) and <table>_concordance_issues.json.
geo_level |
Summary level | GEOID |
codes in build_entities |
|---|---|---|---|
us |
010 | US |
none |
state |
040 | SS |
none; state_code required ("all" = 50 states + DC everywhere) |
county |
050 | SSCCC |
county FIPS; default all in state |
tract |
140 | SSCCCTTTTTT |
county FIPS; one request per county; default all |
place |
160 | SSPPPPP |
place FIPS (required) |
zcta |
860 | ZZZZZ |
ZCTAs; default all ZIPs in the state (zip2fips.json); requested in chunks of 50 |
msa |
310 | MMMMM |
CBSA codes; default all metro and micro areas |
block |
100 | SSCCCTTTTTTBBBB |
2020 Census only (dec/pl, or DHC H1): county FIPS; one request per county, default one per state |
dataset |
Endpoint | Tables | Variable suffixes |
|---|---|---|---|
acs5 |
/acs/acs5 |
B…, C… |
E, M, EA/MA annotations |
profile |
/acs/acs5/profile |
DP02–DP05 |
adds PE/PM percentages |
subject |
/acs/acs5/subject |
S… |
E, M per column (_C01_, …) |
cprofile |
/acs/acs5/cprofile |
CP02–CP05 |
codes embed the period; SS significance flags |
dec/pl |
/2020/dec/pl |
2020 Census P.L. 94-171 redistricting data: P1–P5, H1 |
N (count), NA annotation; no MOE |
dec/dhc |
/2020/dec/dhc |
2020 Census Demographic and Housing Characteristics: e.g. P1, P12, H1, H3 |
N (count), NA annotation; no MOE |
The ACS dataset is inferred from the table code. The 2020 Census datasets are selected with dataset="dec/pl" / "dec/dhc" or product="decennial" (DHC); see 2020 Decennial Census.
output_format |
Files |
|---|---|
both, wide_only, melted_only |
Per entity folder: wide compiled_unmelted_data.csv and/or long per-year CSVs |
excel |
Per entity folder: workbook with Wide_Data and Melted_Data sheets |
panel |
<table>_<geo>_panel_<span>.csv, one row per (geoid, year) |
parquet |
The same panel as Parquet (snappy, native dtypes) |
Every run also writes <table>_extraction_metadata.json. It records the request, the package version and git commit, the metadata checksum per vintage, the concordance mode and normalization version, the overrides applied, the price index and its source, the advisories, and the failed calls. include_codebook=True adds <table>_codebook.csv (code, slug, measurement type, description, concept, universe, unit, concept ID, codes by year, concordance status).
| Column | Meaning |
|---|---|
GEOID / geoid |
Standard Census GEOID (TIGER/Line join key) |
Year / year |
ACS 5-year period end year (2022 = 2018–2022) |
<base>E, <base>M |
Estimate and 90% margin of error; in panels, <base> is the concept's latest-year code |
<base>__<first>_<last> |
Concept segment when a code was reused for different measures |
<base>_SE, _CV, _RELIABILITY |
Derived standard error, CV (%), and reliability flag |
*_est, *_moe, *_pct, *_pct_moe, *_se, *_cv, *_rel, *_sig |
Panel slugs; slug_style="code" gives Stata-safe names |
<base>N, *_count |
2020 Census count (and its panel slug); no MOE, SE or CV columns exist |
| Operation | Rule | Function |
|---|---|---|
| Standard error | SE = MOE / 1.645 |
census.stats.standard_error |
| Coefficient of variation | CV = SE / estimate × 100; High ≤ 15%, Moderate ≤ 30%, Low > 30% |
census.stats.coefficient_of_variation |
| Difference test | `Z = | X1 − X2 |
| Sum of estimates | MOE = sqrt(Σ MOE²) over every component, including zeros (Handbook 2020 ch. 8; the older largest-zero-MOE rule is available) |
census.derived.sum_estimates |
| Proportion (subset) | MOE = sqrt(MOE_X² − P²·MOE_Y²) / Y; ratio formula if the radicand is negative |
census.derived.proportion |
| Ratio, product, percent change | Handbook 2020 ch. 8 formulas | census.derived |
| Exact variance | Var = 4/80 · Σ (rep_r − est)² over Census's 80 replicate estimates |
census.replicate, census.variance |
| Median for custom areas | Sum bracket counts, linear interpolation (ACS Subject Definitions); MOE by replicates or an approximation | census.medians |
| Custom geographies | Sum counts, then derive; non-additive measures refused | census.aggregate |
| Multiple comparisons | Holm (default), Bonferroni, Benjamini–Hochberg, Benjamini–Yekutieli | census.multitest |
| Tract harmonization | Σ w·X, MOE = sqrt(Σ (w·MOE)²) with Census 2010/2020 tract relationship weights |
census.geo_crosswalk |
| Sentinel values | Census jam values (-999999999, -666666666, …) become null |
census.stats.clean_census_sentinels |
| Constant dollars | real = nominal × index_base / index_year on dollar-valued estimates and MOEs (not counts in dollar brackets); default R-CPI-U-RS, optional CPI-U and C-CPI-U |
census.inflation.deflate_dataframe |
Price indexes live in data/price_indexes/ and are generated from BLS sources by scripts/update_price_indexes.py, with checksums. Each bundled CSV (and each Regional Price Parity CSV in data/price_parities/) is checked against the csv_sha256 in its sidecar JSON the first time it is loaded in a process; a mismatch raises BundledDataIntegrityError naming the file and both hashes. Tables from CENSUS_PRICE_INDEX_DIR or CENSUS_PRICE_PARITY_DIR are not checked; their SHA-256 is recorded in price_index_info()["custom_table"] (and so in the extraction metadata) or in the RPP adjustment notes. The sidecar JSON files themselves (including provisional values) are not hashed. R-CPI-U-RS values are the workbook's annual averages; the source workbook is kept in data/price_indexes/source/ and its SHA-256 is recorded. CPI-U annual averages are computed from monthly values (they match BLS's published averages to ±0.001). 2025 is omitted because BLS did not publish October 2025; the R-CPI-U-RS workbook's 11-month 2025 average is stored as provisional and used only with allow_provisional_index=True, which adds an advisory. The Census Bureau switched its CPS income reports to C-CPI-U in 2023 but still uses R-CPI-U-RS for all ACS years.
Every derived result records its method (for example proportion, proportion_ratio_fallback, replicate, median_interpolated, apportioned) alongside estimate, moe, se, and cv_percent. Approximation formulas ignore covariance; for estimates that feed a hypothesis test, check check_replicate_availability and prefer replicate variances.
python scripts/validate_against_census.py (or python -m census validate, or the MCP tool run_validation) recomputes numbers the Census Bureau publishes independently and compares them with this toolkit's output. Every check runs offline against frozen fixtures in the normal test suite, and CI runs the live battery weekly. The latest report is docs/audits/validation_report.md.
| Check | Recomputed by the toolkit | Census-published value |
|---|---|---|
| F1 | Pipeline output cells (panel, Parquet) | Raw API cells |
| F2 | Proportions and MOEs from detailed tables | Data Profile percentages and percent MOEs |
| F3 | MOEs from 80 replicate estimates | Published MOEs |
| F4 | Exact replicate proportion MOEs | Data Profile percent MOEs |
| F5 | Medians from distribution tables | Published medians |
| F6 | Prior-period dollars adjusted with R-CPI-U-RS | Comparison Profile values in current dollars |
| F7 | Z-tests between periods | Comparison Profile significance flags |
| F8 | Sums of counties | State estimates |
| F11 | 2020 Census state population; block housing units summed to tracts and counties; DHC vs P.L. 94-171 | Apportionment-release state totals; published tract and county totals (invariants and hierarchy) |
Models and figures that keep ACS sampling error in view, validated by simulation (docs/audits/inference_validation.md):
| Capability | Function / tool | Validation |
|---|---|---|
| Regression with replicate sampling SEs | census.models.fit_replicate / fit_model |
90% intervals cover 0.896 (population target) and 0.895 (these-areas target) |
| Noisy predictors | census.models.regression_calibration |
Attenuation removed; coverage 0.89 |
| Small-area estimation (Fay–Herriot) | census.sae.fay_herriot / small_area_estimates |
Reproduces the published R sae milk example; EBLUP coverage 0.897 |
| Correlations with intervals and replicate SEs | census.correlation.correlation_matrix |
Coverage 0.895 |
| Trend tests on non-overlapping releases | census.trends.linear_trend_test / trend_test |
Size 0.10 at nominal 0.10 |
| Minimum detectable differences, reliability profiles | census.planning |
|
| Journal figures and LaTeX/Markdown tables | census.figures, census.report_tables |
Byte-identical regeneration |
| Pre-registered analyses | census.manifest / create_manifest, run_manifest |
Hash in every output; deviations recorded |
SIMEX is available but under-covers (0.81) and is not recommended for confirmatory use. Statistics use scipy and statsmodels; figures use matplotlib.
Local models. python mcp_server.py --profile core serves a 13-tool profile with short descriptions, summary/next_step in every result, and step-by-step recipes (get_recipe). Qwen3 8B on CPU completed 5 of 6 evaluation tasks (docs/audits/local_model_eval.md); setup in docs/local_models.md.
Built on GeoPandas, Shapely (GEOS), pyogrio (GDAL), pyproj, and PySAL. Outputs are GeoPackage by default, which ArcGIS Pro and QGIS open natively.
| Capability | Function / tool | Notes |
|---|---|---|
| Census boundaries matched to each ACS release | census.boundaries.get_boundaries / get_boundaries |
Cartographic (1:500k) for maps, TIGER/Line for areal work; vintage mismatches are errors |
| Journal maps that show uncertainty | census.maps.choropleth / make_map |
Quantile classes by default; legend reports class separability (Sun and Wong 2010); low-reliability areas hatched; uncertainty and significance maps |
| Spatial autocorrelation | census.spatial.morans_i, local_morans / spatial_autocorrelation |
Reproduces PySAL references; replicate SE of Moran's I is conservative (coverage 0.987); local clusters use two-sided permutation p-values (9999 permutations, BH): false-positive rates at nominal under randomness |
| Spatial regression | census.spatial.spatial_regression / spatial_regression |
Lag (GMM default) and error (ML default) models with LM diagnostics (spreg); simulated coverage under ACS outcome error: rho 0.89-0.90 (GMM lag), lambda attenuated (0.003-0.85). Error model option correct_outcome_error=True, se_col=... corrects lambda for the outcome's sampling error (spatial Fay-Herriot likelihood, REML): lambda coverage 0.86-0.93. It assumes independent errors across areas, which ACS replicates support for rates, shares and medians (covariance audit); it warns for population/household totals and is biased by positively correlated errors (validation) |
| Spatial Fay–Herriot | census.sae_spatial.spatial_fay_herriot / spatial_small_area_estimates |
Reproduces the R sae grapes example; coverage 0.891 |
| Population-weighted tract crosswalks | census.block_weights, harmonize_panel(weight="auto") |
Decennial block weights; Delaware test: mean error 444 vs 624 people with land-area weights |
| Block groups and custom polygons | geo_level="block group", census.custom_areas / custom_area_mapping |
Area or block-population allocation |
| ACS errata and user notes | census.errata.errata_for; in every extraction plan |
258 notes from census.gov with sources |
| Capability | Function / tool | Validation |
|---|---|---|
| PUMS custom tabulations (count, mean, proportion, median, ratio, crosstabs) | census.pums.tabulate / pums_tabulate |
Reproduces all 32 Census PUMS verification estimates and SEs (Wyoming, 2024 1-year and 2020–2024 5-year) |
| PUMS regression with replicate weights | census.pums.pums_regression / pums_regression |
Design-based SEs from the 80 replicate weights |
| ACS 1-year estimates | product="1-year" in plan_extraction, batch_download, run_batch |
Areas of 65,000+ only (others reported); 2020 skipped (no standard release); 1-year vs 5-year comparisons refused |
| Multilevel models | census.multilevel.fit_multilevel / multilevel_model |
Kenward–Roger intervals; coverage 0.89; reproduces the statsmodels dietox reference |
| Difference-in-differences | census.did.difference_in_differences |
Non-overlapping periods only; coverage 0.90–0.91; reproduces Card and Krueger (1994) |
| Cost of living between places | census.rpp.adjust_for_place / adjust_for_place |
BEA Regional Price Parities; reproduces BEA real personal income for all states (2008); never applied by default |
| Interactive maps | census.web_maps.web_choropleth / make_web_map |
Self-contained HTML, no external requests |
The 2020 Census P.L. 94-171 redistricting data (dec/pl) and the Demographic and Housing Characteristics File (dec/dhc) use the same client, cache, extraction pipeline, output formats, codebook and replay as the ACS: pass dataset="dec/dhc" (or "dec/pl"), or product="decennial", to plan_extraction, batch_download, download_table, run_batch or python -m census extract --product decennial, with years=[2020]. Geography levels are nation, state, county, tract, block group, place and block (block only for P.L. 94-171 and the invariant DHC H1 housing-unit count). Variable metadata for both files is archived in metadata/dec/.
from census import ResearchOptions, build_entities, run_batch
run_batch("P12", build_entities("county", "26"), [2020], "out", "panel",
ResearchOptions(include_codebook=True), dataset="dec/dhc")Uncertainty. Census counts are not sample-based, so they have no sampling margin of error; they are also not error-free. Outputs never carry an MOE or CV of 0: no MOE, SE, CV or reliability columns are written (compute_reliability adds none), the codebook's Uncertainty column reads "not sample-based (MOE undefined)", and the extraction metadata has moe_confidence_level: null and an uncertainty record. Every decennial plan carries a disclosure-avoidance advisory:
- the counts carry coverage error (measured by the Post-Enumeration Survey), processing error, and noise added by the TopDown differential-privacy algorithm;
- only three counts are invariant (published without noise): state total population, total housing units per block, and the number of occupied group-quarters facilities by type per block (Census DAS update, 2020-11-25);
- small geographies and detailed cells are noisier, and block-level characteristics should be aggregated before use (fact sheet).
Statistics. z_test, calculate_significance, batch_significance, calculate_cv and acs_overlap_years refuse decennial-only inputs (product="decennial"), since a sampling test says nothing about disclosure-avoidance noise. An ACS estimate can be tested against a census count with product1="5-year", product2="decennial": the count is a fixed reference, and the result's comparison and notes say that only ACS sampling error is measured. derive_estimate(..., product="decennial") returns the point estimate with MOE, SE and CV undefined. build_analysis_frame gives census counts NaN _se/_cv, method not_sample_based, release dec_2020 (acs5_<year>+dec_2020 with a note when mixed with ACS variables). No new estimators are provided for decennial data; validation check F11 compares the files with the Census Bureau's invariants and hierarchy.
- Boundary vintages. Requests spanning 2019 to 2020 at tract, ZCTA, place, county, or CBSA level produce an advisory. Tract panels can be harmonized with
harmonize_tracts_to=2010|2020(land-area weights from the Census relationship file, or NHGIS-style population weights you supply); apportioned MOEs assume independent components and exact weights, and boundary slivers produce tiny weights. Other levels are not harmonized. - Medians from coarse brackets. Household-income medians rebuilt from B19001 are usually within 1% of the published B19013, but can differ by more than 2% because Census interpolates a finer internal distribution.
- Home values. Before the 2022 releases, home values were not on the period end-year dollar basis (ACS user note 2023-07), so deflating them is approximate. Plans include an advisory.
- Subject tables. Their 2013 and 2017 redesigns changed thousands of labels. Multi-year subject pulls usually need
variables=and reviewed overrides. - 2020 Census counts. No margins of error exist; disclosure-avoidance noise dominates blocks and detailed cells. Aggregate before use; see 2020 Decennial Census. Cross-release concordance and continuity checks do not apply (one census year), and the 2010 Census is not offered.
- ZIP codes vs. ZCTAs.
zip2fips.jsonmaps each ZIP to one primary county; PO-box-only ZIPs have no ZCTA.
census/
api.py Census API client, geography predicates, dataset inference
cache.py SQLite response cache
metadata.py Per-vintage variable and table metadata (archive, then API)
concordance.py Label normalization, concordance, issues, override validation
continuity.py National-level continuity check
review.py Draft and record reviewed overrides
pipeline.py plan_extraction, run_batch, build_entities, ResearchOptions
inflation.py Price indexes, deflator, advisories
stats.py MOE, SE, CV, Z-tests, aggregation, sentinel cleaning
variance.py DerivedEstimate and replicate-variance primitives
derived.py Proportions, ratios, products, sums, percent change (Handbook ch. 8)
replicate.py Census Variance Replicate Estimate tables: download, parse, exact variances
medians.py Medians and MOEs from distribution tables
aggregate.py Custom geography aggregation
multitest.py Multiple-comparison corrections, batch significance tests
geo_crosswalk.py 2010/2020 tract apportionment, Connecticut recodes
validation.py Checks against Census-published values
workflows.py File-level workflows shared by the MCP server and CLI
panel.py Slugs, codebooks, tidy panels, Parquet
geo.py, tables.py, export.py, errors.py, __main__.py (CLI)
mcp_server.py MCP server (instructions + 32 tools)
acs_downloader.py Tkinter GUI (maintenance mode)
metadata/ Per-vintage ACS metadata archive with manifest
crosswalks/ Reviewed concordance overrides and their evidence
data/price_indexes/ R-CPI-U-RS, CPI-U, and C-CPI-U with source sidecars
data/geo_crosswalks/ Census 2010/2020 tract relationships and Connecticut recodes
scripts/ fetch_metadata_archive.py, update_price_indexes.py, audit_concordance.py,
fetch_tract_relationships.py, validate_against_census.py
docs/ agent_protocol.md, claude_setup.md, audits/
tests/ unittest suite (pytest); tests/test_live.py is opt-in
python -m pytest -q # offline, including validation fixtures
CENSUS_LIVE_TESTS=1 python -m pytest -q # adds live API and validation tests
python scripts/validate_against_census.py --live # full live validation report
python -m census validate-crosswalksCreated and maintained by cafeTechne. If you use this toolkit in research, cite it using CITATION.cff (GitHub's "Cite this repository" button produces APA and BibTeX) or the concept DOI 10.5281/zenodo.23925301, which always resolves to the latest release (each release also has its own version DOI on Zenodo), and cite the Census Bureau as the source of the data, naming the ACS 5-year release(s) and table(s) used. Forks and derivative works must keep this attribution and the license notice.
Copyright (C) 2023–2026 cafeTechne.
Licensed under the GNU Affero General Public License v3.0 or later. See LICENSE. Contributions require the Contributor License Agreement; see CONTRIBUTING.md.