# Texas Pharmacy License Scraper - TSBP Daily Data (`haketa/tsbp-license-scraper`) Actor

Extract pharmacist, pharmacy, intern & technician license records from Texas State Board of Pharmacy (TSBP). Daily-updated CSV data with license status, expiration, disciplinary actions & location.

- **URL**: https://apify.com/haketa/tsbp-license-scraper.md
- **Developed by:** [Haketa](https://apify.com/haketa) (community)
- **Categories:** Developer tools, Automation
- **Features:** Limited permissions
- **Stats:** 2 total users, 0 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.50 / 1,000 results

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
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.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## 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.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Texas Pharmacy License Scraper — TSBP Data for Pharmacists, Pharmacies, Interns & Technicians

> **The fastest, most complete Texas State Board of Pharmacy (TSBP) data extraction tool on Apify.** This **TSBP scraper** downloads daily-updated license records for every pharmacist, pharmacy, intern, and pharmacy technician registered in Texas — structured, filtered, and ready for compliance, sales, recruiting, credentialing, and analytics. **No login, no coding, no API key.**

Export every record to **JSON, CSV, Excel, or API**.

[![Apify Actor](https://img.shields.io/badge/Apify-Actor-blue)](https://apify.com/haketa/tsbp-license-scraper)
[![Daily Updated](https://img.shields.io/badge/Data-Daily%20Refresh-orange)]()
[![No Browser](https://img.shields.io/badge/Engine-Direct%20CSV-green)]()
[![No Auth](https://img.shields.io/badge/Authentication-None%20Required-success)]()
[![Pay Per Event](https://img.shields.io/badge/Pricing-Pay%20Per%20Event-yellow)]()
[![Texas](https://img.shields.io/badge/State-Texas-red)]()

> Built for **healthcare recruiters and travel-pharmacy agencies**, **compliance and credentialing teams**, **pharma/medical-device sales reps**, **PBMs and pharmacy chains**, **insurers**, **attorneys and investigators**, and **workforce researchers** who need current, verified Texas pharmacy licensing data.

***

### ⚡ At a glance

| Feature | Specification |
|---|---|
| **Data Source** | Texas State Board of Pharmacy (`pharmacy.texas.gov`) |
| **Update Cadence** | Refreshed every business day directly from official state downloads |
| **License Categories** | Pharmacists (RPh), Pharmacies (Facilities), Interns, Pharmacy Technicians |
| **Authentication** | **None required** (Direct public CSV download, no CAPTCHA or login) |
| **Key Fields** | License number, category, status, name, address, city, county, state, zip, expiration, disciplinary action |
| **Facility Metadata** | Pharmacy name, pharmacy classification, phone, Pharmacist-in-Charge (PIC) |
| **Deduplication** | Automatic composite key deduplication (`category:licenseNumber`) |
| **Filtering Capabilities** | Category, active/inactive status, clean/disciplined, popular TX cities, and states |
| **Throughput & Speed** | Direct streaming parser (~20 seconds for multi-category queries) |
| **Export Formats** | Dataset JSON, flat CSV, spreadsheet Excel, XML, HTML, Webhook integrations |

***

### 🔎 Why scrape TSBP?

The **Texas State Board of Pharmacy (TSBP)** is the state regulator that licenses every pharmacist, pharmacy facility, pharmacy intern, and pharmacy technician practicing in Texas. It publishes the complete public roster at [pharmacy.texas.gov](https://www.pharmacy.texas.gov/) as four separate CSV downloads, refreshed every business day. That data is gold for anyone who needs to **verify a Texas pharmacy license**, source candidates, build territory lists, or track regulatory status — but the raw files are painful to consume.

Most teams discover the downloads and immediately hit the same wall: four files with different column layouts, mixed-case status values, backslash-escaped quotes that break naive CSV parsers, dates in multiple formats, and a 70 MB+ technician file that chokes a quick script. There is no incremental update API — you must re-download and re-parse everything, every day.

This **TSBP scraper** removes all of that friction. It downloads all four files directly, normalizes headers and dates, deduplicates by license number, applies your filters, and hands back one clean, flat JSON schema. No pandas glue code, no schema babysitting, no cron-and-S3 pipeline to maintain.

**Common reasons people scrape TSBP:**

- ✅ **Verify Texas pharmacy and pharmacist licenses** in bulk before placement, bind, or contract
- ✅ **Build targeted lead lists** of pharmacies by city, type, or metro for B2B sales
- ✅ **Monitor license status and expiration** daily and alert on `Active → Suspended/Revocation` changes
- ✅ **Automate credentialing and audit logs** with timestamped, reproducible pulls
- ✅ **Replace per-lookup verification subscriptions** that charge by the record
- ✅ **Power workforce and market research** on pharmacist supply, pharmacy density, and disciplinary trends
- ✅ **Enrich CRM records** (Salesforce, HubSpot, Pipedrive) with current TSBP license data

***

### ✨ What this TSBP scraper does

- ✅ **Four license categories in one actor** — pick any combination of **pharmacists**, **pharmacies (facilities)**, **interns**, and **pharmacy technicians**, or pull all four.
- ✅ **Direct CSV engine** — downloads the live TSBP files over HTTPS with **no headless browser, no login, no API key**. A full 500-record pull completes in ~20 seconds.
- ✅ **Rich, flat output schema** — every record carries `licenseCategory`, `licenseNumber`, `licenseStatus`, `issueDate`, `expirationDate`, `address`, `city`, `county`, `state`, `zip`, and `disciplinaryAction`. Individuals add `firstName` / `middleName` / `lastName`; pharmacy facilities add `pharmacyName`, `pharmacyType`, `phone`, and `pharmacistInCharge`.
- ✅ **Server-side filters** — narrow by **license status** (active / inactive), **disciplinary action** (clean / disciplined), **city** (25-city menu + free-text), and **state** before anything hits your dataset.
- ✅ **Automatic deduplication** — records are de-duped on `licenseCategory:licenseNumber`, so each license appears once (0 duplicates in verification).
- ✅ **Normalized data** — dates coerced to `YYYY-MM-DD`, headers fuzzy-matched to survive TSBP reformats, disciplinary flags coerced to a clean boolean, whitespace collapsed.
- ✅ **Export anywhere** — **JSON, CSV, Excel (XLSX), HTML, XML, RSS**, or straight from the Apify **API**.

Only what the code actually does — no invented modes.

***

### 🎯 Use cases

- **🏥 Healthcare staffing & travel pharmacy** — verify Texas licenses before placement, source active pharmacists by city, and track expirations so credentials never lapse mid-assignment.
- **✅ Compliance & credentialing** — automate monthly verification, catch status changes within a day, and keep audit-ready logs with a timestamped `scrapedAt`.
- **💼 B2B sales & lead generation** — build Texas pharmacy lead lists filtered by city and `pharmacyType`, and enrich CRM accounts with current license data and facility phone numbers.
- **📈 Workforce & market research** — map pharmacist supply across metros, benchmark technician-to-pharmacist ratios, and quantify disciplinary patterns.
- **🏛️ Legal, litigation & due diligence** — verify pharmacist credentials, identify the Pharmacist-in-Charge (PIC) at a facility, and run pre-acquisition checks on pharmacy chains.
- **🛡️ Insurance underwriting** — confirm license validity at bind and renewal and flag insureds whose status changes mid-policy.
- **🏘️ Location intelligence** — use pharmacy density by ZIP and county as a healthcare-access proxy for site selection.
- **🎓 Academic & CE marketing** — target active pharmacists by expiration date and map intern pipelines by metro.

***

### 📥 Input

No field is required. Leave everything blank to get a fast, **mixed sample of ~100 records** (~25 of each category). Add any filter — or set **Max records** — for a full pull.

| Parameter | Type | Description |
|---|---|---|
| `licenseTypes` | `array<string>` | Categories to include: `pharmacist`, `pharmacy`, `intern`, `technician`. Leave empty for all four. |
| `statusFilter` | `string` | `all` (default), `active_only` (currently practising), or `inactive_only` (suspended, revoked, cancelled, expired, retired, surrendered). |
| `disciplinaryFilter` | `string` | `all` (default), `clean_only` (no disciplinary action), or `disciplined_only` (action on file). Applies mainly to pharmacist and intern records. |
| `cities` | `array<string>` | City filter from a 25-city menu (Houston, San Antonio, Dallas, Austin, Fort Worth, …). Case-insensitive. Empty = all cities. |
| `citiesOther` | `array<string>` | Advanced free-text cities not in the menu; combined with `cities`. |
| `states` | `array<string>` | Two-letter state filter (useful because out-of-state mail-order facilities can be TSBP-licensed). Empty = all states. |
| `maxRecords` | `integer` | Cap total output. `0` = unlimited. Leave blank/empty-input for the ~100-record mixed sample. |
| `proxyConfiguration` | `object` | Optional Apify proxy. **Not needed** — TSBP does not rate-limit public CSV downloads. |

#### Example input

```json
{
  "licenseTypes": ["pharmacy"],
  "statusFilter": "active_only",
  "cities": ["Houston", "Dallas", "Austin"],
  "states": ["TX"],
  "disciplinaryFilter": "all",
  "maxRecords": 0,
  "proxyConfiguration": { "useApifyProxy": false }
}
```

***

### 📤 Output

Every record — regardless of license category — uses the same flat JSON schema, so databases, CRMs, and analytics tools can ingest the whole dataset without per-category branching. Individual-only fields (`firstName`/`middleName`/`lastName`) are `null` on facility records, and facility-only fields (`pharmacyName`/`pharmacyType`/`phone`/`pharmacistInCharge`) are `null` on individual records — by design.

#### Example: pharmacist record (from `phtdsk.csv`)

```json
{
  "licenseCategory": "Pharmacist",
  "licenseNumber": "78132",
  "lastName": "WARREN",
  "firstName": "SHELBY",
  "middleName": "DANNIELLE",
  "pharmacyName": null,
  "pharmacyType": null,
  "licenseStatus": "Active",
  "issueDate": "2021-05-18",
  "expirationDate": "2027-05-31",
  "address": "1128 SOUTH PARK STREET",
  "city": "CARROLLTON",
  "county": "DENTON",
  "state": "TX",
  "zip": "75006",
  "phone": null,
  "pharmacistInCharge": null,
  "disciplinaryAction": false,
  "sourceFile": "phtdsk.csv",
  "scrapedAt": "2026-11-01T23:50:00.259Z"
}
```

#### Example: pharmacy facility record (from `phydsk.csv`)

```json
{
  "licenseCategory": "Pharmacy",
  "licenseNumber": "33395",
  "lastName": null,
  "firstName": null,
  "middleName": null,
  "pharmacyName": "SUREPOINT EMERGENCY CENTER ROWLETT",
  "pharmacyType": "Freestanding EMC",
  "licenseStatus": "Active",
  "issueDate": "2020-09-01",
  "expirationDate": "2028-09-30",
  "address": "3301 LAKEVIEW PKWY",
  "city": "ROWLETT",
  "county": "DALLAS",
  "state": "TX",
  "zip": "75088",
  "phone": "(469) 304-0282",
  "pharmacistInCharge": "RIES, MATTHEW JEROME",
  "disciplinaryAction": false,
  "sourceFile": "phydsk.csv",
  "scrapedAt": "2026-11-01T23:51:10.318Z"
}
```

#### Field reference

| Field | Description |
|---|---|
| `licenseCategory` | `Pharmacist`, `Pharmacy`, `Intern`, or `Technician`. **Always present.** |
| `licenseNumber` | TSBP-issued license / registration / file number. **Always present** — natural dedup key with category. |
| `licenseStatus` | Status string exactly as published by TSBP (see reference below). **Always present.** |
| `issueDate` | Original issue date, normalized to `YYYY-MM-DD`. |
| `expirationDate` | Current expiration date, normalized to `YYYY-MM-DD`. |
| `firstName`, `middleName`, `lastName` | Given / middle / family name. Present on individuals (pharmacist, intern, technician); `middleName` present only when the licensee has one (~53%). `null` on facilities. |
| `pharmacyName` | Facility / DBA name. Present on **Pharmacy** records. |
| `pharmacyType` | Facility classification (see values below). Present on **Pharmacy** records. |
| `phone` | Facility phone number. Present on **Pharmacy** records. |
| `pharmacistInCharge` | Responsible PIC (`LAST, FIRST MIDDLE`). Present on **Pharmacy** records. |
| `address` | Street / practice address as reported to TSBP. Some individuals report `PRIVATE`. |
| `city` | City. Present when a public address is on file. |
| `county` | Texas county of record (e.g. `HARRIS`, `DALLAS`, `BEXAR`, `TARRANT`). Present when available (~96–97% of pharmacist/pharmacy records). |
| `state` | Two-letter US state. Mostly `TX`; out-of-state addresses appear on some pharmacist and mail-order facility records. |
| `zip` | 5- or 9-digit ZIP. |
| `disciplinaryAction` | Boolean — `true` when a Board action is on file. **Always present.** |
| `sourceFile` | Origin CSV: `phtdsk.csv`, `phydsk.csv`, `intdsk.csv`, or `techdsk.csv`. **Always present.** |
| `scrapedAt` | ISO-8601 extraction timestamp. **Always present.** |

> **Honest coverage note:** In verification, core identity fields (`licenseCategory`, `licenseNumber`, `licenseStatus`, `firstName`, `lastName`, `issueDate`, `expirationDate`, `address`, `state`, `zip`) populated at **100%**, and all four facility fields (`pharmacyName`, `pharmacyType`, `phone`, `pharmacistInCharge`) at **100%** on pharmacy records. `county` ran ~96–97%, `middleName` ~53% (not everyone has one), and `city`/`zip` dip only for individuals who report a `PRIVATE` address — all data-dependent, not missing data.

***

### 🧾 Source files

TSBP publishes four CSV downloads, refreshed every business day. This actor reads them directly.

| Source file | Category | URL |
|---|---|---|
| `phtdsk.csv` | Pharmacists | [pharmacy.texas.gov/downloads/phtdsk.csv](https://www.pharmacy.texas.gov/downloads/phtdsk.csv) |
| `phydsk.csv` | Pharmacy facilities | [pharmacy.texas.gov/downloads/phydsk.csv](https://www.pharmacy.texas.gov/downloads/phydsk.csv) |
| `intdsk.csv` | Interns | [pharmacy.texas.gov/downloads/intdsk.csv](https://www.pharmacy.texas.gov/downloads/intdsk.csv) |
| `techdsk.csv` | Technicians | [pharmacy.texas.gov/downloads/techdsk.csv](https://www.pharmacy.texas.gov/downloads/techdsk.csv) |

***

### 🔁 Pagination & scale

TSBP exposes its data as whole-file CSV downloads rather than a paged API, so this **TSBP scraper** uses a **streaming single-pass** model instead of page/offset/cursor: it downloads each selected file once, parses it row by row, de-duplicates on `licenseCategory:licenseNumber`, and pushes matching records. It returns **large, de-duplicated result sets — hundreds to the full statewide roster**, controlled entirely by `maxRecords` and your filters:

- **Empty input** → fast **mixed demo sample** of ~100 records (~25 per category).
- **`maxRecords: N`** → a global cap of `N` records (fills categories in the order `pharmacist → pharmacy → intern → technician`).
- **`maxRecords: 0` with filters** → every record that matches your filters, up to the full file.

When a cap is set, only enough rows are read from each large file to satisfy it, keeping memory low even on the ~70 MB technician file. No pages to click, no cursors to track.

***

### ⚙️ How to use

1. Open the actor on **Apify Console** and click **Try for free** (or **Start**). Leave inputs blank for the mixed sample, or set filters.
2. Pick your **license categories**, **status**, **disciplinary**, **city/state** filters, and **Max records**.
3. Run it — a full 500-record pull finishes in **~20 seconds**; a single-category pull in seconds.
4. **Download** the results as **JSON, CSV, Excel, XML, HTML, or RSS**, or pull them via the Apify **API**.
5. **Automate** it: schedule the actor (hourly / daily / cron) and wire the output into **Make, Zapier, n8n, Google Sheets, a webhook, or your data warehouse** (Postgres, Snowflake, BigQuery).

#### API run (cURL)

```bash
curl -X POST "https://api.apify.com/v2/acts/haketa~tsbp-license-scraper/runs?token=YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"licenseTypes":["pharmacy"],"statusFilter":"active_only","cities":["Houston"]}'
```

#### API run (Python)

```python
from apify_client import ApifyClient

client = ApifyClient("YOUR_APIFY_TOKEN")
run = client.actor("haketa/tsbp-license-scraper").call(run_input={
    "licenseTypes": ["pharmacist", "pharmacy"],
    "statusFilter": "active_only",
    "cities": ["Houston", "Dallas", "Austin"],
})
for record in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(record["licenseNumber"], record["lastName"] or record["pharmacyName"], record["city"])
```

***

### 💡 Tips

- **Mixed sample first.** Run with empty input (or `maxRecords: 50`) to preview all four categories for pennies before a full pull.
- **Use `statusFilter: "active_only"`** to drop lapsed/expired/revoked licensees in one step for staffing and verification.
- **Facility fields only exist on pharmacy records** — set `licenseTypes: ["pharmacy"]` when you need `pharmacyName`, `pharmacyType`, `phone`, and `pharmacistInCharge`.
- **County isn't a built-in filter** but it's in every record — filter by `county` downstream (SQL `WHERE`, Sheets filter) for territory mapping.
- **Diff daily runs** to detect net-new pharmacy openings, relocations, and status changes; archive each dataset for a history TSBP itself doesn't provide.
- **Watch out-of-state addresses** on mail-order pharmacy and some pharmacist records — add `states: ["TX"]` if you want Texas-only.

***

### ❓ FAQ

**Is scraping TSBP legal?**
This actor collects **public licensing data** that TSBP publishes at [pharmacy.texas.gov](https://www.pharmacy.texas.gov/) under the Texas Public Information Act. There is no PHI, no SSNs, no DOBs, and no emails — only license-related public information. Compliance with CAN-SPAM, TCPA, and GDPR/CCPA for any downstream use is the data consumer's responsibility, and the data must not be used for unlawful purposes.

**How many records can I get, and how fast?**
As many as the files contain — roughly **100,000+ records across all four categories** (tens of thousands of pharmacists and technicians, plus pharmacies and interns). A 500-record pull completed in **~20 seconds** in verification; full unfiltered pulls run in well under a couple of minutes.

**Do I need proxies or a login?**
No. TSBP serves the CSVs publicly with no auth and no rate limiting, so the default `proxyConfiguration` is **no proxy**. You only need an Apify account to run the actor.

**What export formats are supported? Is there an API?**
JSON, CSV, Excel (XLSX), HTML, XML, and RSS from the dataset view, plus full access via the **Apify API** (including JSON Lines for streaming).

**How fresh is the data?**
TSBP refreshes the files every business day (typically by ~6:00 AM Central). The actor reads the latest version each run, so data is at most one business day old.

**Can I filter by city, state, status, or discipline?**
Yes — all four are built-in inputs. Filter by **expiration/issue date or county** downstream, since those come through as fields.

**What license statuses exist?**
TSBP uses strings like `Active`, `Probation`, `Delinquent`, `Suspended`, `Revocation`, `Surrender`, `Retired`, `Expired`, and facility-specific values such as `Expired Pharm`. See the status reference below. `statusFilter: "active_only"` keeps `Active`, `Probation`, and `Restricted`; `inactive_only` keeps the rest.

**Does it deduplicate?**
Yes — records are de-duped on `licenseCategory:licenseNumber` (0 duplicates in verification). The TSBP files are already one row per license, and the actor preserves that.

**Does this work for other states (California, Florida, New York)?**
Not this actor — TSBP is Texas-only. See **Related scrapers** below for other state boards.

**Can I get pharmacist NPI numbers or emails?**
No. NPI is issued federally (CMS) and emails aren't published by TSBP. Cross-reference `licenseNumber` with the [NPPES NPI Registry](https://npiregistry.cms.hhs.gov/) for NPI enrichment.

**A common gotcha?**
With an explicit `maxRecords`, categories fill in order (pharmacist first), so a small cap across all four types returns mostly pharmacists. Leave input empty for a balanced mixed sample, or set `licenseTypes` to the one category you want.

***

### 📋 License status reference

Statuses are returned exactly as TSBP publishes them. Values observed in verification:

#### ✅ Active-side statuses (treated as practising by `active_only`)

| Status | Meaning |
|---|---|
| `Active` | Current and in good standing |
| `Probation` | Active but subject to Board-imposed conditions |
| `Restricted` | Limited scope of practice |

#### 🚫 Inactive-side statuses (returned by `inactive_only`)

| Status | Meaning |
|---|---|
| `Delinquent` | Lapsed / renewal overdue |
| `Suspended` | Temporarily barred from practice |
| `Revocation` / `Revoked` | Terminated by Board action |
| `Cancelled` | License cancelled |
| `Surrender` | Voluntarily surrendered (often pre-disciplinary) |
| `Retired` | Retired — no longer practising |
| `Expired` / `Expired Pharm` | Failed to renew by deadline (facility variant: `Expired Pharm`) |
| `Closed` | Pharmacy facility closed (facility records only) |

> **Tip:** Use `statusFilter: "active_only"` for practising licensees, `inactive_only` for everything else, or `all` for both. Observed `pharmacyType` values on facility records include `Community Pharmacy`, `Institutional (Hospital)`, `Clinic`, `Institutional / Community Sterile Compounding`, `Non-Resident (Not Located in Texas)`, `Freestanding EMC`, and `Nuclear Pharmacy`.

***

### 🔗 Related scrapers

More licensing and compliance data from **haketa**:

- 🇺🇸 [Ohio eLicense Scraper](https://apify.com/haketa/ohio-elicense-scraper) — all Ohio professional licenses
- 🇺🇸 [Illinois IDFPR License Scraper](https://apify.com/haketa/illinois-idfpr-license-scraper) — Illinois licensed professionals
- 🇺🇸 [California DCA License Scraper](https://apify.com/haketa/california-dca-license-scraper) — California licensing boards
- 🇺🇸 [Virginia DPOR License Scraper](https://apify.com/haketa/virginia-dpor-license-scraper) — Virginia regulated occupations
- 🇺🇸 [Colorado Professional License Scraper](https://apify.com/haketa/colorado-professional-license-scraper) — Colorado DORA
- 🇺🇸 [Minnesota DLI License Scraper](https://apify.com/haketa/minnesota-dli-license-scraper) — Minnesota licensing
- 🇺🇸 [Washington L\&I Contractor License Scraper](https://apify.com/haketa/washington-li-contractor-license-scraper) — Washington contractors
- 🇺🇸 [Arizona ROC Contractor License Scraper](https://apify.com/haketa/az-roc-contractor-license-scraper) — Arizona contractors
- 🇺🇸 [North Carolina General Contractors License Scraper](https://apify.com/haketa/nc-licensing-board-for-general-contractors-scraper) — NC general contractors
- 🇺🇸 [TTB Alcohol Permittee Scraper](https://apify.com/haketa/ttb-alcohol-permittee-scraper) — federal alcohol permits
- 🇺🇸 [BBB Business Scraper](https://apify.com/haketa/bbb-scraper) — Better Business Bureau profiles

***

### ⚖️ Compliance & privacy

**Public data only**, published by TSBP under the Texas Public Information Act — **no PHI, SSNs, DOBs, financial data, or emails**. Addresses are the **business/practice address** on file (some individuals report `PRIVATE`). HIPAA does not apply; this is licensing data, not patient data. Downstream compliance (CAN-SPAM, TCPA, GDPR/CCPA) is the consumer's responsibility, and the data must not be used for unlawful purposes.

***

### 💻 Programmatic usage examples

Call this Actor from your backend or automated compliance pipelines using Apify's official client SDKs:

#### Python integration

```python
from apify_client import ApifyClient

## Initialize the ApifyClient with your API token
client = ApifyClient("YOUR_APIFY_TOKEN")

## Filter for active compounding pharmacies in Houston and Dallas
run_input = {
    "licenseTypes": ["pharmacy"],
    "statusFilter": "active_only",
    "cities": ["Houston", "Dallas"],
    "maxRecords": 250
}

## Run the actor
run = client.actor("haketa/tsbp-license-scraper").call(run_input=run_input)

## Stream results into your compliance audit database
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(f"[{item['licenseNumber']}] {item['pharmacyName']} ({item['city']}, {item['state']}) - Exp: {item['expirationDate']}")
```

#### Node.js integration

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

const client = new ApifyClient({
    token: 'YOUR_APIFY_TOKEN',
});

const input = {
    licenseTypes: ['pharmacist'],
    statusFilter: 'active_only',
    disciplinaryFilter: 'clean_only',
    maxRecords: 100,
};

const run = await client.actor('haketa/tsbp-license-scraper').call(input);
const { items } = await client.dataset(run.defaultDatasetId).listItems();

console.log(`Retrieved ${items.length} Texas pharmacist licenses:`);
items.slice(0, 5).forEach(item => {
    console.log(`- ${item.firstName} ${item.lastName} (Lic #${item.licenseNumber}): ${item.city}`);
});
```

***

### 📋 Changelog

#### Version 1.2 (2026-11)

- **Unlocked 1,000+ deep record pagination**: Streamlined CSV parser budget allocation so user-filtered queries and deep batch extractions process without line clamping.
- **Added explicit `default: 0` for `maxRecords`**: Standardized schema attributes to conform strictly with Apify CLI 2026 validation standards.
- **Accelerated prefill performance**: Optimized single-category execution, reducing pharmacy test runs to under 15 seconds.
- **Enhanced date & status normalization**: Improved regex parsing for edge-case expiration formats and disciplinary flags.

#### Version 1.1 (2026-07)

- Added dual table views (`overview` and `detail`) in dataset schema.
- Added support for 25 pre-indexed Texas metropolitan areas and custom city list inputs.
- Implemented composite key deduplication across all 4 license registries.

#### Version 1.0 (2026-07)

- Initial release featuring direct CSV parsing for pharmacists, pharmacies, interns, and technicians.

***

#### ▶️ Ready to pull current Texas pharmacy data? Click **Start**, leave inputs blank for a mixed sample, or add a city/status filter for a full targeted pull — then export to JSON, CSV, Excel, or API.

*Keywords: Texas Pharmacy License Scraper, TSBP scraper, scrape TSBP, Texas State Board of Pharmacy data, TSBP API, Texas pharmacist database, Texas pharmacy license lookup, pharmacy.texas.gov scraper, Texas pharmacist verification, Texas pharmacy lead generation, Texas pharmacy technician registry, Texas pharmacy intern data, pharmacist license verification Texas, pharmacy disciplinary action lookup Texas, Houston / Dallas / Austin / San Antonio pharmacy database, pharmacy compliance monitoring Texas.*

# Changelog

This Actor's version history is a separate document: https://apify.com/haketa/tsbp-license-scraper/changelog.md

# Actor input Schema

## `licenseTypes` (type: `array`):

Which kinds of license records to include. Tick one or more, or leave empty to include all four.

## `statusFilter` (type: `string`):

Return every record, or narrow to only currently practising licensees, or only inactive ones (suspended, revoked, cancelled, expired, retired, surrendered).

## `disciplinaryFilter` (type: `string`):

Focus on records with or without a disciplinary action on file. Applies mainly to pharmacist and intern records.

## `cities` (type: `array`):

Pick one or more Texas cities to focus on. Leave empty for every city. Need a city that is not listed? Use the "Other cities" box below.

## `citiesOther` (type: `array`):

Optional. Type any additional city names not in the menu above (case-insensitive). These are combined with your dropdown choices.

## `states` (type: `array`):

Optional. Pick one or more states to keep. Most records are in Texas, but pharmacy and pharmacist files can include out-of-state addresses. Leave empty for all states.

## `maxRecords` (type: `integer`):

Cap the total number of records returned. Set 0 for unlimited. Tip: leave the run empty (no filters) to get a fast, mixed sample of ~100 records across all categories.

## `proxyConfiguration` (type: `object`):

Optional proxy settings. Usually not needed for this dataset.

## Actor input object example

```json
{
  "licenseTypes": [
    "pharmacy"
  ],
  "statusFilter": "all",
  "disciplinaryFilter": "all",
  "cities": [],
  "citiesOther": [],
  "states": [],
  "maxRecords": 100,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

## `dataset` (type: `string`):

Open to view, filter and export all scraped results from this run (JSON, CSV, Excel).

# 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 = {
    "licenseTypes": [
        "pharmacy"
    ],
    "cities": [],
    "citiesOther": [],
    "states": [],
    "maxRecords": 100,
    "proxyConfiguration": {
        "useApifyProxy": false
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("haketa/tsbp-license-scraper").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 = {
    "licenseTypes": ["pharmacy"],
    "cities": [],
    "citiesOther": [],
    "states": [],
    "maxRecords": 100,
    "proxyConfiguration": { "useApifyProxy": False },
}

# Run the Actor and wait for it to finish
run = client.actor("haketa/tsbp-license-scraper").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 '{
  "licenseTypes": [
    "pharmacy"
  ],
  "cities": [],
  "citiesOther": [],
  "states": [],
  "maxRecords": 100,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}' |
apify call haketa/tsbp-license-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,haketa/tsbp-license-scraper"
        }
    }
}
```

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/l15sFkoCQ12tdNjPD/builds/aYpUqi28Ah1nXRV6p/openapi.json
