API
A read-only API over HTTPS. Each account holder gets an API key, and the base URL comes with it. New data is ready every morning after the county files are pulled.
Test keys open the sample. Working keys belong to account holders.
How delivery works
- Every morning we pull the county, court and city files, tie each record to its property and owner, and publish the result.
- Each record has an
updateddate, the day it last changed. Ask forsince=the last date you hold and you get only what's new or changed. - Daily change files list the same changes as gzip NDJSON (one JSON record per line), with
{"id": ..., "removed": true}lines for records that dropped off (for example, taxes paid). - Full snapshots of each county and record type are available any time.
Authentication
Send your key in the x-api-key header on every request. Requests without a valid key are refused. Each key has its own rate limit and daily quota, and every request is logged. HTTPS only.
curl -H "x-api-key: YOUR_KEY" "https://BASE_URL/v1/coverage"
Endpoints
| Request | What it returns |
|---|---|
GET /v1/coverage | Counties, record types, counts and dates you can pull |
GET /v1/signals?county=Travis&signal=tax_suit&since=2026-01-01 | Records, newest changes included, up to 500 per page. Pass next_cursor back as cursor= for the next page |
GET /v1/signals?county=Dallas&signal=tax_suit&min_amount=5000&lender_eligible=yes | The same, narrowed by filters. Add count_only=true for just the number of matches |
GET /v1/filters | Every filter, its values, and how many records in each county carry it |
GET /v1/exports/{date} | Short-lived download links (15 minutes) for that day's change files and the current full snapshots. date can be latest |
GET /v1/parcels/{county}/{parcel_id} | One property: owner, exemptions, tax status and every record we deliver for it |
Record types (signal): tax_delinquent, tax_suit, tax_sale, code_violation, foreclosure, bankruptcy, eviction, probate, divorce, civil.
Filters
Filters narrow GET /v1/signals on our side, so you download only the records you want. Every filter you add must match (they combine with AND). Inside zip and property_type, a comma list means any of them.
| Filter | Values | What it means |
|---|---|---|
min_amount, max_amount | dollars, e.g. 2000 | Total taxes owed (tax.taxes_owed). It includes penalty and interest only where tax.penalty_interest_included is true; in other counties it is the base tax, so the payoff is higher |
homestead | yes / no | Homestead exemption |
over65 | yes / no | Over-65 or over-65 surviving spouse exemption |
disabled | yes / no | Disabled-person exemption |
veteran | yes / no | Disabled-veteran exemption |
deferral | yes / no | A tax deferral (Tax Code 33.06) is on file |
lender_eligible | yes / no | lender.lender_eligible: none of the bars to a property tax loan we can see |
has_suit | yes / no | A tax suit was filed (in suit, judgment, or an earlier suit) |
property_type | residential, land, commercial, mobile_home, other | From the appraisal district's state code: residential = houses, multifamily and builder inventory; land = vacant lots, farm and ranch land and rural acreage; commercial = commercial and industrial buildings and land; mobile_home = mobile homes; other = minerals, business personal property, utilities, exempt |
absentee | yes / no | The owner's mailing address is not the property |
out_of_state | yes / no | The owner's mailing address is outside Texas |
min_years_delinquent | whole number, e.g. 2 | At least this many tax years carry an unpaid balance |
zip | 5-digit ZIP, comma list | The property's ZIP code |
count_only | true | Return only the number of matching records, for quoting |
When a record doesn't have the field. Not every county publishes every field (Tarrant's tax file, for example, has no usable exemption codes). A record with no known value for a filtered field is left out, except when the filter is no: then unknown counts as no. So homestead=no keeps records whose exemptions are unknown. GET /v1/filters shows, for each county, how many records have each field.
Paging with filters. Pass next_cursor back as cursor= and keep every other parameter the same. If you change a filter, the old cursor is refused (400); start again without it. A page can hold fewer than limit records; keep going until next_cursor is empty.
Filtered downloads. The daily files from /v1/exports are whole files and can't be filtered. For a filtered download, page through /v1/signals with your filters.
Examples
# How many Tarrant delinquent accounts owe $2,000 or more, have no homestead, and are lender-eligible?
curl -H "x-api-key: YOUR_KEY" "https://BASE_URL/v1/signals?county=Tarrant&signal=tax_delinquent&min_amount=2000&homestead=no&lender_eligible=yes&count_only=true"
# Dallas tax suits on houses, $5,000 or more owed, lender-eligible
curl -H "x-api-key: YOUR_KEY" "https://BASE_URL/v1/signals?county=Dallas&signal=tax_suit&property_type=residential&min_amount=5000&lender_eligible=yes"
# Harris accounts 2+ years behind in two ZIP codes, no over-65 exemption, changed since November 1
curl -H "x-api-key: YOUR_KEY" "https://BASE_URL/v1/signals?county=Harris&signal=tax_delinquent&zip=77084,77449&min_years_delinquent=2&over65=no&since=2026-11-01"
Sample record
Every value below is made up.
{
"id": "0000example0000example0000example0000000",
"signal": "tax_suit",
"county": "Example",
"filed_date": "2026-01-15",
"case_number": "TX-0000-EXAMPLE",
"status": "in_suit",
"amount": 1234.56,
"parties": [{"name": "EXAMPLE OWNER LLC"}],
"property_address": "123 SAMPLE ST",
"match": {"parcel_id": "000000000", "method": "parcel", "confidence": "confirmed"},
"owner": {"name": "EXAMPLE OWNER LLC", "mail_address": "PO BOX 0000", "mail_city": "ANYTOWN",
"mail_state": "TX", "mail_zip": "00000", "absentee": true, "out_of_state": false},
"property": {"situs_address": "123 SAMPLE ST", "property_use": "A1", "year_built": 1999,
"market_value": 100000, "deed_date": "2010-01-01"},
"exemptions": {"codes": ["HS"], "homestead": true, "over_65": false, "disabled": false,
"disabled_veteran": false, "tax_deferral": false},
"tax": {"taxes_owed": 1234.56, "first_year_delinquent": "2024", "suit_status": "in_suit",
"suit_number": "TX-0000-EXAMPLE", "judgment_date": null, "bankruptcy": false, "deferral": false},
"lender": {"lender_eligible": true, "ineligible_reasons": [], "notices": []},
"loans": null,
"source": {"system": "Agentix RE Solutions", "dataset": "county tax records"},
"first_seen": "2026-01-20T11:00:00Z",
"updated": "2026-01-20"
}
source is always {"system": "Agentix RE Solutions", "dataset": ...}, where dataset is the kind of record: county tax records, county court records, county foreclosure notices, city code enforcement records, county property records or bankruptcy court records. A few city code records also carry an attribution line that the city's data license requires.
Loans on the record
Records tied to a property can carry a loans list: what the county records say about the mortgage on it. It comes only from county records: posted trustee-sale (foreclosure) notices, and the lender that escrows the taxes on a tax or appraisal roll. GET /v1/parcels returns the same list, and GET /v1/coverage shows with_loans and with_loan_amount counts for each county and record type.
Every number is {value, unit, source}, and source tells you how we know it:
| Field | What it is |
|---|---|
lender, servicer | As named in the recorded notice or the tax roll |
deed_of_trust_date, deed_of_trust_instrument | From the notice, when it states them |
original_amount | The original principal, only when the notice states it |
rate | A Texas public record never shows the note rate. With a deed of trust date, it is the Freddie Mac Primary Mortgage Market Survey 30-year average for that week, labeled ESTIMATE. With no date, it is 7%, labeled ASSUMPTION: no rate on record |
est_balance | Only when the original amount and date are both known: paid as scheduled over 30 years at the rate above. Labeled ESTIMATE; it ignores missed payments, modifications and arrears |
from_record | The kind of public record the loan came from, as a plain category (for example county foreclosure notices (notice posted 2026-10-05)) |
A record with no county loan facts has "loans": null, which means unknown, not "no loan". Rate estimates: Freddie Mac, Primary Mortgage Market Survey.
{
"id": "0000example0000example0000example0000001",
"signal": "foreclosure",
"county": "Example",
"filed_date": "2026-10-05",
"property_address": "456 SAMPLE DR",
"match": {"parcel_id": "000000001", "method": "address", "confidence": "confirmed"},
"loans": [{
"lender": "EXAMPLE MORTGAGE COMPANY",
"servicer": "EXAMPLE LOAN SERVICING",
"deed_of_trust_date": "2019-06-14",
"deed_of_trust_instrument": "2019-0000000",
"original_amount": {"value": 200000, "unit": "usd", "source": "Stated in the recorded notice"},
"rate": {"value": 3.82, "unit": "pct", "source": "ESTIMATE: Freddie Mac, Primary Mortgage Market Survey (30-year fixed average) for the week of 2019-06-13, when the deed of trust was signed; the loan's actual rate is not on public record"},
"est_balance": {"value": 169444, "unit": "usd", "source": "ESTIMATE: original amount paid as scheduled over 30 years at the rate above (89 payments); ignores missed payments, modifications and arrears"},
"term_years": {"value": 30, "unit": "years", "source": "ASSUMPTION: term not on record"},
"from_record": "county foreclosure notices (notice posted 2026-10-05)",
"record_id": "0000example0000example0000example0000001"
}],
"source": {"system": "Agentix RE Solutions", "dataset": "county foreclosure notices"},
"updated": "2026-11-05"
}
Sample page
{
"county": "Example",
"signal": "tax_suit",
"since": "2026-01-01",
"filters": {},
"total": 6981,
"count": 500,
"next_cursor": "eyJleGFtcGxlIjoidHJ1ZSJ9",
"records": [ ... ]
}
Errors
| Code | Meaning |
|---|---|
| 400 | A parameter is missing or wrong, or a filter isn't one we offer (the message lists the valid ones), or a cursor is reused with different filters |
| 403 | Missing or invalid API key |
| 404 | No such export date or no records for that parcel |
| 410 | The data was refreshed after your cursor was issued. Start again with since= |
| 429 | Rate limit or daily quota reached |
Permitted use
Keys are issued only to businesses that agree to our terms: real estate marketing, lending outreach and research only, and never for credit, employment, insurance or tenant-screening eligibility decisions.
Keys
Keys come in two kinds. A test key is an evaluation courtesy: ask us, and if your operation qualifies we issue one by hand — it opens the sample records and the coverage view, nothing more. Working keys belong to account holders: when you sign and your account is active, your key opens the live feed — the full delivered records for your counties, the daily change files and the full snapshots. There is no self-serve key creation, on purpose: the feed is licensed per customer, and the key is how we hold that line. Ask for a test key, or contact us with any questions.