# TX TDLR Licenses - Texas Professional & Trade License Lookup (`j0401/tx-tdlr-licenses`) Actor

Texas TDLR licenses (983k+ records, 100+ types): electricians, cosmetologists, A/C technicians, barbers and more. Search the full register by type / name / county / expiry, or get one license with its real status and original issue date.

- **URL**: https://apify.com/j0401/tx-tdlr-licenses.md
- **Developed by:** [Wenhao Yang](https://apify.com/j0401) (community)
- **Categories:**
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$1.00 / 1,000 texas tdlr license records

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## TX TDLR Licenses (`tx-tdlr-licenses`)

Search and verify **Texas professional & trade licenses** - the state's ~**983k-license register** across **100+ programs** (electricians, cosmetologists, A/C technicians, barbers, tow operators, and more), free public data from the Texas Department of Licensing and Regulation.

This is a **credentials and compliance data layer**: verify a license is real and current, find who is licensed to do a job in your county, watch who is expiring or newly licensed, and read the state's daily changes feed.

Built for **HR & compliance teams, credentialing and background-screen vendors, contractors and their insurers, staffing agencies, and market researchers** - the question "is this license real, whose is it, and is it current?" answered against the official register.

### What a record carries

Search rows carry the register essentials. A `get` (by license number) adds the **live detail page**: the license's real **status** and its **original issue date**, parsed from the state's detail record - not a derived guess.

| Field | Meaning |
|---|---|
| `licenseNumber` | The license number (numbers repeat across types - a barber and an electrician can share one; each match is returned, flagged by type) |
| `licenseType` / `subtype` | Program and sub-code (e.g. electrician `ME`/`JE`/`AE`) |
| `ownerName` / `businessName` | The person / the business, as the register lists each |
| `county` | Where the licensee is registered |
| `expirationDate` / `isExpired` | Expiry and a derived current flag |
| `status` + `originalIssueDate` | On a `get`: the live detail fields (real status, first issue date) |

> **Honest scope note**: the register does **not** carry bond / restriction / disciplinary detail - that is simply not in TDLR's public record. What it does carry (real status, original issue date, expiry, county, names) is returned accurately rather than padded with fields that don't exist.

### Typical questions it answers

- **Verify a license** - `licenseNumber=223846` (auto-routes to a `get`): live status + original issue date.
- **Who's licensed to do electrical work in my county?** - `licenseType=Journeyman Electrician` + `county=Travis`.
- **Renewals coming due** - filter an `expiryFrom` / `expiryTo` 90-day window (electricians alone run ~326k records).
- **What's the statewide mix?** - `aggregate=true` (one row per license type / subtype / county with count + share).
- **What changed today?** - `mode=changes`: new licenses, expiring licenses, and field changes from the state's daily register.

### Inputs (all optional)

| Input | What it does |
|---|---|
| `mode` | `search` / `get` / `aggregate` / `changes`; blank auto-routes (license number -> get, else search) |
| `licenseNumber` | Exact number - returns every matching license (flagged by type) + live status & original issue date |
| `licenseType` | Program (electrician / cosmetology / barber / A/C tech...) or blank for all 100+ |
| `subtype` | Program sub-code (e.g. `ME`, `JE`, `AE`, `OP`) |
| `name` | Licensee / business name, substring |
| `county` | County, substring |
| `expiryFrom` / `expiryTo` | Expiry window (renewal sweeps) |
| `aggregate` / `groupBy` | One row per license type / subtype / county with count + share |
| `maxResults` | Cap records (default 100) |

### Low cost

**$0.001 per record** - a tenth of a cent for a license row or aggregate group - with a per-run charge cap so no surprise bills.

**~983k licenses across 100+ programs** sit behind one query surface. Two source quirks are absorbed for you: the state republishes its full register as a **~187MB file** every business day, so changes are pre-diffed and served to you as a lean daily feed rather than that file's bulk; and license numbers genuinely repeat across programs (a barber and an electrician can hold the same number), so every matching license is returned flagged by type instead of silently collapsed. You query clean license rows and expiry sweeps; the register's daily bulk and number collisions stay on our side.

### Data source & freshness

- **Source**: [data.texas.gov - TDLR license mirror](https://data.texas.gov/) (`7358-krk7`, Socrata SODA API, free public, no login) + TDLR's official daily register for the changes feed.
- **Update cadence**: daily - the state republishes its register every business day; `changes` reflects it.

### License & disclaimer

MIT - see [LICENSE](./LICENSE).

Independent data service; **not affiliated with or endorsed by** the Texas Department of Licensing and Regulation. All license data is public state information. Verify any credential on the official TDLR site before relying on it.

# Actor input Schema

## `mode` (type: `string`):

search = filter the register. get = one license + its live detail (status / original issue date). aggregate = one row per group. changes = daily new / expiring / field-changed feed. Leave blank to auto-route: license number -> get, otherwise search (or aggregate when aggregate=true).

## `licenseNumber` (type: `string`):

Exact license number, e.g. "223846" or "100252". Numbers repeat across license types (a barber and an electrician can share a number) - the number returns every matching license, each flagged by type. With this set (and no aggregate), returns each match PLUS its live status and original issue date.

## `licenseType` (type: `string`):

The kind of license. Blank = all ~100 types (electricians are the largest at ~326k).

## `name` (type: `string`):

Name, substring, case-insensitive (e.g. "Campo" or "Mercer").

## `county` (type: `string`):

County name, substring, case-insensitive (e.g. "Travis").

## `subtype` (type: `string`):

Program subtype code, e.g. "OP" (cosmetology operator), "ME"/"JE"/"AE" (master/journeyman/apprentice electrician). Narrow a type further.

## `expiryFrom` (type: `string`):

Only licenses expiring on/after this date (YYYY-MM-DD). E.g. to find renewals due, set a 90-day window.

## `expiryTo` (type: `string`):

Only licenses expiring on/before this date (YYYY-MM-DD).

## `aggregate` (type: `boolean`):

When on, returns one summary record per group (license type / subtype / county / expiry year) with count and share.

## `groupBy` (type: `string`):

licenseType -> how many of each program; county -> geographic mix.

## `maxResults` (type: `integer`):

Cap the number of records pushed (0 = a reasonable ceiling of 5000). A get is at most a few rows.

## Actor input object example

```json
{
  "aggregate": false,
  "groupBy": "licenseType",
  "maxResults": 100
}
```

# Actor output Schema

## `recordsUrl` (type: `string`):

Texas TDLR license records - as JSON

## `datasetUrl` (type: `string`):

No description

## `runUrl` (type: `string`):

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {};

// Run the Actor and wait for it to finish
const run = await client.actor("j0401/tx-tdlr-licenses").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {}

# Run the Actor and wait for it to finish
run = client.actor("j0401/tx-tdlr-licenses").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{}' |
apify call j0401/tx-tdlr-licenses --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,j0401/tx-tdlr-licenses"
        }
    }
}

```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/ueabyxSxahRha1rX9/builds/vjZxXWrnZ2pvdXYYK/openapi.json
