All Projects
Case Study Personal Project · 2026 Civic Tech · $0 Infra

Who Reps Me?
Every official who represents you, one address at a time.

In April 2025, Google shut down the Civic Information API — the only free, nationwide way to look up a city council member or school board rep. Who Reps Me? rebuilds that lookup from federal to city hall, on exactly $0 a month of infrastructure.

Role
Solo — Design, Build,
Data Pipeline
Timeline
2026
Coverage
24 states ·
83 jurisdictions
Infra Cost
$0/month
Stack
React · Netlify Functions
GitHub Actions

The Problem

Google walked away.
The paid vendors didn't blink.

For over a decade, Google's Civic Information API was the quiet backbone behind most "find your representative" tools on the web — free, nationwide, and the only source that reached down to city council and school board level. In April 2025, Google shut it down.

Every provider left standing charges for it. Cicero and BallotReady both sell commercial civic data licenses — reasonable products, but not something a free public lookup tool can build on. Federal data is still easy (Congress publishes it). State data is workable (most legislatures publish open data). Local data — the level that actually governs zoning, trash pickup, and school boards — had no free nationwide source left at all.

"Nobody chases city council rosters for a living. Every 'free' civic-data product either quietly depended on Google's API, or was never free to begin with — it was federal and state, with a paid upsell to unlock local."

the gap this project rebuilds

24
States with live coverage
83
Jurisdictions indexed
450
Officials in the database
$0/mo
Infrastructure spend

How It Works

Type an address.
Get everyone who represents you.

One search box, three levels of government. Federal senators and representatives, state legislators and executives, and the city council members and school board reps most "find your rep" tools don't even attempt to cover.

Live demo — search by address, then browse results grouped by LOCAL / STATE / FEDERAL.

Data Sources

Four sources.
Zero paid APIs.

No single free source covers all three levels of government, so the pipeline stitches together four of them — one per layer, plus geocoding to route the address to the right district.

🏛️
Federal

US House & Senate

Via 5calls' free public API — no key required, generous limits, built for exactly this use case.

5calls API · free
🏢
State

Legislators & Executives

Via Open States v3 — state house, state senate, governor, and other statewide offices.

OPENSTATES_API_KEY · free
🏙️
Local

City & County Officials

No API exists for this layer. Built an LLM-extraction scraper that reads municipal websites and pulls structured official data out of them.

Own scraper · free
📍
Geocoding

Address → Coordinates

US Census Geocoder first, Nominatim (OpenStreetMap) as fallback whenever Census can't resolve the query.

Census + Nominatim · no key

Architecture

Search data flow.

One address in, three lookups run in parallel, one merged response rendered by level of government. Geocoding is the fork in the road — get it wrong and everything downstream degrades.

flowchart TD A["Address input"] --> B["Geocode"] B -->|"Full street address"| C["US Census Geocoder"] B -->|"Bare ZIP / City fallback"| D["Nominatim"] C --> E["Lat/Lng + district IDs"] D --> E E --> F["5calls: Federal"] E --> G["Open States: State"] E --> H["Local officials: shard or on-demand scrape"] F --> I["Merge results"] G --> I H --> I I --> J["Rendered by section: LOCAL / STATE / FEDERAL"]

Field Notes

The bare-ZIP bug nobody warns you about.

The Census Geocoder's onelineaddress endpoint is an address-range matcher, not a general-purpose geocoder — it needs an actual street number and street name to match against. Feed it a bare city name or a ZIP code alone and it returns zero matches, even though the search box invites "address or zip code." Without a fallback, that silently degrades a bare-city/zip search down to federal-only results: two senators and a representative, no error message explaining why the state legislator and city council never showed up.

1

Detect the shape of the query

Before ever calling the Census Geocoder, the query is classified: a full street address, a bare 5-digit ZIP, or a "City, State" pair. Only a full address goes straight to the address-range matcher.

2

Bare ZIP → zippopotam.us

A 5-digit ZIP with nothing else routes to zippopotam.us, a free lookup that returns a ZIP's centroid lat/lng directly — no street address required, no matching against a range.

3

Bare "City, State" → Nominatim, then Census coordinates

A city-only query resolves through Nominatim (OpenStreetMap's free geocoder) to a lat/lng, which is then passed to the Census Bureau's coordinates endpoint — a different endpoint than onelineaddress, one built to accept raw coordinates instead of a street-range match — to recover the district and jurisdiction data the address endpoint would have provided.

Address autocomplete suggestion dropdown

Autocomplete nudges users toward a full street address before they ever hit the bare-ZIP edge case.

Crowdsourcing

The database that fixes itself.

Local officials data goes stale fast — elections, resignations, redistricting, a city council seat that flips mid-term. Instead of an admin dashboard nobody but me would ever open, corrections route through GitHub, where they're triaged automatically before a human ever has to look.

flowchart TD A["User clicks 'Suggest an official'"] --> B["Submits a link + note"] B --> C["LLM triage"] C --> D["Duplicate of an open
user-reported issue?"] D -->|"Yes"| E["Comment on existing GitHub issue"] D -->|"No"| F["Open new GitHub issue"] E --> G["Appended to tracking file"] F --> G G --> H["Human review"]
Suggest an official submission modal

The crowdsource submit modal — a link and a note is all it takes.

Infrastructure Philosophy

A JSON file is the database.

There's no database server. There's no always-on worker process. A per-state JSON shard, committed to the git repo, is the database — versioned, diffable in a pull request, and free to host. A GitHub Actions cron job is the worker, waking up on a schedule to re-scrape and re-validate a shard. Netlify's static hosting is the server, serving those JSON files at the edge with zero origin compute sitting behind them.

"Every piece of 'infrastructure' this project needed already existed as a free tier of something else. The discipline wasn't finding clever workarounds — it was refusing to provision anything that a file, a cron job, and a CDN could already do."

the $0/month thesis

Coverage

24 states and counting.

Federal and state coverage is nationwide from day one — Open States and 5calls both cover all 50 states. Local coverage is what's still expanding: each state's local-officials shard has to be built, scraped, and verified one jurisdiction at a time.

Washington — Live coverage WA Oregon — Live coverage OR California — Live coverage CA Idaho — Coming soon ID Nevada — Live coverage NV Utah — Live coverage UT Arizona — Live coverage AZ Montana — Coming soon MT Wyoming — Coming soon WY Colorado — Live coverage CO New Mexico — Coming soon NM North Dakota — Coming soon ND South Dakota — Coming soon SD Nebraska — Coming soon NE Kansas — Coming soon KS Oklahoma — Live coverage OK Texas — Live coverage TX Minnesota — Coming soon MN Iowa — Coming soon IA Missouri — Coming soon MO Arkansas — Coming soon AR Louisiana — Coming soon LA Wisconsin — Coming soon WI Illinois — Live coverage IL Kentucky — Coming soon KY Tennessee — Live coverage TN Mississippi — Coming soon MS Michigan — Live coverage MI Indiana — Live coverage IN West Virginia — Coming soon WV Virginia — Live coverage VA North Carolina — Live coverage NC Alabama — Coming soon AL Ohio — Live coverage OH Pennsylvania — Live coverage PA Maryland — Live coverage MD Washington, D.C. — Coming soon DC South Carolina — Coming soon SC Georgia — Live coverage GA New York — Live coverage NY New Jersey — Live coverage NJ Delaware — Coming soon DE Florida — Live coverage FL Vermont — Coming soon VT Massachusetts — Live coverage MA Connecticut — Coming soon CT Maine — Coming soon ME New Hampshire — Coming soon NH Rhode Island — Coming soon RI Alaska — Coming soon AK Hawaii — Live coverage HI

24 of 51 states and territories currently have live coverage: Washington, Oregon, California, Nevada, Utah, Arizona, Colorado, Oklahoma, Texas, Illinois, Tennessee, Michigan, Indiana, Virginia, North Carolina, Ohio, Pennsylvania, Maryland, Georgia, New York, New Jersey, Florida, Massachusetts, Hawaii.

Live coverage
Coming soon

Screens

What it looks like.

LOCAL Section
LOCAL Section
City council, school board, and county officials — the layer with no free API, built from scratch.
STATE Section
STATE Section
State house, state senate, and executive offices, pulled live from Open States v3.
Federal Official Detail
Federal Official Detail
Senator Maria Cantwell's card — committees, bio, and office locations in one view.
Mobile Viewport
Mobile Viewport
Full results at 375×812 — the layout most users will actually see it at.

Outcome

What it produced.

🗳️

450 officials indexed

Federal, state, and local officials, unified into one searchable dataset.

🗺️

24 states covered

Live local-officials coverage, with federal and state coverage nationwide.

🏛️

83 jurisdictions

Cities and counties with a scraped, human-reviewed local officials shard.

💸

$0/month recurring cost

No database bill, no server bill, no API bill — every dependency runs on a free tier.

🚫

0 databases provisioned

JSON shards in git, a cron job, and static hosting replaced the entire backend.

Tech Stack

Frontend

React

Hosting & Functions

Netlify

Automation

GitHub Actions

Data

Open States API 5calls API US Census Geocoder Nominatim

Extraction

LLM-based scraping (Gemini/Groq/OVH)