datajackpotRequest access
Methodology

How we count

Every number we publish is a database count with a unique key behind it. This page documents the actual mechanisms in our pipelines: how records are deduplicated, how record classes are kept separate, how every source load is gated before it reaches the live tables, and what we deliberately leave out of the totals.

45.5M+
Permits
80.5M
Owned parcels
3.3M
Contractor license records
51.9M
Property events

How we deduplicate

Deduplication is enforced by unique database indexes, not by cleanup scripts. Each dataset has a composite key that exists from day one and is never dropped, so a duplicate row physically cannot land:

Rows without an identity are dropped before they are written: a parcel record with no assessor parcel number and a license record with no license number never enter the database at all.

How we separate record classes

Not every construction signal is a permit, and we refuse to blur the line. Every row in the permits dataset carries a record_class that separates true building permits from new_construction_parcel rows, which are new-construction signals derived from assessor parcel data in places where the permit record itself is not publicly available. Parcel-derived rows are never counted, exported, or sold as permits. When we say 45.5M+ permits, that count excludes them.

How we gate every source load

Every bulk source run is logged before it can affect the live dataset. The parcel and contractor pipelines each write a per-run gate log (parcel_source_log and contractor_source_log) recording how many rows the source was expected to yield and how many were actually staged or upserted.

Field values are bounds-checked on the way in. Year-built values outside 1600 to next year, negative valuations, and implausible building sizes are stored as null rather than as fake data points.

How recency works

Freshness is per source, not a site-wide slogan. Each open-data source keeps its own high-water mark: the latest record date it has successfully delivered. The daily ingest cron reads that watermark and fetches only records newer than it, minus a five-day overlap so late edits and amended records at the source are picked up. The watermark only advances after a successful run, and it is never allowed to advance past today, so a source with bad future-dated rows cannot freeze its own delta window.

Sources that only republish weekly are pulled weekly; assessor rolls and archival vintages update on their own longer clocks. Every source writes its outcome to a health table, which is what the data freshness page displays, including the sources that are currently stale.

What we don't count

One definition worth being precise about: 3,309,148 is the count of raw license records across 37 states plus DC. One business holding an electrical and a plumbing license is two license records. After entity clustering, those records resolve to 2,888,145 unique contractor entities, 12.7 percent of rows were duplicate representations of a business already counted.

Clustering works in two passes. First, an exact license-number match within the same (state, source) pair, provided the business names on those records do not conflict. Second, an exact normalized-business-name match within the same state; when the name looks like a person rather than a company, that match additionally requires a matching 3-digit ZIP prefix, since person-like names collide far more often than company names do. The known limitation: two genuinely different companies that share the same name in the same state can be merged into one entity. Clustering is intentionally conservative, and the entity counts are republished as sources refresh.

Want to check our work?

See the live per-source health board on the data freshness page, or ask us for the gate log of any source you care about: henry@datajackpot.net.