How-to
How to run Tennessee Contractor License Lookup & Verify (TN DOCI)
Step-by-step: the exact input, what each field does, and the rows that come back from TN DOCI.
The honest starting point: if you have exactly one Tennessee contractor to check, you don’t need this actor. TN DOCI’s own licence search at search.cloud.commerce.tn.gov is public, free, and takes about twenty seconds. This actor is for the case where the lookup is not one lookup — you’re onboarding a list of subs and every one of them needs a status, a licence type and an expiry date in a spreadsheet; you re-check the same roster each quarter because a licence that was current in March expires in June; or you’re enriching a CRM where all you hold is a licence number and a company name. Done by hand that’s one portal search per contractor, a click into the detail record, and a dozen fields copy-pasted into a sheet — then the whole thing redone from scratch the next time somebody asks. This turns the same registry into rows a system can read.
What you’ll need
Nothing but an Apify account. The actor talks to TN DOCI directly.
No API key, no proxy subscription, no CAPTCHA-solving service — TN DOCI’s portal has neither a CAPTCHA nor an anti-bot wall. Its search API does require a bearer token, but the portal mints one anonymously for any caller, and the actor mints a fresh one at the start of each run. You never see it, you never register for it, and nobody bills you for it. So the only cost is the actor’s own per-result fee plus Apify compute, with no third-party bill hiding behind it.
Step 1 — Open the actor
Open Tennessee Contractor License Lookup & Verify (TN DOCI) on the Apify Store and press Try for free. The actor’s own page on this site lists its live health and pricing.
Step 2 — Fill in the input
| Field | Type | Required | What it does |
|---|---|---|---|
query | string | yes | A TN contractor license number (numeric, e.g. ‘56787’) OR a business/person name. A numeric query is looked up by license number; anything else is searched against the… |
board | string | no | Filter results to a specific TN DOCI board. Defaults to ‘Contractors and Ltd Licensed Plumbers’ (the main contractor board). Leave blank to search all boards. (default Contractors and Ltd Licensed Plumbers) |
maxResults | integer | no | Cap on the number of license matches returned in a single run. Defaults to 50. (default 50) |
Only query is required, and how you fill it decides which search runs. The rule is mechanical: if
the string is nothing but digits, the actor treats it as a TN licence number and looks it up by
number; anything else is treated as a business or licensee name and searched against the registry’s
name index. TN licence numbers are purely numeric — 56787, no prefix, no letters — so there’s no
ambiguity to resolve, but it does mean a name with a stray digit in it will still be searched as a
name, and a licence number typed with a leading letter will be searched as one too, and find
nothing. The matched_by column in every output row tells you which of the two searches produced
that match.
board is the field first-timers get wrong. It defaults to Contractors and Ltd Licensed Plumbers
— the main contractor board — and that default is a filter, so if the person you’re checking is
licensed under a different DOCI board (home-improvement contractors, for instance), the default
quietly returns nothing. If a search you expected to hit comes back empty, blank the board field
first: an empty value searches every board. Keep the default when you specifically want general
contractors and don’t want alarm-installer or home-improvement records diluting the results.
maxResults (default 50) is just a bound on how many matches a run will return — the actor pages
through results 50 at a time and stops there, so raise it if you’re searching a common name like
“Smith” and want the whole set.
A working input:
{
"query": "Johnson Construction",
"board": "Contractors and Ltd Licensed Plumbers",
"maxResults": 25
}
Step 3 — Run it
Press Start. Rows are pushed to the dataset as they’re found, so the run log fills in as it works.
A healthy run is short. This is a JSON API talking to another JSON API, with no browser to boot and
no pages to render, so a licence-number lookup is a single row and finishes in seconds. A name
search returns as many licences as DOCI holds under that name — often one or two, sometimes a
handful for a company that carries licences on several boards — capped by maxResults. Rows land in
the dataset as they’re parsed, so you watch the count climb rather than wait on a silent finish.
An empty run is nearly always the query, not a broken actor. In order of likelihood: the board
filter excluded the licensee (blank it and try again — see above); the name in the registry is not
the name on the invoice in front of you, because DOCI stores the legal entity name and “Johnson
Construction Co.” may be filed as JOHNSON CONSTRUCTION COMPANY LLC, so try a shorter distinctive
fragment; or the licence number has a transposed digit. Genuine actor failure looks different — the
run errors out rather than returning zero rows.
Step 4 — Read the output
Each run returns a labelled table, not raw JSON:
Every row looks like this — a real row from a real run:
{
"state": "TN",
"license_number": "56787",
"business_name": "JOHNSON PLUMBING",
"person_name": null,
"license_type": "Contractor",
"status": "Expired",
"issue_date": null,
"expiration_date": "03/31/2016",
"city": "JOHNSON CITY",
"county": "Washington",
"address": null,
"zip_code": "37605",
"phone": null,
"bond_amount": null,
"bond_company": null,
"insurance_company": null,
"matched_by": "business_name",
"source_url": "https://search.cloud.commerce.tn.gov/search/C49031/detail"
}
| Field | Example value | Meaning |
|---|---|---|
state | TN | Always ‘TN’ for this actor |
license_number | 56787 | TN DOCI contractor license number (numeric) |
business_name | JOHNSON PLUMBING | Licensed business or individual name |
license_type | Contractor | License classification (e.g. Contractor) |
status | Expired | License status (e.g. Active, Expired, Revoked) |
expiration_date | 03/31/2016 | License expiration date |
city | JOHNSON CITY | Licensee city |
county | Washington | Tennessee county |
Most people came for status and expiration_date, and they belong together. status is DOCI’s own
word for the licence — Active, Expired, Revoked and friends — and expiration_date is a
MM/DD/YYYY string exactly as the registry prints it, not an ISO date, so parse it before you sort
or compare on it. The sample row above is a real one and it’s a useful warning: it’s Expired, with
an expiry in 2016. A licence existing in the registry is not the same as a licence being current,
and the two fields are the whole point of the check.
Watch the nulls, because several are structural rather than accidental. TN DOCI’s public search
results don’t publish a street address or a phone number, so address and phone come back null on
every row — if you need to reach the contractor, this registry won’t give you the number. person_name
and issue_date are likewise not surfaced by this search, and bond_amount, bond_company and
insurance_company exist in the schema so every actor in the licence suite shares one row shape;
Tennessee doesn’t publish them, so they stay null here. That’s the registry’s limit, not a gap in
the scrape. What you do reliably get is license_number, business_name, license_type, status,
expiration_date, city, county and zip_code — and source_url, a direct link to the official
DOCI record, so any row you put in front of an auditor is one click from its source. Names arrive
upper-cased (JOHNSON PLUMBING) because that’s how DOCI stores them; normalise before you match
against your own data.
Step 5 — Export it
Open the Dataset tab and export to CSV, JSON, or Excel — or pull the same rows from the API, which is what you want if this is going to run on a schedule.
What it costs
$0.004 per licence record returned. A search that finds nothing costs nothing — you are never
charged for an empty result, which matters here because a board filter that’s too narrow is the
most common way to get one. Verifying a single contractor by licence number is one record: four
tenths of a cent. Running a 100-vendor onboarding roster through it is 100 records, about $0.40.
Apify’s platform compute is billed separately, per your plan, and this actor is cheap on it — no
browser, no rendering, just JSON.
The honest comparison: against DOCI’s free public search, this is not cheaper. It’s zero-effort and schedulable. If your volume is one lookup, use the free page.
Where the data comes from
This reads TN DOCI directly. The target is Portal REST JSON API - an anonymous public token is minted at runtime (no credentials), and each search POSTs JSON carrying it. Every night a canary runs this actor against that live source and diffs the result against a frozen fixture — what “verified” means.
Tennessee’s licence portal is one of the friendlier targets in this suite — a clean, undocumented
JSON API with no CAPTCHA and no anti-bot wall, which is why the actor is httpx and nothing else,
no browser anywhere in the stack. The two wrinkles are the token and the shape. Every search POST
must carry a bearer token, and the portal hands one out to anonymous callers with a short life, so
the actor mints a fresh one per run rather than storing anything. The shape is the part that keeps
you honest: results come back not as named fields but as positional arrays — the licence type,
number, status and expiry are simply items 0, 2, 3 and 4 of a list, with no key to tell you so. That
parse is correct today and would break silently the day DOCI reorders a column. Which is exactly why
the nightly canary exists: it runs this actor against the live portal and diffs the result against a
frozen fixture, so a reordered column shows up as a failed check rather than as a wrong row in your
spreadsheet.
See it used
Look Up Tennessee Contractors by Name searches the DOCI boards by business name.
If you don’t know which state the contractor is licensed in, or you need the same company checked in several states at once, reach for Multi-State Contractor & Trade License Lookup instead — it runs one query across several state registries in a single run, where this one goes deep on Tennessee alone. For a neighbouring state, North Carolina Contractor License Lookup & Verify (NCLBGC) does the same job against the NC general-contractor board, which is the other lookup you’ll want if your subs work both sides of the state line.
Last updated 2026-07-13