State trade licence records, with change detection avatar

State trade licence records, with change detection

Pricing

$2.00 / 1,000 licence records

Go to Apify Store
State trade licence records, with change detection

State trade licence records, with change detection

Normalised trade-licence records built from the licensing authority's own published data file, with per-run change flags (new, renewed, expired, status-changed, removed). Official downloads only — no lookup-form automation. Street addresses and telephone numbers are never emitted.

Pricing

$2.00 / 1,000 licence records

Rating

0.0

(0)

Developer

Quietfetch

Quietfetch

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

3 days ago

Last modified

Categories

Share

Normalised trade-licence records built from the licensing authority's own published licence list, read from the state's official open-data API, with a change flag on every row: what is new, what was renewed, what lapsed, what dropped out of the register since your last run.

Spec (10 lines)

  1. What: deduplicated, normalised trade-licence records for one or more US states, sourced from each authority's own published licence list, with per-run change flags.
  2. Buyer: property managers, insurers, marketplaces and lead tools that must verify or monitor licences at scale and cannot poll a lookup form.
  3. Input: programs (which licence types), licenseTypes (optional filter), onlyChanged, includeRemoved, snapshotStoreName, maxRecordsPerProgram, pageSize.
  4. Output: one row per licence — state, authority, program, licenseNumber, licenseType, licenseSubtype, status, statusBasis, businessName, city, stateCode, county, issuedDate, expiresDate, continuingEducation, sourceUrl, datasetPage, sourceFileDate, fetchedAt, recordKey, change, previousStatus, previousExpiresDate, complete.
  5. Pricing: pay-per-event license-record at $0.002 ($2 per 1,000 rows). onlyChanged charges only for rows that moved.
  6. Legal floor: official open data only — the state portal's read-only API, robots.txt and its Crawl-delay checked and obeyed, terms recorded per state. No lookup-form automation, no enrichment, no personal-data resale.
  7. How it works: page through one licence type on the state's open-data API → normalise → diff against your own snapshot of the previous run → write rows with change flags.
  8. Tests: golden normalisation, date and status derivation, paging and robots handling, change detection, and a full run against a local stand-in for the portal. No network needed.
  9. Health: a nightly cheap run reads the first couple of thousand licences of one programme and checks the row shape and the dataset's last-updated date.
  10. Not built: anything that automates a state's individual-verification lookup form. Where an authority publishes no licence list with acceptable terms, that state is simply not supported.

Source

Texas licences come from "TDLR - All Licenses" on the Texas Open Data Portal (https://data.texas.gov/dataset/TDLR-All-Licenses/7358-krk7), published under the attribution of the Texas Department of Licensing and Regulation and refreshed daily. Each programme is one licence type in that list:

ProgrammeLicence type in the sourceLicences (Sept 2026, approx.)
tx-ac-contractorsA/C Contractor20,400
tx-electrical-contractorsElectrical Contractor14,000
tx-water-well-drillersWater Well Driller/Pump Installer1,750
tx-electrical-sign-contractorsElectrical Sign Contractor650
tx-elevator-contractorsElevator Contractor370

Only business (contractor) licence types are offered. The same list also covers individual practitioners; those are deliberately not included.

What is never in the output

Street addresses, telephone numbers, owner names, postcodes and email addresses are never in the output — they are not even requested. The source list carries the licence holder's business and mailing addresses, both telephone numbers, the owner's personal name and a map point. This actor asks the API only for the licence columns it needs, so those fields never cross the wire, never reach the actor, and are not available behind any option. For a sole trader, an address and a phone number are a home address and a personal number, and this actor is not a channel for reselling them.

What is published is the licence: who trades under it, in which town and county, of what type and subtype, and until when. That is what the authority publishes for public verification. The town is taken from the business address block without its postcode, because a town alone is coarse and a town plus a postcode is not.

city is empty wherever the authority publishes no business town — which, in the current source, is every A/C contractor. county is filled for about 96% of those licences and is the better geographic field for that programme.

Status is derived, and says so

The authority's licence list carries no status column — only an expiry date. This actor does not invent one. It sets status to active or expired by comparing the expiry date with the dataset's own last-updated date, and every record carries statusBasis spelling out exactly that comparison. issuedDate is null because the list does not publish one; it is never estimated.

Change flags

changeMeaning
newNot in the previous read (every row on your first run)
renewedExpiry date moved later
expiredNow past its expiry date, and was not before
status-changedStatus moved some other way
updatedTrading name, town or expiry changed without being a renewal
removedWas in the previous complete read, absent from this one
unchangedNothing moved

Two honesty rules apply to the diff:

  • A partial run never reports removals. If you cap records or filter by licence type, everything past the cut would otherwise look as though it had vanished from the register. Such a run is marked complete: false and reports no removals.
  • A partial, refused or failed run never overwrites your snapshot. Otherwise the following good run would report the whole programme as newly added.

A licence whose town or trading name is blank in the source stays unchanged from run to run while nothing about it moves — a blank field is not a change, and you are not charged for it under onlyChanged.

Input example

{
"programs": ["tx-ac-contractors"],
"onlyChanged": true,
"includeRemoved": true,
"snapshotStoreName": "state-license-snapshots"
}

Output example (one row, second run)

{
"state": "TX",
"authority": "Texas Department of Licensing and Regulation",
"program": "Electrical Contractors",
"programId": "tx-electrical-contractors",
"licenseNumber": "31522",
"licenseType": "Electrical Contractor",
"licenseSubtype": null,
"status": "active",
"statusBasis": "derived: LICENSE EXPIRATION DATE (2028-03-01) compared with the dataset's last-updated date (2026-09-26); this authority's licence list publishes no status column",
"businessName": "Lone Oak Electric LLC",
"city": "Waco",
"stateCode": "TX",
"county": "McLennan",
"issuedDate": null,
"expiresDate": "2028-03-01",
"continuingEducation": "N",
"change": "renewed",
"previousStatus": "expired",
"previousExpiresDate": "2026-03-01",
"sourceUrl": "https://data.texas.gov/resource/7358-krk7.json",
"datasetPage": "https://data.texas.gov/dataset/TDLR-All-Licenses/7358-krk7",
"sourceFileDate": "2026-09-26",
"complete": true
}

(The licence number and business in this example are illustrative, not a real record.)

When a programme cannot be read

A programme that is refused, disallowed by robots.txt, returns something other than the expected JSON, or is unreachable produces a single row with outcome: "problem" and the reason, visible in the Problems dataset view. Those rows are never charged for, and nothing is worked around: no proxy rotation, no fingerprint spoofing, no CAPTCHA handling. A refusal is reported and the run moves on.

Pricing (pay-per-event)

EventWhen chargedPrice
license-recordafter a record is written to the dataset$0.002 ($2 / 1,000 records)

A full first read charges once per record — about $41 for the whole A/C contractor programme (~20,400 licences), about $28 for electrical contractors, and under $4 for each of the three smaller programmes. After that, onlyChanged: true charges only for the licences that actually moved. Set a maximum charge on the run if you want a hard ceiling; the actor stops writing, and stops charging, when it is reached.

Attribution

The ATTRIBUTION record in the run's key-value store names, per programme, the authority, the dataset page, the dataset's last-updated date and the date it was copied, together with a statement that no state endorsement is implied. The source authority's site policy asks anyone republishing its web content to carry exactly that — if you republish these records onward, carry it too.

Lawful and responsible use

  • Official open data only. Read-only requests to the state portal's public API for the licence list the authority publishes. No lookup-form automation, no session, no login.
  • robots.txt is fetched for every host and obeyed (QuietfetchBot, then *), including its Crawl-delay. A disallowed path is not fetched, and the run says so.
  • Rate: at least one second between requests to a host, or the host's Crawl-delay if longer. Pages are ordered on the portal's own row id, so no page is fetched twice.
  • Identification: the user agent names the bot and a contact address. Bot token QuietfetchBot; contact page https://quietfetch.com/bots, mailbox hello@quietfetch.com.
  • Optional app token: if you set a SOCRATA_APP_TOKEN environment variable on the actor, it is sent as the portal requests, in a header and never in a URL. It is not needed for normal use.
  • Data: the licence facts an authority publishes for public verification. No street addresses, no telephone numbers, no owner names, no postcodes, no email addresses, no enrichment from other sources. Buyers are responsible for their own use of the output, including any obligations that attach to the source data.
  • Terms: each supported state's terms are recorded verbatim, with their source URL and the date they were read, before that state ships.

Local development

npm test # the full suite, no network required
npm start # a local run (needs the Apify SDK and an input)

Built and maintained with AI assistance under human review.