Open-source project
censusreporter/censusreporter avatar
censusreporter/censusreporter

Census Reporter: a Django front door onto American Community Survey data

Census Reporter is a Knight News Challenge-funded project to make it easier for journalists to write stories using information from the U.S. Census bureau.

840 stars141 forksHTMLMIT

At a glance

What is it?
Census Reporter is a Django application that gives journalists profile pages, geographic comparisons and a geo_ids based API over American Community Survey tables. It is a fork-friendly codebase with a MIT licence, aimed at newsrooms rather than analysts.
Who is it for?
Adopt Census Reporter if you build newsroom tools on United States Census data and want the awkward parts of it solved once: geographies resolved to identifiers, ACS release and estimate choices made explicit, and a visual front end you can point a reader at. Do not adopt it for non-United States data without forking, since the profile, map and table vocabulary is tied to the Census Bureau, and do not treat it as a general statistics platform.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 20 days ago.
What is it written in?
Mainly HTML, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 25, 2026, and from our analysis. They are not legal advice.

Editorial analysis

Built for a reporter on deadline, not for a data analyst

The United States Census Bureau publishes a large volume of data about the American people, from demographics to poverty rates to educational attainment, at geographic levels running from the whole country down to city blocks, and the data itself is free. The difficulty is not access but interpretation, particularly for journalists who do not work with Census datasets regularly and who are writing to a deadline.

Census Reporter exists to close that gap. Its focus is the American Community Survey, and the stated goals are to help people understand what the survey covers and to find data for places they care about quickly. The project received funding from the Knight News Challenge, and primary development ran from March 2013 through June 2014. The audience is therefore specific: reporters and editors, plus the developers who build tools for them, and the alternative audience of civic technologists who want to point a reader at an interactive map instead of a table of numbers.

Profiles, comparisons and topical overviews

The public site is organised around three page types, and each one maps to a different question a reporter asks. Geographical profiles give an overview of data indicators for one place. Data comparisons use tabular, map and distribution formats to show how a table behaves across a group of geographies. Topical overviews document the concepts and tables the ACS uses for a subject area, which is the part that answers the question of what a table actually measures before anyone quotes it.

The implementation is split across two repositories. The website code is the Django application in this repository, with the backend under censusreporter/ and the front end sources under js-src/. The data service lives in a separate census-api project, and the guide points developers there for the full endpoint documentation. There is also an embed.censusreporter.org directory in the tree, which suggests the ability to drop a chart or profile into a story page rather than sending readers away to the site.

ACS estimates: why 1-year, 3-year and 5-year are different answers

The guide explains the subtlety that trips up newcomers, and it is worth internalising before quoting a number. The ACS publishes three datasets per year. The 1-year dataset uses 12 months of data and produces estimates for areas with at least 65,000 residents. The 3-year dataset uses 36 months and covers areas with at least 20,000 people. The 5-year dataset uses 60 months and covers areas of all sizes.

So a given table can have a different answer depending on the estimate type, and the choice is not cosmetic. The endpoint accepts latest as a release code, which asks for the most current estimate from the most recent release year and favours 1-year over 3-year over 5-year. A call like this one returns data for Spokane, WA from the Sex By Age table:

bash
    https://api.censusreporter.org/1.0/data/show/latest?table_ids=B01001&geo_ids=16000US5367000

Because Spokane has more than 65,000 residents, the response comes from the ACS 2012 1-year release. You can pin the estimate yourself by replacing latest with a code of the form acs{year}_{estimate}yr, for example acs2012_5yr for the same table and geography drawn from the 5-year release. The endpoint guide also lists endpoints for geography metadata, geography parents, geography search and table search.

Local setup with uv and a deliberately non-standard layout

The development instructions use uv to manage the Python environment, and the first step is a clone and a sync:

bash
    >> git clone [email protected]:censusreporter/censusreporter.git
    >> cd <your cloned repo dir>
    >> uv sync

uv sync reads pyproject.toml and uv.lock and creates a local .venv automatically. The next step is not optional, and the reason is stated plainly: Census Reporter uses a non-standard layout, so the app and its settings have to be on the Python path and Django has to be told which settings module to use. The pyproject.toml confirms this, marking the uv project as package = false so the environment is managed without building the project as an installable package.

bash
    export DJANGO_SETTINGS_MODULE='config.dev.settings'
    export PYTHONPATH='censusreporter:censusreporter/apps'
    >> uv run python manage.py runserver

One warning matters for anyone using the Vagrant setup that sits in the tree. If you run the Django server from inside the vagrant box, the localhost has to be given explicitly:

bash
    >> uv run python manage.py runserver 0.0.0.0:8000

With that in place the application is reachable at localhost:8000. The important caveat for evaluation is that this local copy hits the production data sources, so it queries the live Postgres through the public API. The guide notes you can run the API locally and even load the entire Postgres database, but that is a separate amount of work than the quick start above.

A container image pinned to Python 3.11

The Dockerfile shows how the site is actually deployed. It starts from python:3.11, copies the uv binary from the official standalone image at version 0.11.23, copies pyproject.toml and uv.lock before the source so the dependency layer caches, and installs with a frozen lock:

dockerfile
RUN uv sync --frozen --no-dev

The image then puts the virtualenv on PATH, points Django at the production settings module and sets PYTHONPATH, and collects static files before starting the server:

dockerfile
CMD gunicorn --workers 10 --bind 0.0.0.0:$PORT --statsd-host telegraf.web:8125 --statsd-prefix censusreporter censusreporter.config.prod.wsgi

That single line is where most of the production story sits: ten gunicorn workers bound to the port supplied by the environment, with statsd metrics shipped to a telegraf host. The Python version is a deliberate constraint rather than an accident. pyproject.toml requires-python is >=3.11,<3.12 and the comment explains why: the pinned redis==3.5.3 imports distutils, which was removed in Python 3.12. Upgrading the runtime means upgrading that dependency first, not editing a version string.

A Parcel bundle with MapLibre and Turf for the maps

The front end is a separate JavaScript package called @censusreporter/aggregate, version 1.0.0, built with Parcel rather than a bundler from the Python side. Two scripts cover the loop:

json
        "start": "parcel watch js-src/aggregate.jsm --hmr-port 34471",
        "build": "parcel build js-src/aggregate.jsm"

The build output is written to censusreporter/apps/census/static/js/aggregate.js, which is how a JavaScript bundle ends up inside the Django static tree. The runtime dependencies tell you what the maps are built on: maplibre-gl for rendering, @turf/turf for geometry work, proj4 and epsg for coordinate systems, shapefile for reading boundaries and jszip for archive handling. The package licence field reads ISC, while the repository as a whole is MIT.

That combination is worth noting for anyone who expects to paste a Leaflet map. MapLibre and Turf are a different set of primitives from the classic Leaflet plus jQuery approach, and the coordinate handling runs through proj4 and epsg rather than a simple WGS84 assumption.

When to fork instead, and when Census Reporter is the wrong tool

The clearest example of an alternative to using Census Reporter as it ships comes from the README itself. Developers in South Africa forked the repository to build Wazi, a site exploring South African data. The difference in approach is blunt: rather than adapting an application built around Census Bureau geographies, tables and release codes, they replaced the data source and kept the code. That is the pattern to follow for any country other than the United States.

For United States work there are clear cases where it is the wrong tool. It covers the ACS, which means decennial census tables, the Current Population Survey and the many other Census Bureau programmes are out of scope. It does not update itself faster than the ACS release cycle, so a story about a figure published last week will wait for the agency. And the entire front end assumes a reader arriving at a place, which suits a local or regional beat but is a poor fit for a national story about a single household statistic.

The codebase itself is not the constraint. It is MIT licensed, the guide invites forks and contributions, and the last push was on 2026-10-06, though the repository has no GitHub releases, so there is no tagged version to pin.

Editorial conclusion

Adopt Census Reporter if you build newsroom tools on United States Census data and want the awkward parts of it solved once: geographies resolved to identifiers, ACS release and estimate choices made explicit, and a visual front end you can point a reader at. Do not adopt it for non-United States data without forking, since the profile, map and table vocabulary is tied to the Census Bureau, and do not treat it as a general statistics platform. Verify three things before you build: which ACS estimate your audience can actually use, since the 1-year, 3-year and 5-year datasets cover different population thresholds; where the Postgres data load and the census-api service sit, since the local setup hits production data sources; and which Python version your deployment pins, because the project requires 3.11 and below and explains that redis==3.5.3 imports distutils, which Python 3.12 removed. The licence is MIT and the last push was on 2026-10-06.

Frequently asked questions

What is Census Reporter and who is it for?

It is an open source application for journalists writing stories with data from the United States Census Bureau, focused on the American Community Survey. It received Knight News Challenge funding, and primary development ran from March 2013 through June 2014.

How do I run Census Reporter locally?

Clone the repository, run uv sync to create the local .venv, export DJANGO_SETTINGS_MODULE='config.dev.settings' and PYTHONPATH='censusreporter:censusreporter/apps', then start the server with uv run python manage.py runserver. The site is then reachable at localhost:8000.

What is the difference between the ACS 1-year, 3-year and 5-year estimates?

The 1-year dataset uses 12 months of data for areas with at least 65,000 residents, the 3-year dataset uses 36 months for areas with at least 20,000 people, and the 5-year dataset uses 60 months for areas of all sizes. The latest release code favours 1-year over 3-year over 5-year.

Can I use Census Reporter for data outside the United States?

The application is built around Census Bureau geographies and ACS tables, so it does not handle other statistical agencies directly. The README describes how developers in South Africa forked the repository to build Wazi for South African data, which is the route for other countries.

Which Python version does Census Reporter require?

pyproject.toml sets requires-python to >=3.11,<3.12, pinned to 3.11 to match the production image. The comment in that file explains that the pinned redis==3.5.3 imports distutils, which was removed in Python 3.12.

Official sources

  1. censusreporter/censusreporter on GitHub
  2. Issues
  3. License: MIT
  4. Project website
  5. README
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/censusreporter-censusreporter.svg)](https://hysenlabs.com/projects/censusreporter-censusreporter)

Was this useful?