TX Tax Fight

Property-Tax ARB Data API

Read-only JSON access to Texas Appraisal Review Board (ARB) hearing results, county coverage, and premium no-show / failed-to-reduce leads. Multi-year, multi-county.

Base URL

https://api.txtaxfight.com

All data endpoints are versioned under /v1. Responses are application/json.

Authentication

Every /v1 endpoint except /v1/health requires an API key. Send it as a Bearer token or an x-api-key header:

Authorization: Bearer YOUR_API_KEY
# or
x-api-key: YOUR_API_KEY

Keys have a scope:

Keys are stored server-side as a SHA-256 hash — the plaintext is never persisted. Contact the operator to be issued a key.

Endpoints

GET/v1/healthopen

Liveness probe. No auth. Returns service status, version, and time.

GET/v1/countiesfeed

Coverage list: every TX county with fips, name, tier (T1/T2/T3), status (LIVE / OPEN / PIA), coverageClass, whether a live hearings feed exists, and registry freshness.

GET/v1/counties/:fipsfeed

County detail + stats. When a hearings feed exists, includes stats (total / reduced / no-change / raised / pending / leads) and feed freshness.

GET/v1/counties/:fips/hearingsfeed

Paginated ARB hearing results: date, disposition, board determination, noticed value, and the cut (dollars off the noticed value).

QueryDefaultNotes
limit50max 500
offset0row offset
GET/v1/counties/:fips/leadspremium

The premium prospect list, classified objectively (no guesswork): owners who failed to reduce — the board determination came in at or above the noticed value (zero dollars off) — or who were a no-show (explicit NS disposition). Each lead carries a reason. Same pagination as hearings. agentOfRecord is returned as null until ARB-filing data is wired — it is never fabricated. A feed-scoped key hitting this endpoint gets 403.

POST/v1/portfolio/processpremium

Upload a book of properties — a JSON array of {ref?, address?, city?, state?, zip?, lat?, lng?} and/or a CSV (raw text/csv or a multipart/form-data file field). Each row is geocoded with the free US Census Bureau geocoder (returns lat/lng + county FIPS), matched to txtaxfight coverage, and classified matched (with county FIPS/name, coordinates, coverage status & tier) or failed (with a human reason). Un-geocodable rows are never assigned a guessed county. Matched rows are grouped by county (sorted by count) as a “what to license” suggestion. This endpoint calls an external geocoder, so it carries its own tighter per-key throttle on top of the normal rate limit. Response: { summary, counties[], properties[], failures[] }.

Rate limits

Exceeding either returns 429 Too Many Requests with a Retry-After header (seconds).

Error codes

StatuserrorMeaning
200OK
401missing_api_key / invalid_api_keyNo key, or key not found / inactive
403insufficient_scopefeed key on a premium endpoint
404county_not_found / not_foundUnknown fips or route
413too_many_properties / payload_too_largePortfolio upload over the row/byte cap
415unsupported_media_typePortfolio body not JSON, CSV, or multipart
429rate_limitedPer-key or per-IP limit hit (see Retry-After)
500internal_errorServer error

curl examples

# health (open)
curl https://api.txtaxfight.com/v1/health

# coverage list
curl -H "Authorization: Bearer $KEY" \
  https://api.txtaxfight.com/v1/counties

# county detail + stats (Bell County)
curl -H "Authorization: Bearer $KEY" \
  https://api.txtaxfight.com/v1/counties/48027

# hearings, paginated
curl -H "Authorization: Bearer $KEY" \
  "https://api.txtaxfight.com/v1/counties/48245/hearings?limit=25&offset=0"

# premium leads (requires a premium key)
curl -H "x-api-key: $KEY" \
  "https://api.txtaxfight.com/v1/counties/48245/leads?limit=25"

# portfolio: geocode + county-match a book of properties (premium)
curl -X POST -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
  -d '{"properties":[{"ref":"a","address":"101 E Central Ave","city":"Belton","state":"TX","zip":"76513"}]}' \
  https://api.txtaxfight.com/v1/portfolio/process
# ...or upload a CSV file:
curl -X POST -H "Authorization: Bearer $KEY" -F "file=@book.csv" \
  https://api.txtaxfight.com/v1/portfolio/process

Data sources

EndpointBacked by
/v1/counties, /v1/counties/:fipstxarb Postgres county registry (254 TX counties)
/hearings, /leadsLive published county feeds (Bell 48027, Jefferson 48245)
/portfolio/processUS Census Bureau geocoder (free, public) + txarb county registry

Coverage expands as counties are onboarded. Counties without a live feed return an empty hearings/leads set with total: 0.