TX Sales Tax Permits - Active Permit Holders
Pricing
from $0.03 / 1,000 tx sales tax permit records
TX Sales Tax Permits - Active Permit Holders
Texas Comptroller active sales-tax permit register (public data, 887k permits across 702k taxpayers): outlet name and address, NAICS industry code, city-limits flag, permit and first-sales dates - plus the taxpayer's own registration address and county.
Pricing
from $0.03 / 1,000 tx sales tax permit records
Rating
0.0
(0)
Developer
Wenhao Yang
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
a day ago
Last modified
Categories
Share
Low cost
From $0.00005 per record, down to $0.00003 at Gold - pay per record delivered, and nothing for the query. Every active sales-tax permit in Texas, billed by the permit.
The Texas Comptroller's register of businesses permitted to collect sales tax: 887,244 permits held by 702,193 taxpayers, refreshed daily.
What you get
Each row is one outlet permit - a business can hold many.
| who | taxpayer number and registered name, plus the outlet's trade name and number |
| where it sells | outlet street address, city, state, ZIP, county, and whether it sits inside city limits |
| where it is registered | the taxpayer's own mailing address, city, state, ZIP and county |
| industry | the outlet's NAICS code |
| when | permit issue date and first-sales date |
The two addresses are the point
Every row carries two addresses, and they are not copies of each other. One is where the business is registered; the other is where this particular permit is. They disagree on 518,696 rows for the street address - over half the register - and on 259,054 for the county, of which 162,162 are a genuine county-to-county difference (the rest are rows where the taxpayer's county was never reported).
That gap is the product: "registered in Austin, selling in Houston" is answerable here, and a dataset carrying one address would silently tell you the wrong city for more than half of Texas permits.
Modes
- permits (default) - individual permits, newest permit first
- aggregate - one count row per group: by county, NAICS sector, organization type, city-limits status, or permit year
Filter by taxpayer number, business name, city, ZIP, county (by name or Comptroller code), NAICS industry, inside/outside city limits, and issue / first-sales dates.
Example inputs
Every outlet permit one taxpayer holds - taxpayerNumber is the exact 11-digit Comptroller number, so one call returns that business's whole permit list.
{ "taxpayerNumber": "17515166332", "maxResults": 10 }
Full-service restaurants in Houston - naicsCode takes the 6-digit industry, city is a case-insensitive substring.
{ "city": "HOUSTON", "naicsCode": "722511", "maxResults": 10 }
Retail outlets inside Harris County city limits - countyCode=101 is Harris, inCityLimits=Y is the city-limits flag.
{ "countyCode": "101", "inCityLimits": "Y", "maxResults": 10 }
The industry mix - aggregate=true and groupBy=naicsSector roll the register up by NAICS sector.
{ "aggregate": true, "groupBy": "naicsSector" }
Why this is hard
The county is a code, and the codes are numbered the hard way. The Comptroller numbers Texas's 254 counties by name with the spaces removed - so LA SALLE collates as LASALLE and lands after Lampasas, not up with the other La- counties. Sorting the 254 names alphabetically puts ten of them in the wrong slot, which is how a county lookup quietly returns the neighbouring county's permits. This register resolves the code to a county name against a table checked both ways, and the aggregate carries the name alongside the code.
96,896 permit rows (23,820 taxpayers) have no county reported - the code comes through as 000. That is not a missing value to paper over; it is the register saying the county was not given, and it is kept distinguishable from a real county rather than folded into one.
The industry code is a number, not a string. It looks like a text identifier and behaves like one to a reader, but treating it as text fails at the source - every text operation on it is rejected. Groups and prefixes have to be expressed numerically.
And the organization-type field is 62 two-letter codes with no legend anywhere in the dataset. There is no published mapping from CL / IS / CT to anything a buyer could act on, so it is offered only as a count dimension - never as a filter you would have to guess the codes for.
Output
One schema in every mode: each record carries the full field set, empty where the source has nothing, and dates arrive as YYYY-MM-DD. Every record carries sourceUpdatedAt, the register's own last-refresh timestamp. Aggregate rows use the same schema, with groupKey / groupCount filled.
Example output
aggregate=true, groupBy=naicsSector - one row per industry sector; sector 45 is retail trade. Aggregate rows keep the permit schema, with groupKey / groupCount / groupBy filled and the permit fields empty:
{"firstSalesDate": "","inCityLimits": "","naicsCode": "","outletAddress": "","outletCity": "","outletCountyCode": "","outletCountyName": "","outletName": "","outletNumber": "","outletState": "","outletZip": "","permitIssueDate": "","sourceUpdatedAt": "2026-09-19T08:05:21Z","taxpayerAddress": "","taxpayerCity": "","taxpayerCountyCode": "","taxpayerCountyName": "","taxpayerId": "","taxpayerName": "","taxpayerState": "","taxpayerZip": "","platform": "tx-sales-tax-permits","source": "tx-comptroller-sales-tax-permits","groupKey": "45","groupCount": "191979","groupBy": "naicsSector"}
Notes
- Public open data from the Texas Comptroller. No login, no scraping.
- Charges are metered per record delivered, so a single taxpayer lookup costs a fraction of a cent.