# Ohio eLicense Scraper - Pharmacy & Professional (`haketa/ohio-elicense-scraper`) Actor

Ohio eLicense scraper & API: search Ohio professional licenses across boards and export license number, profession, status, name, address and dates. Professional license verification, compliance and lead generation — fast, no login.

- **URL**: https://apify.com/haketa/ohio-elicense-scraper.md
- **Developed by:** [Haketa](https://apify.com/haketa) (community)
- **Categories:** Automation, Developer tools, Other
- **Stats:** 3 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.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/platform/actors/running/actors-in-store#pay-per-event

## What's an Apify Actor?

Actors are a software tools running on the Apify platform, for all kinds of web data extraction and automation use cases.
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.

In JavaScript/TypeScript projects, use official [JavaScript/TypeScript client](https://docs.apify.com/api/client/js.md):

```bash
npm install apify-client
```

In Python projects, use official [Python client library](https://docs.apify.com/api/client/python.md):

```bash
pip install apify-client
```

In shell scripts, use [Apify CLI](https://docs.apify.com/cli/docs.md):

````bash
# MacOS / Linux
curl -fsSL https://apify.com/install-cli.sh | bash
# Windows
irm https://apify.com/install-cli.ps1 | iex
```bash

In AI frameworks, you might use the [Apify MCP server](https://docs.apify.com/platform/integrations/mcp.md).

If your project is in a different language, use 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

## Ohio eLicense Scraper — Pharmacy, Nursing, Medical & Multi-Board Professional License Lookup

> **The most comprehensive Ohio eLicense data extraction tool on Apify.** Search the entire **Ohio eLicense portal** ([elicense.ohio.gov](https://elicense.ohio.gov/oh_verifylicense)) across **24 state boards** — pharmacists, nurses, physicians, dentists, engineers, contractors, real estate agents, cosmetologists and more — and pull **clean, structured license records** with status, expiration date, county, and disciplinary flags in a single run. Perfect for **credentialing, compliance monitoring, B2B lead generation, recruiting, M&A diligence, and statewide workforce analytics** in Ohio.

[![Apify Actor](https://img.shields.io/badge/Apify-Actor-blue)](https://apify.com/haketa/ohio-elicense-scraper)
[![Live Updated](https://img.shields.io/badge/Data-Live%20at%20Runtime-orange)]()
[![Engine](https://img.shields.io/badge/Engine-Playwright%20Browser-green)]()
[![Pay Per Event](https://img.shields.io/badge/Pricing-Pay%20Per%20Event-yellow)]()
[![Coverage](https://img.shields.io/badge/Boards-24%20Ohio%20Boards-purple)]()
[![State](https://img.shields.io/badge/State-Ohio-red)]()
[![No Auth](https://img.shields.io/badge/Authentication-None%20Required-success)]()
[![Schema](https://img.shields.io/badge/Schema-Normalized%20JSON-blueviolet)]()

---

### What This Actor Does

The **Ohio eLicense Scraper** is a production-grade Apify Actor that automates the **Ohio eLicense public license verification portal** at [elicense.ohio.gov/oh_verifylicense](https://elicense.ohio.gov/oh_verifylicense). The portal is the **single official lookup tool** for every license issued by the State of Ohio's regulatory boards — covering everything from pharmacists in Columbus to nursing assistants in Toledo to general contractors in Cincinnati to engineers in Cleveland.

In one run, the actor performs end-to-end browser automation: it loads the JSF/RichFaces form, selects boards and license types, fires the search, paginates through every result, and (optionally) drills into each license detail page to extract address, county, disciplinary history, and full licensure metadata. The output is a **flat, normalized JSON dataset** that drops cleanly into Postgres, BigQuery, Snowflake, Salesforce, HubSpot, Google Sheets, or any downstream stack.

The actor covers all **24 Ohio state regulatory boards** — Pharmacy, Nursing, Medical, Dental, Chiropractic, Optometry, Optical Dispensers, Speech & Hearing, OT/PT/Athletic Trainers, Counselor/Social Worker/MFT, Chemical Dependency, Psychology, Veterinary, Embalmers & Funeral Directors, Cosmetology, Engineers & Surveyors, Architects, Landscape Architects, Accountancy, Construction Industry Licensing Board (OCILB), Real Estate & Professional Licensing, Board of Executives of Long-Term Services & Supports, Orthotics/Prosthetics/Pedorthics, and other niche boards. The full board → license-type cheat sheet appears in the [Ohio State Boards Covered](#ohio-state-boards-covered-reference-table) reference table below.

Each record carries license number, board, license type, name (or business name), normalized status, issue date, expiration date, full address (street, city, county, state, ZIP), disciplinary action flag, source detail URL, and an ISO-8601 scrape timestamp.

#### Why scrape Ohio eLicense yourself when this exists?

The Ohio eLicense portal is built on **Salesforce Visualforce + RichFaces + Ajax4JSF** — a legacy JSF stack that fights every modern scraping toolkit. Teams who try to roll their own scraper hit the same wall every time:

- ❌ **No public API and no CSV exports** — the only access point is the interactive `oh_verifylicense` web form
- ❌ **JSF `ViewState` + j_id form IDs** mutate between page loads, so every selector breaks on the next session
- ❌ **AJAX partial page updates** — the results table re-renders in place without changing the URL, defeating `requests` + `BeautifulSoup` pipelines
- ❌ **Form fields load asynchronously** — the initial DOM is empty; selects, buttons, and inputs materialize seconds after `DOMContentLoaded`
- ❌ **24 different boards** with their own license-type vocabularies, each requiring board-specific dropdown handling
- ❌ **Pagination is event-driven** (Ajax4JSF re-renders the table) with no clean `?page=N` URL to iterate
- ❌ **Status terminology varies by board** (`Active` vs `Active - Renewed` vs `Probation`) — downstream consumers need normalization
- ❌ **Session expiry** — long search jobs lose their `ViewState` token and get bounced; detail pages add a second navigation per record

This actor handles every one of those problems out of the box. Configure a few inputs, hit Start, walk away with structured JSON.

---

### Quick Start

#### One-Click Run (Apify Console UI)

1. Open the actor page on **Apify Console** and click **"Try for free"**.
2. Choose one or more boards in **Boards** (e.g., `Board of Pharmacy`, `Board of Nursing`). Leave empty to scan every board.
3. (Optional) Add a city/county filter, name filter, or set `maxRecords: 200` for a sample run.
4. Hit **Start** — your dataset appears in the Apify run view in minutes and can be exported as JSON, CSV, Excel, HTML, or RSS.

#### API Run (Python)

```python
from apify_client import ApifyClient

client = ApifyClient("YOUR_APIFY_TOKEN")

run = client.actor("haketa/ohio-elicense-scraper").call(run_input={
    "boards": ["Board of Pharmacy"],
    "licenseTypes": ["Pharmacist", "Terminal Distributor of Dangerous Drugs"],
    "statusFilter": "active_only",
    "counties": ["Franklin", "Cuyahoga", "Hamilton"],
    "scrapeDetails": True,
    "maxRecords": 500
})

for record in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(record["licenseNumber"], record["board"],
          record["lastName"] or record["businessName"], record["city"])
````

#### API Run (Node.js / TypeScript)

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

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

const run = await client.actor('haketa/ohio-elicense-scraper').call({
    boards: ['Board of Nursing'],
    licenseTypes: ['Registered Nurse'],
    statusFilter: 'active_only',
    counties: ['Cuyahoga'],   // Cleveland metro
    maxRecords: 1000,
});

const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(`Pulled ${items.length} active Cleveland RNs`);
```

#### API Run (cURL)

```bash
curl -X POST "https://api.apify.com/v2/acts/haketa~ohio-elicense-scraper/runs?token=YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "boards": ["State Medical Board"],
    "licenseTypes": ["Doctor of Medicine"],
    "statusFilter": "active_only",
    "counties": ["Hamilton"],
    "maxRecords": 250
  }'
```

***

### How It Works

The Ohio eLicense portal is a **Salesforce Visualforce page** rendered with **RichFaces** (a JSF component library) and a sprinkling of jQuery for UI polish. The form elements are not present in the initial HTML — they are injected by JavaScript after the page mounts, and the results table re-renders via Ajax4JSF partial updates instead of full page reloads.

To scrape this reliably the actor uses **Playwright (Chromium)** with a careful state machine: navigate → wait for JSF/AJAX form materialization → iterate each board in `boards[]` (opening dropdown, waiting for license-type repopulation) → narrow by `licenseTypes[]`, `searchName`, or `searchLicenseNumber` → submit, wait for the RichFaces partial update → parse every row → optionally follow each detail link for full address + disciplinary history (`scrapeDetails: true`) → paginate until `maxPages` / `maxRecords` is hit → normalize statuses, parse addresses, infer the `disciplinaryAction` boolean, and push to the Apify dataset.

#### Source URLs

| Resource | URL |
|---|---|
| Public license verification form (AJAX) | [elicense.ohio.gov/oh\_verifylicense](https://elicense.ohio.gov/oh_verifylicense) |
| Ohio eLicense home | [elicense.ohio.gov](https://elicense.ohio.gov) |
| Board of Pharmacy / Nursing / Medical | [pharmacy.ohio.gov](https://pharmacy.ohio.gov) · [nursing.ohio.gov](https://nursing.ohio.gov) · [med.ohio.gov](https://med.ohio.gov) |

#### Engineering details

- **Playwright + Chromium** — the only reliable way to drive a JSF + Ajax4JSF UI in 2026
- **`networkidle` + element-wait fallbacks** guard the empty-DOM-on-mount problem; per-board state reset dodges stale `ViewState` tokens
- **Configurable `requestDelay`** (default 2s) keeps the portal happy; Apify proxy support via `proxyConfiguration`
- **Status normalization** — Active / Inactive / Suspended / Revoked / Expired / Surrendered / Not Renewed / Denied / Pending all mapped to canonical strings
- **Address parsing** splits single-string addresses into `address` / `city` / `county` / `state` / `zip`; jobs are resumable via Apify state persistence

***

### Input Parameters

```json
{
  "boards": ["Board of Pharmacy", "Board of Nursing"],
  "licenseTypes": ["Pharmacist", "Registered Nurse"],
  "searchName": "Smith",
  "searchLicenseNumber": "",
  "statusFilter": "active_only",
  "counties": ["Franklin", "Cuyahoga"],
  "scrapeDetails": false,
  "maxRecords": 1000,
  "maxPages": 0,
  "requestDelay": 2000,
  "proxyConfiguration": { "useApifyProxy": false }
}
```

#### Parameter reference

| Parameter | Type | Default | Description |
|---|---|---|---|
| `boards` | `array<string>` | `[]` | Ohio boards to search — e.g. `Board of Pharmacy`, `Board of Nursing`, `State Medical Board`, `Ohio State Dental Board`. Empty array = iterate every board the portal exposes. |
| `licenseTypes` | `array<string>` | `[]` | License types within the chosen boards — e.g. `Pharmacist`, `Pharmacy Technician`, `Registered Nurse`, `Doctor of Medicine`, `Professional Engineer`, `Real Estate Salesperson`. Empty = every license type per board. |
| `searchName` | `string` | `""` | Optional licensee/business-name search. Partial matches supported by the portal. |
| `searchLicenseNumber` | `string` | `""` | Optional exact license-number lookup. Bypasses bulk pagination. |
| `statusFilter` | `string` | `all` | `all` = every status. `active_only` = current Active licenses only. `inactive_only` = Suspended, Revoked, Expired, Inactive, Surrendered, Not Renewed, Denied, Pending. |
| `counties` | `array<string>` | `[]` | Post-filter by Ohio county. Examples: `Franklin` (Columbus), `Cuyahoga` (Cleveland), `Hamilton` (Cincinnati), `Summit` (Akron), `Montgomery` (Dayton), `Lucas` (Toledo), `Mahoning` (Youngstown), `Stark` (Canton). |
| `scrapeDetails` | `boolean` | `false` | If `true`, opens each license detail page for full address + disciplinary history. Slower but complete. |
| `maxRecords` | `integer` | `0` | Hard cap on total records emitted. `0` = unlimited. Set `50` for a cheap smoke test. |
| `maxPages` | `integer` | `0` | Hard cap on pages per search permutation. `0` = paginate to the end. |
| `requestDelay` | `integer` (ms) | `2000` | Pause between page navigations. Keep ≥1000 to avoid rate limiting. Range 500–10000. |
| `proxyConfiguration` | `object` | `{ "useApifyProxy": false }` | Apify proxy block. Use `RESIDENTIAL` group for the most resilient runs. |

***

### Output Schema

Every record uses the same flat JSON shape — whether the licensee is a Columbus pharmacist, a Cleveland Clinic RN, a Cincinnati dentist, a Toledo HVAC contractor, or a Dayton real estate broker. This lets downstream consumers ingest the entire dataset without per-board branching.

#### Fields

| Field | Type | Description |
|---|---|---|
| `licenseNumber` | `string` | Ohio license or registration number issued by the board |
| `board` | `string` | Issuing board — e.g. `Board of Pharmacy`, `Board of Nursing`, `State Medical Board` |
| `licenseType` | `string` | Specific license category — e.g. `Pharmacist`, `Registered Nurse`, `Doctor of Medicine`, `Professional Engineer` |
| `licenseeType` | `string` | `Individual` or `Business` / `Facility` |
| `lastName` | `string \| null` | Family name (individuals only) |
| `firstName` | `string \| null` | Given name (individuals only) |
| `businessName` | `string \| null` | Facility / firm / pharmacy name (business records only) |
| `licenseStatus` | `string` | Normalized status — see status reference below |
| `issueDate` | `string \| null` | License issue date, `YYYY-MM-DD` |
| `expirationDate` | `string \| null` | Current expiration date, `YYYY-MM-DD` |
| `address` | `string \| null` | Street address as filed with the board |
| `city` | `string \| null` | City |
| `county` | `string \| null` | Ohio county |
| `state` | `string \| null` | Two-letter state abbreviation (usually `OH`) |
| `zip` | `string \| null` | ZIP code (5 or 9 digits) |
| `disciplinaryAction` | `boolean \| null` | `true` if board disciplinary action exists on file |
| `detailUrl` | `string \| null` | Direct link back to the source license detail page |
| `scrapedAt` | `string` | ISO-8601 timestamp of extraction |

#### Example: Individual pharmacist (Columbus)

```json
{
  "licenseNumber": "03-9-99999",
  "board": "Board of Pharmacy",
  "licenseType": "Pharmacist",
  "licenseeType": "Individual",
  "lastName": "MARTINEZ",
  "firstName": "ELENA",
  "businessName": null,
  "licenseStatus": "Active",
  "issueDate": "2014-07-15",
  "expirationDate": "2027-09-15",
  "address": "1234 N HIGH ST",
  "city": "COLUMBUS",
  "county": "Franklin",
  "state": "OH",
  "zip": "43201",
  "disciplinaryAction": false,
  "detailUrl": "https://elicense.ohio.gov/oh_verifylicense?id=99999",
  "scrapedAt": "2026-05-16T13:22:04.000Z"
}
```

#### Example: Cincinnati pharmacy facility (Terminal Distributor)

```json
{
  "licenseNumber": "02-2-99999",
  "board": "Board of Pharmacy",
  "licenseType": "Terminal Distributor of Dangerous Drugs",
  "licenseeType": "Business",
  "lastName": null,
  "firstName": null,
  "businessName": "WALGREEN CO #09999",
  "licenseStatus": "Active",
  "issueDate": "2008-04-01",
  "expirationDate": "2027-04-30",
  "address": "5550 RIDGE AVE",
  "city": "CINCINNATI",
  "county": "Hamilton",
  "state": "OH",
  "zip": "45213",
  "disciplinaryAction": false,
  "detailUrl": "https://elicense.ohio.gov/oh_verifylicense?id=99999",
  "scrapedAt": "2026-05-16T13:22:04.000Z"
}
```

A third typical record (e.g., a Cleveland RN with `licenseStatus: "Probation"` and `disciplinaryAction: true`) follows the same flat shape — only `board`, `licenseType`, and `disciplinaryAction` change.

***

### License Status Reference

The actor normalizes every status string against a canonical taxonomy. Use the `statusFilter` parameter to bucket them at the source.

#### Active statuses (`active_only`)

| Status | Meaning |
|---|---|
| `Active` | License current and in good standing — may legally practice or operate |
| `Active - Renewed` | Renewed within the current cycle |
| `Active - In Renewal` | Renewal application submitted, still in good standing |
| `Probation` | Active but subject to board-imposed conditions |
| `Restricted` | Active with scope-of-practice limitations |

#### Inactive statuses (`inactive_only`)

| Status | Meaning |
|---|---|
| `Inactive` | Voluntarily on inactive status — may not practice |
| `Expired` | License lapsed past renewal deadline |
| `Suspended` | Temporarily barred by board action |
| `Revoked` | Permanently terminated by board action |
| `Surrendered` | Voluntarily surrendered (often pre-disciplinary) |
| `Not Renewed` | Allowed to lapse without active surrender |
| `Denied` | Application denied by board |
| `Pending` | Application in review (not yet authorized to practice) |
| `Closed` | Facility/business permanently closed |

> **Tip:** Healthcare compliance teams typically want `statusFilter: "active_only"` for credentialing and `statusFilter: "all"` for change-detection diffs against the previous day's run.

***

### Ohio State Boards Covered (Reference Table)

| Board | Common License Types |
|---|---|
| State Board of Pharmacy | Pharmacist, Pharmacy Intern, Pharmacy Technician, Terminal Distributor of Dangerous Drugs (TDDD), Wholesale Distributor, Outsourcing Facility |
| Ohio Board of Nursing | RN, LPN, APRN, CRNA, CNP, CNS, CNA, Medication Aide, Community Health Worker |
| State Medical Board of Ohio | MD, DO, Physician Assistant, Podiatrist, Massage Therapist, Dietitian, Anesthesiologist Assistant, Genetic Counselor, Respiratory Care |
| Ohio State Dental Board | Dentist, Dental Hygienist, Dental Assistant Radiographer, EFDA |
| Chiropractic / Optometry / Optical Dispensers | DC, Acupuncturist, OD (TPA), Optical Dispenser |
| Speech & Hearing / OT / PT / Athletic Trainers | SLP, Audiologist, OT, OTA, PT, PTA, Athletic Trainer |
| Counselor / Social Worker / MFT / Chemical Dependency / Psychology | LPC, LPCC, LSW, LISW, LMFT, IMFT, LCDC, LICDC, OCPS, Psychologist |
| Veterinary / Embalmers & Funeral Directors / Cosmetology | Veterinarian, Vet Tech, Embalmer, Funeral Director, Crematory, Cosmetologist, Barber, Manicurist, Esthetician |
| Engineers & Surveyors / Architects / Accountancy | PE, PS, Engineering / Surveying Intern, Architect, Landscape Architect, CPA, Public Accountant Firm |
| Construction Industry Licensing Board (OCILB) | Electrical, HVAC, Hydronics, Plumbing, Refrigeration Contractor |
| Division of Real Estate & Professional Licensing | Broker, Salesperson, Appraiser, Foreign Real Estate Dealer |
| Long-Term Services & Supports / Orthotics-Prosthetics-Pedorthics | Nursing Home / Assisted Living Administrator, Orthotist, Prosthetist, Pedorthist |

***

### Use Cases

#### Healthcare Staffing & Travel Pharmacy / Nursing

Travel agencies, locum firms, and per-diem platforms placing clinicians into Ohio facilities use this dataset to:

- **Verify Ohio licenses** for every pharmacist, RN, LPN, APRN, MD, or DO before assignment
- **Source candidates** by county — active RNs in Cuyahoga (Cleveland) or pharmacists in Franklin (Columbus)
- **Monitor expirations** with refreshed expiration dates so credentials never lapse mid-contract
- **Filter out disciplinary issues** automatically with the `disciplinaryAction` boolean
- **Build talent pipelines** for Cleveland Clinic, OhioHealth, ProMedica, TriHealth, Premier Health

#### Credentialing, Compliance & Provider Enrollment

Hospital systems, MSOs, PBMs, ACOs, and payer networks use Ohio eLicense data to:

- **Automate primary-source verification (PSV)** for Joint Commission, NCQA, URAC, DNV-GL audits
- **Catch status changes** within 24 hours — `Active → Suspended` triggers immediate clinician suspension
- **Maintain audit-ready logs** with timestamped `scrapedAt` fields proving exactly when verification ran
- **Replace expensive per-lookup verification subscriptions** that charge per click
- **Sweep entire panels** of Ohio-licensed providers in one overnight run

#### B2B Sales & Lead Generation

Pharma reps, medical-device vendors, EHR / pharmacy software companies, dental supply firms, contractor SaaS, and real-estate proptech use the dataset to:

- **Build targeted Ohio lead lists** filtered by board, county, license type, or business size
- **Identify net-new license issuances** by diffing this week's run against last week's
- **Route territory assignments** based on professional density per ZIP, county, or metro
- **Enrich CRM records** (Salesforce, HubSpot, Pipedrive, Zoho) with current license status
- **Power direct mail** with verified addresses across Columbus, Cleveland, Cincinnati, Toledo, Akron, Dayton, Youngstown
- **Surface decision-maker pharmacists-in-charge** at every Ohio TDDD facility

#### Recruiting & Workforce Intelligence

Healthcare and skilled-trades recruiters use the dataset to:

- **Map clinician supply and demand** across rural Appalachian Ohio vs. urban metros
- **Build longitudinal pipelines** of newly issued licenses by year and metro
- **Benchmark with salary intelligence** by joining to [H1B Visa Database Scraper](https://apify.com/haketa/h1b-visa-database-scraper) or [Levels.fyi](https://apify.com/haketa/levels-fyi-scraper)
- **Identify recently-revoked clinicians** who may re-enter the workforce in a different state
- **Source skilled-trade contractors** (HVAC, electrical, plumbing) across all 88 Ohio counties

#### Compliance & Disciplinary Monitoring

Risk, legal, and quality teams use the dataset to:

- **Continuously monitor disciplinary events** for every Ohio-licensed provider in a panel
- **Build provider risk scores** combining disciplinary history with tenure and renewal history
- **Trigger alerts** to credentialing, HR, and payer enrollment when status flips
- **Investigate adverse events** by pulling full disciplinary history via `scrapeDetails: true`
- **Cross-state risk scoring** by joining Ohio data with [Texas TSBP](https://apify.com/haketa/tsbp-license-scraper), [Illinois IDFPR](https://apify.com/haketa/illinois-idfpr-license-scraper), [California DCA](https://apify.com/haketa/california-dca-license-scraper)

#### Legal, Litigation & M\&A Due Diligence

Attorneys, M\&A advisors, and investigators use the dataset to:

- **Verify licensure status** in malpractice, divorce, and licensing disputes
- **Build chronologies** of a license history (combined with archived daily runs)
- **Pre-acquisition diligence** on Ohio pharmacy chains, dental DSOs, urgent-care groups, contractor roll-ups, real estate brokerages
- **Identify all responsible parties** at a facility for discovery — PICs, qualifying agents, designated brokers
- **Validate expert witness credentials** before engagement

#### Insurance Underwriting

Carriers writing professional liability for Ohio clinicians, contractors, and real estate professionals use this data to:

- **Verify license validity** at policy bind and renewal
- **Auto-adjust pricing for disciplinary history**
- **Monitor portfolio risk** — flag insureds whose status changes mid-policy
- **Detect application fraud** by validating self-reported license numbers against the source

#### Market, Academic & Public-Health Research

Health-policy analysts, universities, state agencies, and journalists use the dataset to:

- **Study Ohio's healthcare workforce shortages** by region — Appalachian Ohio, Rust Belt cities, suburban growth zones
- **Investigate pharmacy and primary-care deserts** at ZIP and county granularity
- **Cover regulatory enforcement** — disciplinary trends, board action patterns, opioid-era pharmacy interventions
- **Benchmark workforce mix** — APRN-to-MD ratios, technician-to-pharmacist ratios, contractor density
- **Build investigative datasets** on contractor fraud, nursing-home staffing, or opioid prescribing

***

### Sample Queries & Recipes

#### Recipe 1 — All active Columbus pharmacies (Terminal Distributors)

Map every operating pharmacy facility in the state capital metro for sales territory planning.

```json
{
  "boards": ["Board of Pharmacy"],
  "licenseTypes": ["Terminal Distributor of Dangerous Drugs"],
  "statusFilter": "active_only",
  "counties": ["Franklin"],
  "scrapeDetails": true
}
```

#### Recipe 2 — Every disciplined Ohio RN statewide (compliance dashboard)

```json
{
  "boards": ["Board of Nursing"],
  "licenseTypes": ["Registered Nurse"],
  "statusFilter": "all",
  "scrapeDetails": true
}
```

Then post-filter on `disciplinaryAction === true`.

#### Recipe 3 — Cleveland-area MDs expiring in the next 90 days

```json
{
  "boards": ["State Medical Board"],
  "licenseTypes": ["Doctor of Medicine", "Doctor of Osteopathy"],
  "statusFilter": "active_only",
  "counties": ["Cuyahoga", "Lake", "Lorain", "Geauga", "Medina"]
}
```

Then filter downstream:

```python
from datetime import date, timedelta
cutoff = (date.today() + timedelta(days=90)).isoformat()
expiring = [r for r in records if r["expirationDate"] and r["expirationDate"] <= cutoff]
```

#### Recipe 4 — Cincinnati HVAC contractors for B2B campaign

```json
{
  "boards": ["Ohio Construction Industry Licensing Board"],
  "licenseTypes": ["HVAC Contractor"],
  "statusFilter": "active_only",
  "counties": ["Hamilton", "Butler", "Warren", "Clermont"]
}
```

#### Recipe 5 — Single license verification by license number

```json
{
  "boards": ["Board of Pharmacy"],
  "searchLicenseNumber": "03-9-99999"
}
```

#### Recipe 6 — Statewide active dentists for DSO M\&A pipeline

```json
{
  "boards": ["Ohio State Dental Board"],
  "licenseTypes": ["Dentist"],
  "statusFilter": "active_only",
  "scrapeDetails": true
}
```

#### Recipe 7 — Test run / smoke check before a full scrape

```json
{
  "boards": ["Board of Nursing"],
  "maxRecords": 50,
  "maxPages": 5
}
```

***

### Integration Examples

#### Google Sheets

Schedule the actor nightly via Apify Scheduler, add the **Google Sheets** integration, and receive a fresh Ohio license sheet every morning auto-deduplicated by `licenseNumber`.

#### Make.com / Zapier / n8n

Use the **Apify** connector to trigger downstream flows on new licenses (today minus yesterday), status changes (`Active → Suspended`), address changes, new disciplinary actions, or upcoming expirations.

#### Power BI / Tableau / Looker Studio

Connect Apify's REST API as a data source. Build dashboards for active provider count by Ohio metro (Columbus / Cleveland / Cincinnati / Toledo / Akron / Dayton / Youngstown), disciplinary rates by board and year, issuance trends, geographic heat maps across all 88 counties, and renewal-compliance windows.

#### Postgres / Snowflake / BigQuery

Use the [Apify webhook integration](https://docs.apify.com/platform/integrations/webhooks) to POST run results into your warehouse keyed on `licenseNumber` + `board` for a versioned, auditable Ohio licensure table.

#### Salesforce / HubSpot CRM Enrichment

Trigger a nightly Apify run, upsert against Account / Contact records keyed on license number. Status-change events create Tasks, open Cases, or trigger account-block flows.

#### Webhooks

Apify can POST a webhook to any URL on run completion — combine with AWS Lambda, Cloud Functions, or Workers for serverless ingestion into your data lake.

***

### Major Ohio Markets at a Glance

| Metro / County | Population | Licensure Significance |
|---|---|---|
| Columbus (Franklin) | 1.3M | State capital — every regulatory board headquartered nearby; densest pharmacist + nurse pool |
| Cleveland (Cuyahoga) | 1.2M | Cleveland Clinic, University Hospitals, MetroHealth — largest MD / RN concentration |
| Cincinnati (Hamilton) | 0.8M | UC Health, TriHealth, Mercy Health, Cincinnati Children's |
| Toledo (Lucas) | 0.4M | ProMedica system HQ, Mercy Health Toledo |
| Akron (Summit) | 0.5M | Summa Health, Cleveland Clinic Akron General |
| Dayton (Montgomery) | 0.5M | Premier Health, Kettering Health, Wright-Patterson AFB medical |
| Youngstown (Mahoning) | 0.2M | Mercy Health Youngstown, Steward Health |
| Canton (Stark) | 0.4M | Aultman Health, Mercy Medical |
| Lorain / Elyria (Lorain) | 0.3M | University Hospitals Elyria, Mercy Health Lorain |
| Springfield, Lima, Mansfield, Athens | varies | Regional health hubs serving central, NW, north-central, and Appalachian Ohio |

Ohio has **88 counties** total — every one is covered by the `counties` filter.

***

### Cost & Performance

| Metric | Value |
|---|---|
| Engine | Playwright (headless Chromium) |
| Runtime (targeted, ~500 records) | 3–6 minutes |
| Runtime (full board sweep, no details) | 20–60 minutes |
| Runtime (full sweep with `scrapeDetails`) | Varies — hours for large boards |
| Pricing model | Pay-per-event (pennies for targeted runs) |
| Data freshness | Live at runtime — directly off `elicense.ohio.gov` |
| Auth required | None |
| Proxy required | Optional — recommended for large concurrent runs |
| Memory footprint | 1024 MB minimum, 4096 MB recommended for full sweeps |

***

### Compliance, Privacy & Legal Notes

- **Public data only** — every field is published by the State of Ohio at [elicense.ohio.gov](https://elicense.ohio.gov/oh_verifylicense) under the Ohio Public Records Act (R.C. 149.43).
- **No PHI, SSNs, DOBs, or financial data** — only license-related public information. HIPAA does not apply.
- **Address data** is the **business / practice address** on file with the board — not personal residence in most cases.
- **No emails** are included; Ohio eLicense does not publish licensee email addresses.
- Compliance with **CAN-SPAM, TCPA, GDPR/CCPA, and Ohio data-broker rules** is the responsibility of the data consumer.
- The actor honors `requestDelay`, does not bypass any authentication / paywall / CAPTCHA, and interacts only with the public verification form.

> **Important:** Ohio license data may not be used for unlawful purposes including but not limited to identity fraud, stalking, or harassment. Always consult Ohio's Public Records Act guidance and your own counsel before using this data in regulated workflows.

***

### Frequently Asked Questions

#### How fresh is the data?

**Live at run time.** The actor queries the Ohio eLicense portal directly during each run, so every record reflects the portal's current state at scrape time. Schedule it daily for an always-up-to-date local copy.

#### How many records will I get?

Varies enormously by configuration. A single license-number lookup returns 1 record; a targeted single-county / single-license-type sweep typically returns hundreds to low thousands; a full statewide sweep across all 24 boards returns hundreds of thousands of records and takes hours.

#### Does this scraper require login or API keys to Ohio eLicense?

No. The eLicense verification portal is fully public — no login, no API key, no CAPTCHA at the verification page. You only need an Apify account to run the actor.

#### Does this work for other states (Texas, California, Illinois)?

Not this actor — Ohio is Ohio-specific because each state portal is a different tech stack. We maintain dedicated actors for [Texas TSBP](https://apify.com/haketa/tsbp-license-scraper), [Illinois IDFPR](https://apify.com/haketa/illinois-idfpr-license-scraper), [California DCA](https://apify.com/haketa/california-dca-license-scraper), [Colorado DORA](https://apify.com/haketa/colorado-professional-license-scraper), [Virginia DPOR](https://apify.com/haketa/virginia-dpor-license-scraper), [Minnesota DLI](https://apify.com/haketa/minnesota-dli-license-scraper), and more. See the Related Actors section below.

#### Can I use this for license verification at scale?

Yes. Many compliance teams run this actor daily for their Ohio panel, diff against the previous day, and trigger alerts on status changes. Apify schedules and webhooks make this fully automated.

#### Are licensee emails / phone numbers / dates included, and does the actor deduplicate?

**Emails: no** — Ohio eLicense does not publish them. **Phone numbers:** sometimes on detail pages for business records (enable `scrapeDetails: true`). **Dates:** `issueDate` and `expirationDate` are returned as fields — apply date-range filters downstream (SQL `WHERE`, Python, Sheets, dbt). **Dedup:** every record is keyed by `licenseNumber` + `board`; overlapping search permutations are deduped before push.

#### How long does a full board sweep take, and is proxy / CAPTCHA an issue?

Single board, no detail pages: ~20–60 minutes. Single board with detail pages: an hour or more. All 24 boards with detail pages: schedule overnight. Use `maxRecords` and `maxPages` to bound runtime. The public verification form has no CAPTCHA, but bursty parallel traffic from one IP can trigger temporary throttling — keep `requestDelay` ≥ 2000 ms or enable Apify residential proxy via `proxyConfiguration.useApifyProxy: true` for long jobs.

#### Free plan, scheduling, export formats?

Small runs fit the free tier comfortably; large sweeps work better on a paid tier with more memory. Apify's built-in Scheduler runs the actor on any cron expression. Export as JSON, CSV, Excel (XLSX), HTML, XML, JSON Lines, or RSS from the dataset view or API.

#### Does the dataset include closed or revoked records?

Yes — set `statusFilter: "all"` or `statusFilter: "inactive_only"` for historical analysis, churn studies, or sanction monitoring.

#### Can I get NPI numbers for Ohio clinicians?

NPI is issued federally (CMS), not Ohio. Cross-reference Ohio license numbers with the [NPPES NPI Registry](https://npiregistry.cms.hhs.gov/) for NPI enrichment.

#### How is disciplinary action determined?

Two ways: (a) status codes like `Probation`, `Suspended`, `Revoked`, `Surrendered`, and (b) explicit disciplinary-history fields parsed from the license detail page when `scrapeDetails: true`. The `disciplinaryAction` boolean is `true` if either signal fires.

#### Can I limit cost by capping records?

Yes — set `maxRecords` to a hard ceiling. The actor stops as soon as it's hit. Combine with `maxPages` for belt-and-suspenders cost control.

#### How does pay-per-event pricing work?

You pay a small actor-start fee plus a small per-dataset-item fee. Targeted runs cost pennies. No monthly minimums. Bug reports and feature requests: use the Issues tab on the Apify Store page.

***

### Related Apify Actors by Haketa

If you need licensing data from other states, related healthcare regulators, or contractor / real-estate licensure boards, check these:

- [Texas Pharmacy License Scraper (TSBP)](https://apify.com/haketa/tsbp-license-scraper) — Texas State Board of Pharmacy daily-CSV pipeline
- [Illinois IDFPR License Scraper](https://apify.com/haketa/illinois-idfpr-license-scraper) — Illinois multi-board state portal (closest sibling to Ohio)
- [California DCA License Scraper](https://apify.com/haketa/california-dca-license-scraper) — every California professional board
- [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 licensing
- [Minnesota DLI License Scraper](https://apify.com/haketa/minnesota-dli-license-scraper) — Minnesota Department of Labor & Industry
- [Washington L\&I Contractor License Scraper](https://apify.com/haketa/washington-li-contractor-license-scraper)
- [Arizona ROC Contractor License Scraper](https://apify.com/haketa/az-roc-contractor-license-scraper)
- [NC Licensing Board for General Contractors Scraper](https://apify.com/haketa/nc-licensing-board-for-general-contractors-scraper)
- [TTB Alcohol Permittee Scraper](https://apify.com/haketa/ttb-alcohol-permittee-scraper) — Federal TTB permittee database
- [SAM.gov Federal Contractor Entity Scraper](https://apify.com/haketa/sam-gov-federal-contractor-scraper)
- [BBB Business Scraper](https://apify.com/haketa/bbb-scraper) — Better Business Bureau business profiles

***

### Comparison vs. Alternatives

| Approach | Setup time | Data freshness | Cost (10K records) | Schema normalization | Compliance audit log |
|---|---|---|---|---|---|
| **This actor** | < 5 minutes | Live at run time | Pennies (pay-per-event) | Built-in | Automatic timestamps |
| Manual portal lookups | Hours/day | Live | Free + labor | None | None |
| Custom Playwright script | 2–4 days dev | Live | Free + infra + maintenance | DIY | DIY |
| Paid verification API | Hours setup | Real-time | $100–500+/mo + per-call fees | Yes | Yes |
| Ohio public-records request | Days–weeks | Stale by delivery | Variable | None | None |
| CAQH / VeriCred subscription | Days | Daily | Enterprise pricing | Yes | Yes |

***

### Why Pay-Per-Event Pricing?

- You only pay when the actor runs — no idle monthly subscription
- Charges scale with the data you actually consume; transparent line-item billing inside Apify
- No monthly minimums; sample with `maxRecords: 50` for a fraction of a cent
- Great for both ad-hoc one-time pulls and high-frequency scheduled jobs

***

### Changelog

| Version | Date | Notes |
|---|---|---|
| 1.0.0 | 2026-05 | Initial public release — Playwright-driven full eLicense portal coverage, 24 boards, normalized JSON output, county/board/status filtering, optional detail-page enrichment, pay-per-event pricing |

***

### Keywords

Ohio eLicense scraper · Ohio professional license lookup · OH pharmacy license verification · elicense.ohio.gov scraper · Ohio Board of Pharmacy data · Ohio Board of Nursing scraper · State Medical Board of Ohio data · Ohio dental license lookup · Ohio nursing medical contractor license · Ohio nurse license verification · Ohio RN LPN APRN database · Ohio physician MD DO lookup · Ohio Terminal Distributor of Dangerous Drugs · Ohio TDDD scraper · Cleveland Cincinnati Columbus license data · Columbus pharmacist database · Cleveland Clinic credentialing data · Cincinnati pharmacy lookup · Toledo nurse license · Akron contractor license · Dayton dentist database · Youngstown RN registry · Franklin County Ohio licensee lookup · Cuyahoga County Ohio license search · Hamilton County Ohio professional license · eLicense Ohio search API · Ohio license verification API · Ohio compliance monitoring · Ohio credentialing automation · Ohio professional license data extraction · Ohio multi-board license scraper · Ohio real estate broker license lookup · Ohio HVAC electrical contractor scraper · Ohio cosmetology license database · Ohio veterinary license search · Ohio Board of Cosmetology data · Ohio Engineers Surveyors lookup · Apify Ohio actor · OH state license API · Ohio license disciplinary action data · Ohio license expiration monitoring · Ohio license B2B lead generation

***

### Support

- **Bug reports:** Use the **Issues** tab on the Apify Store actor page
- **Feature requests:** Same place — please describe your use case so we can prioritize
- **Direct contact:** Through the Apify developer profile

If this actor saves you time, a **5-star rating** on the Apify Store helps other healthcare, compliance, recruiting, and contractor-services teams discover it. Thank you!

# Actor input Schema

## `boards` (type: `array`):

Which Ohio boards to search. Examples: 'Board of Pharmacy', 'Board of Nursing', 'State Medical Board'. Leave empty to search all boards.

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

License types to search within selected boards. Examples for Pharmacy: 'Pharmacist', 'Pharmacy Intern', 'Pharmacy Technician', 'Terminal Distributor of Dangerous Drugs'. Leave empty for all types.

## `searchName` (type: `string`):

Search by licensee name. Partial match supported. Leave empty to return all records.

## `searchLicenseNumber` (type: `string`):

Search by specific license number. Leave empty to return all records.

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

Filter by license status. 'all' returns every status. 'active\_only' returns only Active licenses. 'inactive\_only' returns Suspended, Revoked, Expired, Inactive.

## `counties` (type: `array`):

Filter by Ohio county. Examples: 'Franklin', 'Cuyahoga', 'Hamilton', 'Summit', 'Montgomery'. Leave empty for all counties.

## `scrapeDetails` (type: `boolean`):

Visit each license detail page for full information (address, disciplinary actions, certifications). Slower but more complete data.

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

Maximum total records to output. Set 0 for unlimited.

## `maxPages` (type: `integer`):

Maximum pages to scrape per search. Each page typically returns 10-25 records. Set 0 for unlimited.

## `requestDelay` (type: `integer`):

Delay between page navigations in milliseconds. Keep above 1000 to avoid rate limiting.

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

Optional proxy settings. Recommended if encountering rate limits or blocks.

## Actor input object example

```json
{
  "boards": [
    "Board of Pharmacy"
  ],
  "licenseTypes": [],
  "searchName": "Smith",
  "statusFilter": "all",
  "counties": [],
  "scrapeDetails": false,
  "maxRecords": 100,
  "maxPages": 0,
  "requestDelay": 2000,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

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

Ohio license or registration number

## `board` (type: `string`):

Issuing board (Board of Pharmacy, Board of Nursing etc.)

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

Pharmacist, Pharmacy Technician, RN, MD etc.

## `licenseeType` (type: `string`):

Individual or Business

## `lastName` (type: `string`):

Individual last name

## `firstName` (type: `string`):

Individual first name

## `businessName` (type: `string`):

Business or facility name

## `licenseStatus` (type: `string`):

Active / Inactive / Suspended / Expired / Revoked

## `issueDate` (type: `string`):

License issue date

## `expirationDate` (type: `string`):

License expiration date

## `address` (type: `string`):

Street address

## `city` (type: `string`):

City name

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

Ohio county

## `state` (type: `string`):

State abbreviation

## `zip` (type: `string`):

Postal code

## `disciplinaryAction` (type: `string`):

Whether disciplinary action exists

## `detailUrl` (type: `string`):

Direct link to license detail page

## `scrapedAt` (type: `string`):

ISO timestamp when scraped

# 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 = {
    "boards": [
        "Board of Pharmacy"
    ],
    "licenseTypes": [],
    "searchName": "Smith",
    "searchLicenseNumber": "",
    "counties": [],
    "maxRecords": 100,
    "proxyConfiguration": {
        "useApifyProxy": false
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("haketa/ohio-elicense-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 = {
    "boards": ["Board of Pharmacy"],
    "licenseTypes": [],
    "searchName": "Smith",
    "searchLicenseNumber": "",
    "counties": [],
    "maxRecords": 100,
    "proxyConfiguration": { "useApifyProxy": False },
}

# Run the Actor and wait for it to finish
run = client.actor("haketa/ohio-elicense-scraper").call(run_input=run_input)

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

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

```

## CLI example

```bash
echo '{
  "boards": [
    "Board of Pharmacy"
  ],
  "licenseTypes": [],
  "searchName": "Smith",
  "searchLicenseNumber": "",
  "counties": [],
  "maxRecords": 100,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}' |
apify call haketa/ohio-elicense-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=haketa/ohio-elicense-scraper",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

```json
{
    "openapi": "3.0.1",
    "info": {
        "title": "Ohio eLicense Scraper - Pharmacy & Professional",
        "description": "Ohio eLicense scraper & API: search Ohio professional licenses across boards and export license number, profession, status, name, address and dates. Professional license verification, compliance and lead generation — fast, no login.",
        "version": "0.0",
        "x-build-id": "fItxWMnFeJxVDhefB"
    },
    "servers": [
        {
            "url": "https://api.apify.com/v2"
        }
    ],
    "paths": {
        "/acts/haketa~ohio-elicense-scraper/run-sync-get-dataset-items": {
            "post": {
                "operationId": "run-sync-get-dataset-items-haketa-ohio-elicense-scraper",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for its completion, and returns Actor's dataset items in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        },
        "/acts/haketa~ohio-elicense-scraper/runs": {
            "post": {
                "operationId": "runs-sync-haketa-ohio-elicense-scraper",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor and returns information about the initiated run in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/runsResponseSchema"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/acts/haketa~ohio-elicense-scraper/run-sync": {
            "post": {
                "operationId": "run-sync-haketa-ohio-elicense-scraper",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for completion, and returns the OUTPUT from Key-value store in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        }
    },
    "components": {
        "schemas": {
            "inputSchema": {
                "type": "object",
                "properties": {
                    "boards": {
                        "title": "Boards",
                        "type": "array",
                        "description": "Which Ohio boards to search. Examples: 'Board of Pharmacy', 'Board of Nursing', 'State Medical Board'. Leave empty to search all boards.",
                        "items": {
                            "type": "string"
                        }
                    },
                    "licenseTypes": {
                        "title": "License Types",
                        "type": "array",
                        "description": "License types to search within selected boards. Examples for Pharmacy: 'Pharmacist', 'Pharmacy Intern', 'Pharmacy Technician', 'Terminal Distributor of Dangerous Drugs'. Leave empty for all types.",
                        "items": {
                            "type": "string"
                        }
                    },
                    "searchName": {
                        "title": "Name Search (optional)",
                        "type": "string",
                        "description": "Search by licensee name. Partial match supported. Leave empty to return all records."
                    },
                    "searchLicenseNumber": {
                        "title": "License Number (optional)",
                        "type": "string",
                        "description": "Search by specific license number. Leave empty to return all records."
                    },
                    "statusFilter": {
                        "title": "Status Filter",
                        "enum": [
                            "all",
                            "active_only",
                            "inactive_only"
                        ],
                        "type": "string",
                        "description": "Filter by license status. 'all' returns every status. 'active_only' returns only Active licenses. 'inactive_only' returns Suspended, Revoked, Expired, Inactive.",
                        "default": "all"
                    },
                    "counties": {
                        "title": "Counties (optional)",
                        "type": "array",
                        "description": "Filter by Ohio county. Examples: 'Franklin', 'Cuyahoga', 'Hamilton', 'Summit', 'Montgomery'. Leave empty for all counties.",
                        "items": {
                            "type": "string"
                        }
                    },
                    "scrapeDetails": {
                        "title": "Scrape Detail Pages",
                        "type": "boolean",
                        "description": "Visit each license detail page for full information (address, disciplinary actions, certifications). Slower but more complete data.",
                        "default": false
                    },
                    "maxRecords": {
                        "title": "Max Records",
                        "minimum": 0,
                        "type": "integer",
                        "description": "Maximum total records to output. Set 0 for unlimited.",
                        "default": 100
                    },
                    "maxPages": {
                        "title": "Max Pages",
                        "minimum": 0,
                        "type": "integer",
                        "description": "Maximum pages to scrape per search. Each page typically returns 10-25 records. Set 0 for unlimited.",
                        "default": 0
                    },
                    "requestDelay": {
                        "title": "Request Delay (ms)",
                        "minimum": 500,
                        "maximum": 10000,
                        "type": "integer",
                        "description": "Delay between page navigations in milliseconds. Keep above 1000 to avoid rate limiting.",
                        "default": 2000
                    },
                    "proxyConfiguration": {
                        "title": "Proxy Configuration",
                        "type": "object",
                        "description": "Optional proxy settings. Recommended if encountering rate limits or blocks."
                    }
                }
            },
            "runsResponseSchema": {
                "type": "object",
                "properties": {
                    "data": {
                        "type": "object",
                        "properties": {
                            "id": {
                                "type": "string"
                            },
                            "actId": {
                                "type": "string"
                            },
                            "userId": {
                                "type": "string"
                            },
                            "startedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "finishedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "status": {
                                "type": "string",
                                "example": "READY"
                            },
                            "meta": {
                                "type": "object",
                                "properties": {
                                    "origin": {
                                        "type": "string",
                                        "example": "API"
                                    },
                                    "userAgent": {
                                        "type": "string"
                                    }
                                }
                            },
                            "stats": {
                                "type": "object",
                                "properties": {
                                    "inputBodyLen": {
                                        "type": "integer",
                                        "example": 2000
                                    },
                                    "rebootCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "restartCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "resurrectCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "computeUnits": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "options": {
                                "type": "object",
                                "properties": {
                                    "build": {
                                        "type": "string",
                                        "example": "latest"
                                    },
                                    "timeoutSecs": {
                                        "type": "integer",
                                        "example": 300
                                    },
                                    "memoryMbytes": {
                                        "type": "integer",
                                        "example": 1024
                                    },
                                    "diskMbytes": {
                                        "type": "integer",
                                        "example": 2048
                                    }
                                }
                            },
                            "buildId": {
                                "type": "string"
                            },
                            "defaultKeyValueStoreId": {
                                "type": "string"
                            },
                            "defaultDatasetId": {
                                "type": "string"
                            },
                            "defaultRequestQueueId": {
                                "type": "string"
                            },
                            "buildNumber": {
                                "type": "string",
                                "example": "1.0.0"
                            },
                            "containerUrl": {
                                "type": "string"
                            },
                            "usage": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "integer",
                                        "example": 1
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "usageTotalUsd": {
                                "type": "number",
                                "example": 0.00005
                            },
                            "usageUsd": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "number",
                                        "example": 0.00005
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
```
