API docs

Read-only REST over published impact fee schedules. Base URL https://levy.thrain.ai. All endpoints are GET; responses are JSON.

Auth

Send your key on every request: Authorization: Bearer <key>. Quota rides the response headers (x-quota-day, x-quota-remaining). No key yet? Request one.

Quickstart

curl -H "Authorization: Bearer $KEY" \
  "https://levy.thrain.ai/v1/fees/schedules?state=FL&land_use_class=single_family&unit=per_dwelling_unit&as_of=2026-08-29&limit=5"

Each row carries the jurisdiction (GEOID, name, type, state), the fee as the jurisdiction names it plus its normalised category, the printed land-use label and its class, the unit as printed and normalised, the amount and the exact printed token, the stated effective window, any footnote or extractor note, and source_url + source_page — the .gov document and page the line was read from.

Endpoints

EndpointReturns
/v1/fees/schedulesFee lines, filterable, paginated (limit ≤ 500, offset)
/v1/fees/schedules/{schedule_key}One fee line
/v1/fees/jurisdictionsPer-jurisdiction rollup: lines, categories, service areas, effective range
/v1/fees/statsDataset totals
/v1/fees/changesThe change feed: fee lines by the run that first observed them. Poll with since= (date or ISO timestamp, exclusive); filter state, jurisdiction_geoid, fee_category, change_type (baseline = corpus at feed start, added = observed later). Newest first.
/v1/fees/indexThe Levy Index: national series by default, state=XX for a state, since=YYYY-MM-DD to trim. Methodology travels with the rows
/v1/fees/index/categoriesThe index by fee category: how many panel jurisdictions levy each category and the middle of that charge

Filters on /v1/fees/schedules

ParamMeaning
state2-letter state code
jurisdiction_geoidCensus GEOID: 5-digit county or 7-digit place
fee_categorytransportation, water, wastewater, stormwater, parks, schools, fire, police, ems, library, general_government, housing, power, other
land_use_classNormalised class, e.g. single_family, multifamily, retail, industrial (land_use_label keeps the printed wording)
unitNormalised charging unit, e.g. per_dwelling_unit, per_1000_sqft, per_meter
amount_kindcollected (what is charged) or max_assessable (a published ceiling)
basisflat or formula (formula lines state a rate × lookup; amount is null)
service_areaExact service-area name as printed; citywide means the whole jurisdiction
as_ofYYYY-MM-DD — only lines in force on that date, keeping the latest where a later schedule restates one (the explorer's rule). Without it, every published line is returned, superseded and future included.
effective_since / effective_untilBounds on effective_start
min_amount / max_amountBounds on the printed amount
source_idOne registry source
sort, dirjurisdiction (default), effective_start, amount, state; asc/desc

Fields on a fee line

Every field ships on every tier. Amounts are the printed figures; nothing is computed.

schedule_keyStable id of the line: a hash of jurisdiction, fee, service area, land-use tier, unit and effective date
jurisdiction_geoidCensus GEOID: 5-digit county or 7-digit place
jurisdiction_typecounty or place
jurisdiction_nameCensus name, e.g. Glendale city
state_code2-letter state code
levying_entityWho levies the fee when it is not the jurisdiction itself (a school district, a utility); null otherwise
service_areaAs printed; citywide means the whole jurisdiction
fee_categoryNormalised category (see the filter above)
fee_nameThe fee's printed name
land_use_classNormalised land-use class (see the filter above)
land_use_labelThe land-use tier as printed
unitNormalised charging unit, e.g. per_dwelling_unit
unit_rawThe unit as printed
amount_kindcollected or max_assessable
basisflat or formula
amountThe printed dollar figure; null on formula lines
amount_rawThe printed token, e.g. $1,282
effective_startWhen the line took effect
effective_endWhen a later schedule superseded it; null while in force
footnote_textThe printed footnote attached to the line, if any
notesCollection notes, e.g. where an effective date was taken from when the body does not print one
source_idThe registry source the line came from
source_urlThe document the line was read from
source_pagePage number within that document
fetch_timestampWhen the document was last fetched (UTC)
first_seen_timestampWhen this line first appeared (UTC)
last_seen_timestampWhen this line was last observed (UTC)

The change feed

curl -H "Authorization: Bearer $KEY" \
  "https://levy.thrain.ai/v1/fees/changes?since=2026-08-01&change_type=added&state=FL"

Append-only: every fee line, stamped with the run that first observed it. Watched-market jurisdictions are re-checked daily, so a newly adopted schedule shows up here within a day of detection. Store the newest first_seen_at you have seen and pass it back as since=.

The index

The impact fee on one single-family home, month by month. Per jurisdiction the stack is the in-force per-dwelling-unit fees, citywide, collected, without water, wastewater or power. A jurisdiction is in the panel when its stack covers at least three fee categories. The level is the panel median; the chained index links month to month on the common panel from its base month, so coverage growth moves the level but never the index. Nothing is a figure a jurisdiction did not print; the index sums them and says so.

index_keyStable id of the (as-of date, scope, scope key) row
as_of_dateMonth-end the index is computed for (today, for the current month)
scopenational or state
scope_keyUS, or the 2-letter state code
panel_sizeJurisdictions in the panel that month: their in-force stack covers at least panel_min_categories fee categories
median_totalMedian total single-family impact fee across the panel, dollars
p25_total25th percentile of panel totals
p75_total75th percentile of panel totals
mean_totalMean of panel totals
common_panelJurisdictions present in both this month and the previous one
link_ratioThe common panel's aggregate fee this month over last month
base_dateFirst month the panel reached 10 jurisdictions; the chained index is 100 there
chained_index100 at base_date, linked forward by link_ratio; null before the base
chain_gapMonths since the base with no common panel, where the index was carried flat
panel_min_categoriesThe panel rule in force when the row was built
category_keyCategories view: stable id of the (as-of date, scope, scope key, category) row
fee_categoryCategories view: the normalised category
jurisdictions_levyingCategories view: panel jurisdictions with a fee in this category
share_levyingCategories view: jurisdictions_levying / panel_size
median_amountCategories view: median of the category charge across those jurisdictions
p25_amountCategories view: 25th percentile
p75_amountCategories view: 75th percentile
conformed_timestampWhen the row was built (UTC)

Errors

StatusMeaning
400Bad parameter; the message names it
401Missing or unknown key
403Key revoked
405Anything but GET
429Daily quota exceeded