PermitParser
FILED &
NORMALIZED
New Haven County, CT

Every town files
differently. We don't.

PermitParser scrapes, parses, and normalizes building permits and zoning filings from fractured municipal portals: PDFs, HTML tables, scanned agendas. All of it lands in one queryable, spatial record. Built to survive fifteen towns' worth of inconsistent government web software.

LIVE FROM /api/v1
municipalities
records parsed
documents ingested
Loading recent filings…
Architecture

Four stages. Every source, the same shape.

A PDF agenda and a JavaScript permit portal fail differently, so the pipeline is built to expect that: a schema-drift landing zone, a content-hash ledger, and a normalization boundary between messy source text and a queryable record.

01. fetch_latest

Discover

Polls a town's listing surface (a table, an agenda index) for documents published since the last run. Never downloads a full body just to check if it's new.

02. download_raw

Dedupe

SHA-256 of the raw bytes before anything touches disk. The same PDF fetched twice, whether from a re-run or an unchanged listing, costs one indexed lookup and never a duplicate row.

03. parse

Normalize

pdfplumber for a real text layer, PyMuPDF + Tesseract OCR when there isn't one. Pure functions with no I/O, validated into a strict Pydantic PermitRecord.

04. upsert

Serve

PostGIS-indexed, queryable by category, valuation, date range, or radius. A rescrape updates a record in place, never a duplicate for the same real-world filing.

Coverage

Live in Branford. Fourteen towns to go.

Every town in South Central Connecticut runs its own GIS, its own permit vendor, its own PDF export format. Each one gets checked and built for real before it's marked live. No placeholder rows.

Live

Branford

Planning & Zoning Commission agendas, CivicPlus AgendaCenter. PDF, statically scrapable.

Researched, not built

New Haven

Portal vendor not yet confirmed against a live scraper.

Researched, not built

Wallingford

Portal vendor not yet confirmed against a live scraper.

Researched, not built

Milford

Portal vendor not yet confirmed against a live scraper.

API

Read layer, not a locked dashboard

Every figure on this page comes from the same public, unauthenticated API. Filter by municipality, category, valuation, date range, or a straight-line radius search.

GET /api/v1/permits?municipality=branford&category=zoning_variance
GET /api/v1/permits?lat=41.28&lng=-72.81&radius_miles=2
GET /api/v1/permits/analytics/velocity?municipality=branford
// live response...
One town at a time

Built to survive fifteen towns' worth of inconsistent government software.

Every design decision, the raw/normalized split, the config-driven scrapers, the honest "not built yet" coverage table, exists to make the next town an addition, not a rewrite.