Texas TDLR License Scraper — Lookup, Verify & Business Leads
Pricing
$3.00 / 1,000 per license returneds
Texas TDLR License Scraper — Lookup, Verify & Business Leads
Texas TDLR licences from the state's own open-data registry. Verify a licensee by licence number, business or owner name, or pull the 99,720 contactable businesses in the 983,494-row file with phone, street address, county and expiry. 86 licence types and 254 counties as dropdowns. No login, no key.
Pricing
$3.00 / 1,000 per license returneds
Rating
0.0
(0)
Developer
Scrapers Delight
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
a day ago
Last modified
Categories
Share
Texas trade and establishment licences straight from the state's own registry at data.texas.gov, published by the Texas Department of Licensing and Regulation. One row per licence, carrying licenseNumber, licenseType, licenseSubtype, businessName, ownerName, phone, businessAddress, businessCity, businessState, businessZip, businessCounty, mailingAddress, mailingCity, mailingZip, mailingCounty, expirationDate, daysUntilExpiry and continuingEducationFlag.
Two jobs, one Actor: verify a licensee by licence number, business name or owner name, or pull a lead list filtered by licence type, county, city, ZIP and renewal window. No login, no API key, no CAPTCHA solving, no browser automation — it reads the state's public JSON feed.
983,494 licences, 86 licence types, 254 Texas counties. 99,720 of those rows (10.1%) carry a phone number — measured live against the registry on 2026-08-12, not quoted from a marketing page. Those 99,720 are the licensed businesses; the other 883,774 are individual worker registrations that TDLR publishes with no contact data at all.
$0.003 per licence returned — $3 per 1,000. No per-run fee, no monthly platform fee from this Actor.
{"licenseTypes": ["Electrical Contractor"],"counties": ["HARRIS", "DALLAS"],"maxItems": 100}
Click Try for free and hit Start — that is the input the Actor ships with. It returns 100 Harris/Dallas electrical contractors with phone and street address in about 4 seconds, for $0.30.
Quick start
- Sign up for an Apify account (free credit included) and open this Actor.
- Press Start. The prefilled input already works; you do not have to edit anything.
- To go wider: clear Counties, pick more Licence types from the dropdown (each entry shows its measured row count and whether it carries contact data), raise Max results.
- To verify instead of prospect: paste licence numbers, or a business/owner name, into the Verify specific licensees section.
- Download from the Dataset tab as CSV, Excel, JSON or XML, or pull it over the API.
Time from a cold account to a downloaded CSV: a couple of minutes. No code required.
Call it from code (Apify API — swap in your token and the input you want):
from apify_client import ApifyClientclient = ApifyClient("<YOUR_APIFY_TOKEN>")run = client.actor("scrapersdelight/tdlr-texas-license-scraper").call(run_input={"licenseTypes": ["Full Service Establishment"],"counties": ["BEXAR"],"maxItems": 500,})for row in client.dataset(run["defaultDatasetId"]).iterate_items():print(row["businessName"], row["phone"], row["expirationDate"])
import { ApifyClient } from 'apify-client';const client = new ApifyClient({ token: '<YOUR_APIFY_TOKEN>' });const run = await client.actor('scrapersdelight/tdlr-texas-license-scraper').call({licenseNumbers: ['17000'],licenseTypes: ['Electrical Contractor'],});const { items } = await client.dataset(run.defaultDatasetId).listItems();console.log(items[0]);
# single-licence verification, synchronous, results straight backcurl -X POST "https://api.apify.com/v2/acts/scrapersdelight~tdlr-texas-license-scraper/run-sync-get-dataset-items?token=<YOUR_APIFY_TOKEN>" \-H 'Content-Type: application/json' \-d '{"licenseNumbers":["17000"],"licenseTypes":["Electrical Contractor"]}'
The wedge: the whole file is filterable server-side, and the dropdowns are the real lists
Every enumerable filter in this Actor ships as a dropdown built from the live registry, with the measured row count and a ✅/❌ contact-data marker on every entry:
- 86 licence types —
Full Service Establishment — 35,584 rows · ✅ 35,566 with phone,Apprentice Electrician — 253,611 rows · ❌ no phone/address. - 255 county values —
HARRIS — 15,462 contactable of 161,937, including TDLR's ownOUT OF STATEbucket (49,395 rows, 2,642 contactable).
That means you can see what a slice is worth before you spend anything, and a mistyped licence type cannot silently return zero rows. A custom licence type you type is validated against the live registry before the walk starts, and a city or ZIP that empties the result stops the run with the real names it found instead of handing back an empty dataset.
The filtering itself happens server-side at data.texas.gov, so a county × trade × expiry slice costs one HTTP request, not a 983,494-row download you then filter yourself.
Read this before you run it
- Most of TDLR's file is not contactable. 983,494 licences; 99,720 (10.1%) have a phone,
108,037 (11.0%) have a street address.
withContactOnlytherefore defaults to true. The four biggest licence types — Apprentice Electrician (253,611), Cosmetology Operator (197,266), Cosmetology Manicurist (70,377), A/C Technician (59,405) — are individual worker registrations at 0.0% phone. Nobody sells you 983,494 rows here, because 883,774 of them have no way to contact anyone. - A TDLR licence number is not a unique id. On a contiguous 10,000-row walk, 2,236 rows
(22.4%) carried a licence number that had already appeared under a different licence type.
Licence number
109alone returns 7 rows across 6 licence types — including two different "Service Contract Provider" #109 businesses. Always readlicenseNumbertogether withlicenseTypeandbusinessName. - Verification lookups deliberately ignore the contact-only filter. If you paste a licence number and TDLR publishes no phone for it, you still get the row. A lookup that returned nothing would read as "not licensed", which is the wrong answer.
- The registry is a periodic snapshot, not a live feed. When checked on 2026-08-12, Socrata reported the file's last data update as 2026-07-16. TDLR publishes no refresh schedule. Expect the export to lag the board's live record by weeks — this is fine for lead generation and worth stating out loud if you use it for compliance.
- 5.6% of rows have no licence type at all. 54,932 of 983,494 rows have
license_typenull in TDLR's own file. Any run that filters by licence type excludes them by definition.
What you get
One row per licence. All dates are ISO-8601 (YYYY-MM-DD); daysUntilExpiry is a signed integer
computed at run time and goes negative for already-expired licences.
Identity
| Field | Type | Notes |
|---|---|---|
licenseNumber | string | TDLR licence number. Unique only within a licence type — see gotcha #2 |
licenseType | string | One of TDLR's 86 types, e.g. Electrical Contractor |
licenseSubtype | string | null | TDLR's short code, e.g. EC, CS, REG |
businessName | string | null | The licensed business |
ownerName | string | null | Owner, stored LAST, FIRST for individuals |
Contact
| Field | Type | Notes |
|---|---|---|
phone | string | null | Normalised from TDLR's raw 17135622862 to (713) 562-2862 |
hasContact | boolean | true when a phone was published — the flag withContactOnly filters on |
Business address
| Field | Type | Notes |
|---|---|---|
businessAddress | string | null | Street line 1 |
businessAddress2 | string | null | Suite/unit line, sparse |
businessCity | string | null | Parsed out of TDLR's single CITY ST ZIP column |
businessState | string | null | Two-letter |
businessZip | string | null | ZIP5 or ZIP+4, as published |
businessCounty | string | null | TDLR's own county value, or OUT OF STATE |
Mailing address
| Field | Type | Notes |
|---|---|---|
mailingAddress, mailingCity, mailingState, mailingZip, mailingCounty | string | null | Often a PO box where the business address is a storefront |
Licence status
| Field | Type | Notes |
|---|---|---|
expirationDate | string | null | ISO-8601 |
daysUntilExpiry | integer | null | Negative = already expired |
continuingEducationFlag | string | null | TDLR's Y/N CE flag |
Provenance
| Field | Type | Notes |
|---|---|---|
sourceUrl | string | https://www.tdlr.texas.gov/LicenseSearch/ |
scrapedAt | string | ISO-8601 UTC timestamp of the run |
Field fill — measured on real runs
Table A — 13,501 rows, all licence types, contact-bearing, run on Apify 2026-08-12. This is a
contiguous walk in license_number order across the whole contactable corpus, so it mixes salons,
electrical contractors, recyclers, elevator contractors and the rest.
| Field | Fill |
|---|---|
licenseNumber, licenseType, businessName, ownerName, phone, expirationDate, daysUntilExpiry, continuingEducationFlag, hasContact, sourceUrl, scrapedAt | 100.0% |
businessAddress, businessCity, businessCounty, mailingCounty | 99.1% |
| businessState, businessZip | 98.5% |
mailingAddress, mailingCity | 98.2% |
mailingState, mailingZip | 97.5% |
licenseSubtype | 92.9% |
businessAddress2 | 4.7% |
Table B — 10,000 Full Service Establishments (salons and spas), run on Apify 2026-08-13. Plan against this one if you are buying a single establishment type.
| Field | Fill |
|---|---|
licenseNumber, licenseType, licenseSubtype, businessName, ownerName, phone, expirationDate, daysUntilExpiry, continuingEducationFlag, hasContact | 100.0% |
businessAddress, businessCity, businessCounty, mailingCounty | 99.8% |
| businessState, businessZip | 99.8% |
mailingAddress, mailingCity | 98.3% |
mailingState, mailingZip | 97.7% |
businessAddress2 | 11.3% |
The headline that could mislead you: phone is 100% only because withContactOnly is on. Turn
it off and phone fill across the whole 983,494-row file is 10.1%, street address 11.0%. The
filter is not hiding a data problem — it is the entire reason the product is honest about its size.
Across the whole contactable corpus (all 99,720 rows, counted server-side 2026-08-12): business street address 99.3%, business city/state/ZIP 99.3%, mailing address 92.2%, licence subtype 98.5%.
Phone normalisation: 13,496 of 13,501 rows (99.96%) normalised cleanly to (NNN) NNN-NNNN. The
5 that did not are kept exactly as TDLR published them because they are not ten digits —
9419252990 X 20, 8005626463x2131, 95482995952. Nothing is invented and nothing is dropped.
Every run prints its own row count and request count in the log.
How to run it
Bulk lead pull (the default)
{"licenseTypes": ["Full Service Establishment", "Mini Establishment"],"counties": ["BEXAR"],"withContactOnly": true,"maxItems": 2000}
Salons and spas in Bexar County with phone and street address. Cost: up to $6.
Verify one licensee, or a batch of them
{"licenseNumbers": ["17000", "109", "508016"],"maxItems": 100}
Look up many in one run instead of starting a run per lookup — billing is unchanged, you are
charged per record returned, not per number submitted. The contact-only filter is ignored here, so
you see the licence even when TDLR publishes no phone for it. Add a licenseTypes entry to narrow
a reused number to the trade you care about.
Search by business or owner name
{"businessName": "TORRES ELECTRIC","licenseTypes": ["Electrical Contractor"]}
Case-insensitive substring, run server-side. businessName and ownerName combine with OR with
each other and with licenseNumbers; the licence-type and location filters apply with AND on
top.
Territory slice by city or ZIP
{"licenseTypes": ["Electrical Contractor"],"cities": ["SPRING", "KATY"],"maxItems": 500}
City matching is exact on TDLR's CITY ST ZIP column, so SPRING returns Spring and not Spring
Branch. zipCodes takes 5-digit ZIPs and matches ZIP+4 forms too. Paste a whole territory list in.
Renewal-trigger monitor
{"licenseTypes": ["Electrical Contractor"],"expiringWithinDays": 60,"maxItems": 1000}
Only licences expiring between today and 60 days out. This filter runs server-side, so the
"matched your filters" count in the log is the real number and you are never billed for a row
outside the window. Point an Apify Schedule at it weekly and you have a standing renewal feed.
For already-expired licensees instead, leave expiringWithinDays at 0 and filter the dataset on
daysUntilExpiry < 0 — 2,552 of the 13,501 rows in Table A (18.9%) were already past expiry.
Continuing a run that stopped at your budget
Rows are charged as they are pushed. If a run hits its Max total charge it stops on a whole row,
logs how many it delivered, and tells you so — it does not half-bill a dataset. Raise the cap in the
run options, or lower maxItems, and re-run with the same filters.
Sample rows
A real row from the default input, pulled 2026-08-13:
{"licenseNumber": "17000","licenseType": "Electrical Contractor","licenseSubtype": "EC","businessName": "TORRES ELECTRICAL SERVICE","ownerName": "TORRES ELECTRICAL SERVICE","phone": "(713) 562-2862", // normalised from TDLR's raw 7135622862"hasContact": true,"businessAddress": "11910 DOVER STREET","businessAddress2": null,"businessCity": "HOUSTON","businessState": "TX","businessZip": "77031-2516","businessCounty": "HARRIS","mailingAddress": "11910 DOVER ST","mailingCity": "HOUSTON","mailingState": "TX","mailingZip": "77031-2516","mailingCounty": "HARRIS","expirationDate": "2026-07-31","daysUntilExpiry": -13, // negative: this licence has already expired"continuingEducationFlag": "N","sourceUrl": "https://www.tdlr.texas.gov/LicenseSearch/","scrapedAt": "2026-08-13T02:03:57.957Z"}
A salon row from the 10,000-row Full Service Establishment run:
{"licenseNumber": "508016","licenseType": "Full Service Establishment","licenseSubtype": "CS","businessName": "MARY'S BEAUTY SHOP","ownerName": "MARY'S BEAUTY SHOP","phone": "(281) 328-1667","businessAddress": "12218 LLOYD","businessCity": "CROSBY","businessState": "TX","businessZip": "77532","businessCounty": "HARRIS","mailingZip": "77532-2611","expirationDate": "2027-02-28","daysUntilExpiry": 199,"continuingEducationFlag": "Y","hasContact": true}
And what a verification lookup on the reused number 109 actually returns — 7 rows, 6 licence
types, two different "Service Contract Provider" businesses, two with no phone at all:
109 | Boiler Authorized Inspection Agency | CINCINNATI INSURANCE CO., THE | null | OUT OF STATE109 | Barber School | EAST TEXAS BARBER SCHOOL INC | (903) 593-4424 | SMITH109 | Licensed Breeder | TNT KENNEL | (214) 477-3811 | VAN ZANDT109 | Electrician Apprenticeship Program | ASSOCIATED BUILDERS & CONTRACTORS SO TEX | (210) 342-1994 | BEXAR109 | Service Contract Provider | MERCURY SELECT MANAGEMENT COMPANY INC | (405) 621-6179 | OUT OF STATE109 | Tow Truck Operator (Consent Tow) | BRANDL, CONNIE | null | VICTORIA109 | Service Contract Provider | FIRST GUARD WARRANTY CORPORATION | (512) 502-1496 | TRAVIS
Fields people misread:
daysUntilExpiryis signed.-13means expired 13 days ago;nullmeans TDLR published no expiry date on that row.phone: nullmeans TDLR publishes no phone for that licensee, not that a fetch failed.hasContactis the flag to filter on.businessCountymay be the literal stringOUT OF STATE— that is TDLR's own value for a licensee with a non-Texas business address, not a parsing failure.businessAddressis a storefront;mailingAddressis frequently a PO box. They differ on plenty of rows and both are published.
Input
Fields are listed in the order they appear in the Console.
| Field | Type | Default | What it does |
|---|---|---|---|
licenseTypes | multi-select | ["Electrical Contractor"] | 86 TDLR licence types, each labelled with its measured row count and a ✅/❌ contact-data marker. Empty = every type. Picking from the list is typo-proof |
licenseTypeCustom | string | "" | Escape hatch for a type not yet in the dropdown. Validated against the live list — a typo errors immediately with the closest real names |
counties | multi-select | ["HARRIS","DALLAS"] | 254 Texas counties plus OUT OF STATE, each labelled with its contactable count. Empty = statewide |
cities | string list | [] | Business cities, exact match on TDLR's CITY ST ZIP. 1,910 distinct Texas cities appear on the contactable rows |
zipCodes | string list | [] | 5-digit business ZIPs; matches ZIP+4 too. 3,405 distinct ZIP5s on the contactable rows |
licenseNumbers | string list | [] | Batch verification. Switches off the contact-only filter for the run. Billed per record returned, not per number submitted |
businessName | string | "" | Case-insensitive substring of the business name. OR'd with the other two lookup fields |
ownerName | string | "" | Case-insensitive substring of the owner name (LAST, FIRST) |
withContactOnly | checkbox | true | Keep only rows with a phone — 99,720 of 983,494. Ignored in verification lookups |
expiringWithinDays | integer | 0 | 0 = off. 1–365 = only licences expiring in that window. Applied server-side, so out-of-window rows are never fetched and never billed |
maxItems | integer | 100 | Hard cap on rows returned, and therefore your cost ceiling: 100 = $0.30, 1,000 = $3 |
proxyConfiguration | proxy | off | Off on purpose — see the transport section |
The licence types that carry contact data
42 of TDLR's 86 licence types publish at least one phone number. These are the ones worth pulling for lead work (rows / of which contactable, measured 2026-08-12):
Full Service Establishment 35,584 / 35,566 · Mini Establishment 21,616 / 21,612 ·
Electrical Contractor 13,917 / 13,911 · Manicurist/Esthetician Establishment 7,690 / 7,690 ·
Devices 13,518 / 5,128 · Esthetician Establishment 4,768 / 4,763 ·
Water Well Driller/Pump Installer 1,740 / 1,709 · Manicurist Establishment 857 / 855 ·
Appliance Installation Contractor 835 / 835 · Used Auto Parts Recycler 740 / 740 ·
Eyelash Extension Establishment 666 / 666 · Electrical Sign Contractor 654 / 653 ·
Registered Accessibility Specialist 645 / 640 · Cosmetology Private School 640 / 637 ·
Service Contract Provider 577 / 574 · Water Well Driller/Pump Installer Apprentice 1,002 / 459 ·
Professional Employer Organization 432 / 432 · Elevator Contractor 365 / 361 ·
Distributor/Wholesaler 331 / 331 · Cosmetology Vocational/High School 257 / 257 ·
Cosmetology CE Provider 235 / 235 · Licensed Breeder 185 / 185 ·
Electrician CE Provider 184 / 184 · Mobile Establishment 169 / 168 · Barber School 132 / 131 ·
Service Company 103 / 103 · Hair Weaving Establishment 85 / 85 · Supplier 85 / 85 ·
A/C CE Provider 79 / 79 · Electrician Apprenticeship Program 70 / 70 · plus twelve smaller ones.
Devices deserves a note: it is a site registration held mostly by convenience stores and fuel
retailers (LILLY FOOD 2, MURPHY EXPRESS #8751, QUICK MART), and it is the one large type where fill
is partial — 5,128 of 13,518 rows (37.9%) carry a phone.
The big licence types with no contact data at all
Mostly individual worker registrations. TDLR publishes a name and an expiry date and nothing else — no phone, no address, on any row of any of them:
Apprentice Electrician 253,611 · Cosmetology Operator 197,266 · Cosmetology Manicurist 70,377 ·
A/C Technician 59,405 · Cosmetology Esthetician 47,683 · Journeyman Electrician 44,623 ·
Class A Barber 30,405 · A/C Contractor 20,323 · Master Electrician 19,745 ·
Cosmetology Manicurist/Esthetician 12,136 · Tow Truck Operator (Incident Management) 11,856 ·
Licensed Behavior Analyst 9,023 · Tow Truck Operator (Consent Tow) 6,999 ·
Cosmetology Eyelash Extension Specialist 5,471 · Apprentice Sign Electrician 5,294 ·
Property Tax Appraiser 3,962 · VSF Employee 3,504 · Property Tax Consultant 2,663 ·
Residential Wireman 2,535 · Appliance Installer 2,508 · Auctioneer 1,839.
Note that A/C Contractor (20,323) and A/C Technician (59,405) are both in this list: TDLR
publishes no phone and no address for any of them. If you came here for an HVAC contact list,
this file does not contain one — the contactable trades here are the electrical, cosmetology,
appliance, water-well, elevator, accessibility and auto-recycling ones.
Pricing
$0.003 per licence returned — $3 per 1,000. Charged on the license-scraped event. No per-run
fee and no monthly platform fee from this Actor.
| Run | Rows | Cost |
|---|---|---|
| Default smoke test (Harris + Dallas electrical contractors) | 100 | $0.30 |
| Every electrical contractor in Texas | 13,917 | $41.75 |
Every salon and spa in Texas (Full Service Establishment) | 35,584 | $106.75 |
| The whole contactable corpus | 99,720 | $299.16 |
$5 of Apify credit is about 1,666 licences.
What you are not charged for:
- Rows dropped as duplicates. Deduplication happens before the push, so the dataset — and your bill — never holds the same published record twice.
- Rows outside your
expiringWithinDayswindow, your county, your city or your ZIP. Every filter runs server-side at data.texas.gov, so those rows are never even fetched. - A run that matches nothing. It throws before writing a row, so nothing is delivered and nothing is billed.
- The pre-run count query and the filter-validation queries. Those are ours, not yours.
Rows are charged as they are pushed (Actor.pushData(items, 'license-scraped')), so if you hit
a budget cap you get whole rows and stop, not a half-billed dataset. Verified on 2026-08-13: a run
asked for 20,000 rows, hit its $40.32 max-total-charge, stopped at 13,439 rows, charged exactly
13,439 events, wrote exactly 13,439 dataset items, and said so in the log.
maxItems is your hard cost cap (default 100).
Honest limits
- Only 10.1% of TDLR's file is contactable. 99,720 rows of 983,494 carry a phone; 108,037 (11.0%) carry a street address. This is what the state publishes, not a scraping shortfall — the registry's four biggest licence types are individual workers with 0.0% contact fill. If you need contactable people rather than businesses, this file cannot give them to you and no Actor reading it can.
- There are no email addresses. TDLR publishes none, for any licensee, in this dataset. Anyone
selling you "TDLR emails" generated them somewhere else.
websiteis not published either. The handle to enrich from isbusinessName+businessAddress+phone. - There is no licence status column. This export carries an expiry date, not an
active/suspended/revoked flag, and no disciplinary or complaint history.
daysUntilExpiry < 0tells you a licence has lapsed in the snapshot; it does not tell you a licence was revoked. For a status determination, use TDLR's own licence search. - The snapshot lags. Socrata reported the file's last data update as 2026-07-16 when checked on 2026-08-12, and TDLR publishes no refresh schedule.
- 5.6% of rows carry no licence type (54,932 of 983,494) and 3.8% no business county (37,776 rows). Filtering by type or county excludes them.
- TDLR misspells its own cities. 692 of the 1,910 distinct city spellings on the contactable
rows appear exactly once, and some are plainly typos —
HOUTON,HOUSTPN,HOUSONandHOUSTONTXput 8 rows outside aHOUSTONcity filter on the contactable slice, 15 across the whole file, and there are further variants (HOUSOTN,HOUSTN,HOUSWTON,HOUSTON TEXAS). If a city slice matters, cross-check withcountiesorzipCodes, which are cleaner. - We do not emit latitude/longitude, and that is deliberate. TDLR ships a geo point on 98.5% of
contactable rows, but it is a city centroid: 400 sampled Houston rows spread across 87 different
ZIP codes all carry the identical coordinate
-95.36952, 29.76078. Publishing that as a business location would be misleading, so this Actor drops it. GeocodebusinessAddressyourself if you need real coordinates. businessStateandbusinessZipcome from parsing one column. TDLR stores the city, state and ZIP together asHOUSTON TX 77031-2516. 99.1% of contactable rows parse cleanly; the 180 rows (0.2%) that do not keep the raw string inbusinessCitywithbusinessStateandbusinessZipnull, rather than guessing.- No login, no API key, no CAPTCHA solving, no browser automation. There is nothing to defeat here and this Actor does not pretend otherwise — it reads a public open-data endpoint.
How it works, and the transport ladder
One endpoint: GET https://data.texas.gov/resource/7358-krk7.json, the Socrata open-data mirror of
TDLR's full licence file. The Actor:
- Reads the live
license_typeandbusiness_countylists and validates whatever you selected or typed against them, before spending anything. - Builds a single SoQL
$wherefrom your licence types, counties, cities, ZIPs, lookup terms, contact filter and expiry window, and asks for acount(*)first so the log states the real match count. - Walks pages of up to 1,000 rows with
$order=license_numberpinned, pushing and charging each page as it lands.
Why $order is not optional: Socrata does not guarantee a stable row order across pages unless
you pin one, and offset paging without it can silently repeat and skip rows.
Why the expiry filter is an explicit date list: license_expiration_date_mmddccyy is stored as
text in MM/DD/CCYY, so a range comparison would sort lexicographically and be wrong. The Actor
sends the actual calendar dates in your window as a SoQL IN (…) list — exact, and it keeps the
pre-run count honest.
Transport, measured through Apify
data.texas.gov is not walled: no login, no key, no anti-bot, no geo gate. So the default is the
cheapest possible rung — no proxy at all.
| Rung | Result |
|---|---|
| Apify container, direct, no proxy — three runs on 2026-08-12/13 | 44 content requests, 44 successes = 100%, zero retries |
| Apify Proxy | The first build of this Actor died on proxy responded with 590 UPSTREAM502 while the direct request succeeded. Left available for anyone who needs a fixed egress IP, off by default |
| Home broadband, direct (a labelled non-rung — no Apify container has a home IP) | 20 contiguous pages, 20/20 successes. Reported, then ignored |
Sustained load, on Apify: 20,000 rows in 20 content requests in 12.4 seconds of walking (2026-08-12, 1 GB container), then a 10,000-row run that finished in 7.9 seconds wall-clock end to end, then 13,439 rows in 14 requests. No retries fired in any of the three. That is roughly 1,600 rows/second, and it is why there is no concurrency or delay knob in the input: the bottleneck is not the site.
Uniqueness and dedupe
Measured on contiguous page walks — sparse offset sampling proves nothing here.
license_numberalone is not unique. On a contiguous 10,000-row walk, 2,236 rows (22.4%) repeated a licence number already seen under a different licence type.license_number+license_typeis not unique either. On a contiguous 20,000-row walk it collided 43 times — and every single one of those 43 collisions was two genuinely different businesses.Service Contract Provider#109 is both MERCURY SELECT MANAGEMENT COMPANY and FIRST GUARD WARRANTY CORPORATION. A number+type dedupe key would silently delete real licensees from your dataset.- The key this Actor uses is the row's full published identity — number, type, subtype, business name, owner name, phone, street, city, ZIP and expiry. Over the same 20,000 contiguous rows it found 0 duplicates, and on the 13,501-row Apify run it correctly kept the 35 number+type collisions that were distinct businesses.
- Deduplication happens before the push, so a duplicate is never charged and never counts
against
maxItems. - Across runs, dedupe by
licenseNumber+licenseType+businessNameon your side. Two runs with the same filters return the same rows; there is no cursor to resume from because the source is a static snapshot, not a stream.
When a run fails
This Actor is built to fail loudly rather than hand back a quiet empty dataset.
- A licence type or county that TDLR does not use stops the run before anything is billed, with
the live list. Typing
Electrical Contractrerrors with: "Unknown licence type(s). TDLR publishes 86 licence types. Closest live names: Apprentice Electrician | Apprentice Sign Electrician | Electrical Contractor | Electrical Sign Contractor …" - A city or ZIP that matches nothing is checked individually and the error names near matches: "city 'HOUSTONN' matches nothing in TDLR's file — did you mean: HOUSTON, HOUSWTON, HOUATON …"
- Zero matching rows throws, printing the exact SoQL filter in force so you can see which clause emptied it. Nothing is written and nothing is billed.
- Zero rows emitted despite a non-zero pre-run count throws, because that would mean something broke between the count and the walk.
- A run stopped by its budget cap says so explicitly and reports how many whole rows it delivered — it never claims rows it did not write.
- Socrata occasionally 502s. Each request retries up to four times with backoff before the run fails.
There is no "legitimate zero" mode in this Actor: if it returns nothing, that is an error and it tells you why.
Who buys this
- Beauty-industry suppliers and salon software — Boulevard, Vagaro, GlossGenius, distributors of
professional product.
Full Service Establishment(35,566 contactable),Mini Establishment(21,612) andManicurist/Esthetician Establishment(7,690) is a 64,868-row Texas salon list with a phone on every row. - Electrical wholesalers and trade SaaS — ServiceTitan, Housecall Pro, Jobber, supply houses. 13,911 licensed electrical contractors with a street address to route a rep to.
- Insurance and surety underwriters — verify a Texas trade licensee by number or business name before binding, in one API call, without a portal seat.
- General contractors and marketplaces vetting subs — paste the licence numbers off a bid packet and get back type, county, owner and expiry, including for licensees with no published phone.
- Renewal and re-engagement marketers —
expiringWithinDaysis a dated trigger, anddaysUntilExpiry < 0is a lapsed-licensee list (18.9% of a mixed 13,501-row sample). - Auto recyclers and parts platforms — every one of the 740
Used Auto Parts Recyclerlicences carries a phone. - Elevator, accessibility and water-well specialists — small, complete national-scale-impossible lists: 361 elevator contractors, 640 registered accessibility specialists, 1,709 water-well drillers, all with contact data.
Which of our licence Actors to choose
| Actor | What it is | Why you would use it instead |
|---|---|---|
| California Contractor License Scraper (CSLB) | California contractor licences | You need California, and you want bond and workers-comp data — which TDLR does not publish |
| Florida Contractor License Scraper (DBPR) | Florida DBPR licence records | Same job, Florida |
| NPI Registry Scraper | US healthcare providers | You want clinicians nationwide rather than one state's trades |
| City Business License Monitor | New business licences by city | You want new openings as a trigger, not the standing roster |
| New Liquor License Monitor | New bar/restaurant licences | Hospitality openings specifically |
Use this Actor when the answer has to come from Texas's own registry — either because you are verifying a specific licensee, or because you want the trades and establishments TDLR regulates with a phone number attached.
FAQ
Does this need an account, a login or an API key?
No. data.texas.gov is a public open-data endpoint. The Actor sends no credentials and logs into
nothing.
Do I need a proxy? No, and it is off by default. Three Apify runs made 44 content requests direct with 44 successes and zero retries. Turn it on only if you need a fixed egress IP.
Can I get the whole registry in one run?
Yes — clear the licence types and counties and set maxItems high. All 983,494 rows with
withContactOnly: false; the 99,720 contactable ones with it on (about $299). Watch your run's
Max total charge setting, which will stop the run cleanly before maxItems if it is lower.
How much does 1,000 rows cost?
$3. The rate is $0.003 per licence returned, charged on the license-scraped event.
Do I get charged for rows my filters remove? No. Every filter runs server-side at data.texas.gov, so filtered-out rows are never fetched. A run that matches nothing throws before writing anything and bills nothing.
Two runs — will I get duplicates?
Within a run, no: duplicates are dropped on the row's full published identity before the push, so
they are never billed. Across runs with the same filters you get the same rows, because the source
is a static snapshot. Dedupe on licenseNumber + licenseType + businessName on your side.
Why does one licence number return several rows?
Because TDLR reuses licence numbers across licence types — 22.4% of rows on a 10,000-row walk did
so, and even number+type is reused by different businesses. Add a licenseTypes value to narrow it.
Does it return email addresses or websites? No. TDLR publishes neither in this dataset, for any licensee. Nothing can produce them from this source.
Does it tell me whether a licence is active, suspended or revoked?
No. The export carries an expiry date, not a status flag, and no disciplinary history. Use
daysUntilExpiry for lapsed-in-snapshot, and TDLR's own licence search for a status determination.
How fresh is the data? Socrata reported the file's last data update as 2026-07-16 when checked on 2026-08-12. TDLR publishes no refresh schedule, so treat it as a snapshot that lags by weeks.
Can I schedule it?
Yes. Point an Apify Schedule at it with expiringWithinDays set and you get a standing weekly feed
of licences coming up for renewal.
Will a run ever succeed with zero rows? No. Zero matches throws, with the exact filter that emptied the result printed in the error.
Something looks wrong — how do I debug it? The run log prints the SoQL match count before the walk, the emitted count after every page, and the total request count at the end. If a filter looks wrong, the error messages name the live values the registry actually publishes.
Does it work on the Apify Free plan? Yes. The default input returns 100 rows in about 4 seconds for $0.30.
Legal and fair use
This Actor reads a public open-data endpoint published by the Texas Department of Licensing and Regulation on the State of Texas open-data portal. It does not log in, does not solve CAPTCHAs, does not run browser automation, and collects nothing behind any authentication.
Texas professional licence records are public record. Some rows are sole proprietors and individuals trading under their own name, so a row can constitute personal data — treat it accordingly. You are responsible for complying with TDLR's and data.texas.gov's terms and with how you use the data, including TCPA and state telemarketing rules for phone outreach, CAN-SPAM for email, and GDPR/CCPA where they apply to you.
TDLR® and the Texas Department of Licensing and Regulation are marks of their owner. This Actor is an independent tool and is not affiliated with, endorsed by or sponsored by TDLR or the State of Texas.
Changelog
| Version | Date | Change |
|---|---|---|
| 0.1.4 | 2026-08-13 | Licence-number / business-name / owner-name verification lookups; city and ZIP filters; licence type and county became live-validated dropdowns with measured counts; expiry filter moved server-side; dedupe key widened to the row's full identity after measuring that number+type dropped 43 real businesses per 20,000 rows; budget-cap stop now reports honestly instead of over-claiming; maxItems default 5,000 → 100 |
| 0.1.0 | 2026-08-11 | First release: licence type and county filters, contact-only mode, expiry window |
Verified against the live registry on 2026-08-13.
Feedback
Found a missing field, want a filter that is not here, or hit a licence type the dropdown does not carry? Open an issue on the Issues tab — they get read. If the Actor saved you time, a rating on the store page helps other people find it.
Keywords: texas tdlr license scraper · tdlr license search · texas license lookup · tdlr license verification · texas electrical contractor list · texas electrician license lookup · texas cosmetology license · salon owner leads texas · texas salon list · texas spa leads · data.texas.gov api · texas licensing and regulation data · texas business license data · electrical contractor leads · appliance installer leads · water well driller texas · elevator contractor texas · used auto parts recycler texas · registered accessibility specialist · texas contractor verification · subcontractor vetting texas · license expiration monitoring · renewal trigger leads · texas trade license api · tdlr open data · texas licensee phone numbers · harris county contractors · dallas county contractors · bexar county salons · travis county electricians · texas b2b lead list · licensed business directory texas