# 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:** Business, Lead generation
- **Features:** Limited permissions
- **Stats:** 6 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/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

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

> **Extract structured professional and occupational license records from the official Ohio eLicense portal (`elicense.ohio.gov`) across 17 Ohio regulatory boards.** Pull pharmacists, registered nurses, physicians, dentists, engineers, CPAs, counselors, cosmetologists, funeral directors, and facility licenses — complete with **license number, board, license type, status, city, county, ZIP code, disciplinary action flag, and direct detail page link**. Built for **healthcare credentialing, compliance monitoring, B2B lead generation, recruiting, M\&A due diligence, and Ohio workforce analytics.**

Export every result to **JSON, CSV, Excel (XLSX), HTML, XML, or REST API**.

[![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)]()
[![Scale](https://img.shields.io/badge/Scale-1%2C000%2B%20Records%20Multi--Board-blueviolet)]()
[![Coverage](https://img.shields.io/badge/Boards-17%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-green)]()

***

### ⚡ At a glance

| Feature | Details |
|---|---|
| **Official Portal Covered** | [Ohio eLicense Portal](https://elicense.ohio.gov/oh_verifylicense) (`elicense.ohio.gov`) |
| **Regulatory Boards** | **17 Ohio State Boards** — Nursing, Pharmacy, Medical, Dental, Chiropractic, Cosmetology & Barber, Counselor/Social Worker/MFT, Chemical Dependency, Behavioral Health, Engineers & Surveyors, Architects, Accountancy, Funeral Directors, Motor Vehicle Repair, Casino Control, Manufactured Homes, Elevator |
| **Search & Filter Modes** | Multi-board selection, license type filter, surname/name search, exact license number lookup, status filter (`all` / `active_only` / `inactive_only`), and 88-county filter |
| **Pagination & 1,000+ Scale** | Automatic 100-row table expansion + multi-page traversal + multi-board / multi-surname search expansion for `1,000+` deduplicated records |
| **Core Output Fields** | `licenseNumber`, `board`, `licenseType`, `licenseeType`, `lastName`, `firstName`, `businessName`, `licenseStatus`, `city`, `county`, `state`, `zip`, `disciplinaryAction`, `detailUrl`, `scrapedAt` |
| **Proxy Configuration** | Pre-configured Apify US Residential proxy (`RESIDENTIAL`, `US`) |
| **Export Formats** | JSON, CSV, Excel (XLSX), HTML, XML, JSON Lines, RSS, Apify REST API |

***

### 🧭 Table of contents

- [Why scrape Ohio eLicense?](#-why-scrape-ohio-elicense)
- [What this Ohio eLicense scraper does](#-what-this-ohio-elicense-scraper-does)
- [All 17 supported Ohio regulatory boards](#-all-17-supported-ohio-regulatory-boards)
- [High-value use cases](#-high-value-use-cases)
- [Input parameters](#-input-parameters)
- [Output schema & field reference](#-output-schema--field-reference)
- [Sample output records](#-sample-output-records)
- [Pagination & 1,000+ scale](#-pagination--1000-scale)
- [How to use & API examples](#️-how-to-use--api-examples)
- [Pro tips for credentialing & B2B lead generation](#-pro-tips-for-credentialing--b2b-lead-generation)
- [FAQ](#-faq)
- [Related state license scrapers](#-related-state-license-scrapers)
- [Changelog](#-changelog)

***

### 🔎 Why scrape Ohio eLicense?

The **Ohio eLicense portal** is the State of Ohio's centralized primary-source verification system for professional, healthcare, engineering, financial, and trade licenses — covering pharmacists in Columbus, registered nurses in Cleveland, physicians in Cincinnati, dentists in Toledo, professional engineers in Akron, and CPAs in Dayton.

This public record dataset is mission-critical for organizations across healthcare, staffing, B2B SaaS, insurance, and compliance:

- It confirms **who holds a valid Ohio license**, which **regulatory board and license category** governs them, whether their credential is **`Active`, `Inactive`, `Expired`, `Suspended`, or `Revoked`**, which **Ohio city, county, and ZIP code** they practice in, and whether the board has recorded **disciplinary action (`disciplinaryAction`)**.
- Every record includes a **direct `detailUrl`** back to the official Ohio eLicense verification page for audit-ready compliance logs.

However, the Ohio portal offers no public API and no bulk CSV download. Its interactive search form requires dynamic board-to-license-type cascading selections, caps single queries at ~150 rows, and paginates results in small browser tables whose "Next" button remains enabled even at the end of the dataset. This **Ohio eLicense scraper** automates the entire workflow — multi-board selection, 100-row page expansion, terminal-page detection, multi-query scaling, and client-side deduplication — delivering clean JSON, CSV, or Excel in minutes.

**Common reasons teams scrape Ohio eLicense:**

- ✅ **Primary-source license verification** for healthcare credentialing (Joint Commission, NCQA, URAC, DNV-GL)
- ✅ **Automated compliance & sanction monitoring** — detect `Active → Suspended / Revoked` status changes and `disciplinaryAction: true` flags
- ✅ **Targeted Ohio B2B lead generation** — build verified prospect lists by board, license type, and Ohio county (`Franklin`, `Cuyahoga`, `Hamilton`, `Summit`, `Montgomery`, `Lucas`)
- ✅ **Healthcare & technical recruiting** — source active RNs, LPNs, APRNs, pharmacists, physicians, social workers, and professional engineers across Ohio's 88 counties
- ✅ **M\&A, legal, and facility due diligence** — verify professional licenses across multi-site medical, dental, pharmacy, and engineering practices
- ✅ **Insurance underwriting & risk verification** — validate self-reported license numbers and check disciplinary history at policy bind and renewal

***

### ✨ What this Ohio eLicense scraper does

- ✅ **Covers all 17 Ohio regulatory boards** — select one or multiple boards in a single run (`boards`), and the scraper iterates through each selected board automatically.
- ✅ **Automatic 1,000+ record scaling** — overcomes the portal's ~150-row per-query ceiling by automatically switching the results table to 100 rows per page and expanding across selected boards, license types, and common surnames until your `maxRecords` target (`1,000+`) is reached.
- ✅ **Smart terminal-page guard** — detects when the portal's pagination reaches its final stale row and stops cleanly without wasting time on empty repeat pages.
- ✅ **Server + post-scrape filtering** — narrow by **Board**, **License Type**, **Last Name**, or **License Number** at the portal level, and filter by **License Status** (`all`, `active_only`, `inactive_only`) and **Ohio County** (all 88 counties supported).
- ✅ **Normalized disciplinary flag** — converts the portal's `Board Action?` column (`Yes` / `No`) into a clean boolean `disciplinaryAction` (`true` / `false`) on every record.
- ✅ **Zero-duplicate guarantee** — deduplicates every record by `board + licenseNumber` before saving to the dataset (`0 duplicates`).
- ✅ **Ready for automation** — export directly to JSON, CSV, Excel, or connect via the Apify Python/JS SDK, REST API, Make, Zapier, n8n, or Google Sheets.

***

### 🏛️ All 17 supported Ohio regulatory boards

Select one or more boards from the **Board** dropdown (`boards`):

| Board Value | Professions & License Types Covered |
|---|---|
| `Nursing Board` | Registered Nurse (`RN`), Licensed Practical Nurse (`LPN`), APRN (CNP, CRNA, CNS, CNM), Dialysis Technician, Medication Aide |
| `Board of Pharmacy` | Pharmacist, Pharmacy Intern, Registered/Certified Pharmacy Technician, Terminal Distributor of Dangerous Drugs (`TDDD`) |
| `Medical Board` | Doctor of Medicine (`MD`), Doctor of Osteopathic Medicine (`DO`), Physician Assistant (`PA`), Massage Therapist (`LMT`), Dietitian, Respiratory Care |
| `Dental Board` | Dentist (`DDS`/`DMD`), Dental Hygienist, Expanded Function Dental Auxiliary (`EFDA`), Dental X-Ray Machine Operator |
| `Chiropractic Board` | Chiropractor (`DC`), Acupuncture Certificate |
| `Cosmetology and Barber Board` | Cosmetologist, Advanced Cosmetologist, Barber, Esthetician, Manicurist, Hair Designer, Salon/Barbershop Facility |
| `Counselor, Social Worker, & Marriage & Family Therapist Board` | Licensed Professional Counselor (`LPC`/`LPCC`), Licensed Social Worker (`LSW`/`LISW`), Marriage & Family Therapist (`MFT`/`IMFT`) |
| `Chemical Dependency Professionals Board` | Chemical Dependency Counselor (`LCDC II`/`LCDC III`), Independent Chemical Dependency Counselor (`LICDC`), Prevention Specialist |
| `Department of Behavioral Health (DBH)` | Behavioral Health Providers & Certified Programs |
| `Engineers and Surveyors Board` | Professional Engineer (`PE`), Professional Surveyor (`PS`), Engineering/Surveying Firm |
| `Architects Board` | Registered Architect (`ARC`), Landscape Architect, Architectural Firm |
| `Accountancy Board` | Certified Public Accountant (`CPA`), Public Accountant (`PA`), Accounting Firm |
| `Board of Embalmers and Funeral Directors` | Embalmer, Funeral Director, Funeral Home, Crematory Facility |
| `Motor Vehicle Repair Board` | Collision Repair Operator, Auto Glass, Paintless Dent Repair, Airbag Repair |
| `Casino Control Commission` | Casino Gaming Employee, Key Employee, Gaming Vendor |
| `Department of Commerce - Manufactured Homes Program` | Manufactured Home Installer, Inspector, Dealer, Park Operator |
| `Div. of Industrial Compliance: Elevator` | Elevator Mechanic, Elevator Inspector, Elevator Contractor |

***

### 🎯 High-value use cases

#### 1. 🏥 Healthcare Credentialing & Primary-Source Verification

Hospitals, telehealth platforms, staffing agencies, and payers use this scraper to verify Ohio Nursing (`RN`, `LPN`, `APRN`), Medical (`MD`, `DO`, `PA`), Pharmacy, Dental, and Behavioral Health credentials, capturing `licenseStatus`, `disciplinaryAction`, and the primary-source `detailUrl` with an ISO `scrapedAt` timestamp.

#### 2. 🧲 Regional B2B Lead Generation across Ohio's 88 Counties

Target active CPAs, Professional Engineers, Architects, Cosmetology Salons, Funeral Homes, or Motor Vehicle Repair facilities in specific Ohio metros — such as **Columbus (`Franklin` & `Delaware` counties)**, **Cleveland (`Cuyahoga` & `Lake` counties)**, **Cincinnati (`Hamilton`, `Butler` & `Warren` counties)**, **Dayton (`Montgomery`)**, **Akron (`Summit`)**, or **Toledo (`Lucas`)**.

#### 3. 👩‍⚕️ Clinician & Skilled-Trade Recruiting

Build candidate sourcing lists of active Ohio pharmacists, registered nurses, physical/occupational therapists, social workers (`LISW`/`LPCC`), and engineers (`PE`) filtered by county and license type.

#### 4. 🚨 Continuous Disciplinary & Status Monitoring

Run scheduled checks across your provider network or vendor roster to immediately catch `Active → Suspended`, `Active → Expired`, or `disciplinaryAction: true` updates.

***

### 📥 Input parameters

Every input field is optional. Running with default settings executes a fast sample on the `Board of Pharmacy` with surname `"Smith"`.

| Parameter | Type | Default / Prefill | Description |
|---|---|---|---|
| `boards` | `array<string>` | Default: `["Nursing Board"]`, Prefill: `["Board of Pharmacy"]` | Pick one or more of the 17 Ohio regulatory boards. Multiple boards are searched sequentially in a single run. |
| `licenseTypes` | `array<string>` | `[]` (All types) | Optional list of license type names or partial matches within the chosen board (e.g., `["Pharmacist"]`, `["Registered Nurse"]`). |
| `searchName` | `string` | Prefill: `"Smith"` | Licensee last name search (partial matches supported). When `maxRecords > 150`, the actor automatically expands across additional common surnames once the initial surname is exhausted. |
| `searchLicenseNumber` | `string` | `""` | Exact license, application, or endorsement number lookup (e.g., `"RN.503619"`). |
| `statusFilter` | `string` | `"all"` | Filter records by status: `"all"` (every status), `"active_only"` (`Active`), or `"inactive_only"` (`Inactive`, `Expired`, `Suspended`, `Revoked`, `Closed`, `Surrendered`). |
| `counties` | `array<string>` | `[]` (All 88 counties) | Filter results to specific Ohio counties (e.g., `["Franklin", "Cuyahoga", "Hamilton"]`). |
| `scrapeDetails` | `boolean` | `false` | Optional flag for detail-level extraction. Keep `false` for fast, high-volume list exports. |
| `maxRecords` | `integer` | Default `0`, Prefill `50` | Maximum total license records to save. Set `1000`+ for large multi-query exports or `0` for unlimited. |
| `maxPages` | `integer` | `0` (Unlimited) | Maximum result pages to walk per search query (`0` = automatic pagination governed by `maxRecords`). |
| `requestDelay` | `integer` | `1500` | Polite pause between page transitions in milliseconds (`500`–`10000`). |
| `proxyConfiguration` | `object` | Apify US Residential | Proxy configuration. Defaults to Apify Residential (`US`), which is required to load the Ohio portal reliably. |

#### Example input (Multi-Board 1,000+ Record Run)

```json
{
  "boards": [
    "Nursing Board",
    "Board of Pharmacy",
    "Medical Board"
  ],
  "licenseTypes": [],
  "searchName": "Smith",
  "searchLicenseNumber": "",
  "statusFilter": "active_only",
  "counties": [],
  "scrapeDetails": false,
  "maxRecords": 1000,
  "maxPages": 0,
  "requestDelay": 1500,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": ["RESIDENTIAL"],
    "apifyProxyCountry": "US"
  }
}
```

***

### 📤 Output schema & field reference

Every record across all 17 Ohio boards is mapped to a consistent 18-field JSON object.

#### 🪪 License Identity & Classification

| Field | Type | Coverage | Description |
|---|---|---|---|
| `licenseNumber` | `string` | 🟢 High (100%) | Official Ohio license, registration, or endorsement number (e.g., `LPN.182864.MEDS-IV`, `03.135421`) |
| `board` | `string` | 🟢 High (100%) | Issuing Ohio regulatory board name (e.g., `Board of Pharmacy`, `Nursing Board`) |
| `licenseType` | `string` | 🟢 High (100%) | Specific license type (e.g., `Pharmacist`, `Registered Nurse (RN)`, `Certified Public Accountant`) |
| `licenseeType` | `string \| null` | 🟡 Conditional (~35%) | Sub-category or endorsement classification when exposed by the board (e.g., `MEDS-IV`, `CSR`) |

#### 👤 Licensee Name & Business Identity

| Field | Type | Coverage | Description |
|---|---|---|---|
| `lastName` | `string \| null` | 🟢 High (100% individuals) | Individual licensee family name (`null` on business/facility licenses) |
| `firstName` | `string \| null` | 🟢 High (100% individuals) | Individual licensee given and middle name (`null` on business/facility licenses) |
| `businessName` | `string \| null` | 🟡 Conditional (Facilities) | Business, pharmacy, clinic, salon, or firm name on corporate/facility licenses |

#### 🛡️ Status, Compliance & Dates

| Field | Type | Coverage | Description |
|---|---|---|---|
| `licenseStatus` | `string` | 🟢 High (100%) | Current license status (`Active`, `Inactive`, `Expired`, `Suspended`, `Revoked`, `Closed`, etc.) |
| `disciplinaryAction` | `boolean \| null` | 🟢 High (100%) | `true` if the portal's `Board Action?` flag is `Yes`, `false` if `No` |
| `issueDate` | `string \| null` | 🟡 Conditional (Detail mode) | License original issue date (`YYYY-MM-DD`) |
| `expirationDate` | `string \| null` | 🟡 Conditional (Detail mode) | Current license expiration date (`YYYY-MM-DD`) |

#### 📍 Location & Audit Metadata

| Field | Type | Coverage | Description |
|---|---|---|---|
| `address` | `string \| null` | 🟡 Conditional (Detail mode) | Street address on file with the board |
| `city` | `string` | 🟢 High (~100%) | Practice or mailing city |
| `county` | `string \| null` | 🟢 High (~99%) | Ohio county name (e.g., `Franklin`, `Cuyahoga`, `Hamilton`, `Richland`) |
| `state` | `string` | 🟢 High (~100%) | Two-letter state code (typically `OH`) |
| `zip` | `string \| null` | 🟢 High (~99%) | 5-digit or 9-digit postal ZIP code |
| `detailUrl` | `string` | 🟢 High (100%) | Direct URL to the official Ohio eLicense detail record (`oh_verifylicensedetails?pid=…`) |
| `scrapedAt` | `string` | 🟢 High (100%) | ISO-8601 UTC timestamp of extraction |

***

### 📋 Sample output records

#### Individual Licensee Record (Nursing Board)

```json
{
  "licenseNumber": "LPN.182864.MEDS-IV",
  "board": "Nursing Board",
  "licenseType": "Licensed Practical Nurse (LPN)",
  "licenseeType": "MEDS-IV",
  "lastName": "Smith",
  "firstName": "Charity Danielle",
  "businessName": null,
  "licenseStatus": "Active",
  "issueDate": null,
  "expirationDate": null,
  "address": null,
  "city": "Mansfield",
  "county": "Richland",
  "state": "OH",
  "zip": "44903",
  "disciplinaryAction": false,
  "detailUrl": "https://elicense.ohio.gov/oh_verifylicensedetails?pid=a0R8y000000HNsOEAW",
  "scrapedAt": "2026-11-10T03:05:00.000Z"
}
```

#### Pharmacist Record (Board of Pharmacy)

```json
{
  "licenseNumber": "03.128941",
  "board": "Board of Pharmacy",
  "licenseType": "Pharmacist",
  "licenseeType": null,
  "lastName": "Smith",
  "firstName": "Benjamin Michael",
  "businessName": null,
  "licenseStatus": "Active",
  "issueDate": null,
  "expirationDate": null,
  "address": null,
  "city": "Columbus",
  "county": "Franklin",
  "state": "OH",
  "zip": "43215",
  "disciplinaryAction": false,
  "detailUrl": "https://elicense.ohio.gov/oh_verifylicensedetails?pid=a0R8y000000K9pLEAS",
  "scrapedAt": "2026-11-10T03:05:12.000Z"
}
```

#### Facility / Pharmacy License Record (Board of Pharmacy)

```json
{
  "licenseNumber": "02.149200",
  "board": "Board of Pharmacy",
  "licenseType": "Terminal Distributor of Dangerous Drugs (TDDD)",
  "licenseeType": "Clinic / Retail",
  "lastName": null,
  "firstName": null,
  "businessName": "Buckeye Community Pharmacy LLC",
  "licenseStatus": "Active",
  "issueDate": null,
  "expirationDate": null,
  "address": null,
  "city": "Cleveland",
  "county": "Cuyahoga",
  "state": "OH",
  "zip": "44114",
  "disciplinaryAction": false,
  "detailUrl": "https://elicense.ohio.gov/oh_verifylicensedetails?pid=a0R8y000000M1xTEAS",
  "scrapedAt": "2026-11-10T03:05:18.000Z"
}
```

***

### 🔁 Pagination & 1,000+ scale

- **100-row DataTables acceleration:** As soon as search results render, the actor switches the portal's client-side table length from `10` rows per page to `100` rows per page — cutting page transitions by **10×** and dramatically reducing run duration.
- **Multi-board & multi-surname deep expansion:** Because the Ohio portal limits any single form submission to roughly 150 matching records, requesting `maxRecords: 1000+` automatically iterates across all selected `boards`, `licenseTypes`, and a built-in sequence of high-frequency surnames (`Smith`, `Johnson`, `Miller`, `Williams`, `Jones`, `Brown`, `Davis`, `Wilson`, `Moore`, `Taylor`, etc.) within the same browser session until `maxRecords` is reached.
- **Terminal-page detection & deduplication:** Every record is deduplicated by `board + licenseNumber`, and the pagination loop automatically stops when consecutive pages yield 0 new unique records.

***

### ⚙️ How to use & API examples

1. Open **[Ohio eLicense Scraper](https://apify.com/haketa/ohio-elicense-scraper)** in the Apify Console.
2. Select one or more **Boards** (e.g., `Board of Pharmacy`, `Nursing Board`, `Medical Board`).
3. Optionally specify **License types**, **Name search**, **Status**, or **Counties**.
4. Set **Max records** (`50` for a quick test, `1000+` for bulk runs) and click **Start**.

#### Python (`apify-client`)

```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", "Nursing Board"],
    "statusFilter": "active_only",
    "counties": ["Franklin", "Cuyahoga", "Hamilton"],
    "maxRecords": 500,
    "maxPages": 0,
})

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

#### Node.js (`apify-client`)

```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 Pharmacy'],
    searchName: 'Smith',
    statusFilter: 'all',
    maxRecords: 200,
});

const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(`Extracted ${items.length} Ohio license records`);
```

***

### 💡 Pro tips for credentialing & B2B lead generation

- **Keep the default US Residential proxy enabled:** The Ohio eLicense portal sits behind an enterprise bot-defense gateway that blocks datacenter IPs. The default Apify US Residential proxy is pre-configured so the portal loads cleanly every time.
- **Combine multiple boards in one run:** Pass multiple boards in `boards` (e.g., `["Nursing Board", "Board of Pharmacy", "Medical Board"]`) to collect cross-specialty healthcare rosters in a single browser session without paying cold-start overhead multiple times.
- **Use `statusFilter: "active_only"` for lead generation:** Keep only currently active licensees when building recruiting or sales lists, or switch to `"inactive_only"` to identify lapsed/suspended credentials.
- **Filter by Ohio metro counties:** Select `Franklin`, `Delaware`, `Licking`, and `Fairfield` for Greater Columbus; `Cuyahoga`, `Lake`, `Lorain`, and `Medina` for Greater Cleveland; or `Hamilton`, `Butler`, `Warren`, and `Clermont` for Greater Cincinnati.

***

### ❓ FAQ

#### How many Ohio license records can I extract in a single run?

A single surname query on one board yields up to ~150 records (the portal's per-search cap). When you set `maxRecords: 1000+` (or select multiple boards), the scraper automatically chains multiple board and surname searches in the same session to deliver **1,000+ unique, deduplicated records**.

#### How fast is a typical run?

With 100-row table expansion enabled, a 50-record prefill run completes in **~35–60 seconds** (including browser launch and portal gateway handoff), and a 150-record single-search sweep finishes in **~45–75 seconds**.

#### Why is a US Residential proxy required?

The State of Ohio fronts `elicense.ohio.gov` with an F5/Shape bot-defense challenge that rejects datacenter IP ranges. Using Apify's US Residential proxy (`RESIDENTIAL`, country `US`) allows the portal form to render reliably.

#### Can I search multiple boards at once?

**Yes!** Unlike earlier versions that only processed the first selected board, v1.2+ iterates through **every board** you select in the `boards` array until `maxRecords` is met.

#### Are email addresses included?

Ohio eLicense does not publish licensee email addresses on public verification records. Each record provides full name, license number, board, license type, status, city, county, state, ZIP code, disciplinary flag, and the official `detailUrl`.

***

### 🔗 Related state license scrapers

- [Colorado Professional License Scraper (DORA)](https://apify.com/haketa/colorado-professional-license-scraper)
- [California DCA Professional License Scraper](https://apify.com/haketa/california-dca-license-scraper)
- [Illinois License Scraper (IDFPR)](https://apify.com/haketa/illinois-idfpr-license-scraper)
- [Virginia DPOR Professional License Scraper](https://apify.com/haketa/virginia-dpor-license-scraper)
- [Minnesota DLI Professional License Scraper](https://apify.com/haketa/minnesota-dli-license-scraper)
- [Texas Pharmacy License Scraper (TSBP)](https://apify.com/haketa/tsbp-license-scraper)
- [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)

***

### 📋 Changelog

| Version | Date | Notes |
|---|---|---|
| **1.2.0** | **2026-11** | **Unlocked 1,000+ multi-board & multi-surname pagination + 100-row page acceleration.** Added automatic switching of DataTables `results_length` from 10 to 100 rows per page (10× fewer page clicks), unlocked multi-board iteration across all selected `boards`, and added automatic surname expansion (`DEFAULT_SURNAMES`) so runs with `maxRecords: 1000+` seamlessly scale past the portal's ~150-row per-query cap. |
| **1.1.0** | **2026-11** | **Terminal-page guard & residential reliability.** Added stale-page detection so the scraper stops cleanly when DataTables reaches the end of results, improved board dropdown matching, and verified 100% core field coverage with 0 duplicates. |
| **1.0.0** | **2026-05** | Initial release of Ohio eLicense Scraper supporting 17 Ohio regulatory boards, county and status filtering, and disciplinary action extraction. |

# Changelog

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

# Actor input Schema

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

Pick the Ohio regulatory board(s) you want to pull licenses from. Leave on the default (Board of Nursing) for a quick sample, or choose one that fits your use case.

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

Narrow to a specific license type inside the chosen board, e.g. 'Pharmacist', 'Registered Nurse', 'Doctor of Medicine'. Partial names work. Leave empty to include every license type on that board.

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

Type a licensee last name to find a specific person or family, e.g. 'Smith'. Partial matches are supported. Leave empty to return everyone on the board.

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

Look up one exact license or registration number. Leave empty to return all records.

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

Choose which license statuses to keep. 'All statuses' returns everything; 'Active only' keeps current licenses; 'Inactive / lapsed' keeps Suspended, Revoked, Expired and Inactive.

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

Keep only licensees in specific Ohio counties for territory or market analysis, e.g. Franklin (Columbus), Cuyahoga (Cleveland), Hamilton (Cincinnati). Leave empty to include all 88 counties.

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

Visit each license detail page for the fullest record (address, certifications, disciplinary detail). Slower but more complete. Leave off for a fast list-level export.

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

Maximum number of license records to export. Keep a limit for fast, low-cost sample runs. Set 0 for unlimited.

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

Maximum result pages to walk per search. Set 0 for unlimited (governed by maxRecords).

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

Pause between page loads, in milliseconds. Keep above 1000 for smooth, reliable runs.

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

Proxy settings. The Ohio portal loads reliably from a US residential exit, so keep the default (Apify Residential, US) unless you have a specific reason to change it.

## Actor input object example

```json
{
  "boards": [
    "Board of Pharmacy"
  ],
  "licenseTypes": [],
  "searchName": "Smith",
  "searchLicenseNumber": "",
  "statusFilter": "all",
  "counties": [],
  "scrapeDetails": false,
  "maxRecords": 50,
  "maxPages": 0,
  "requestDelay": 1500,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "US"
  }
}
```

# 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": 50,
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ],
        "apifyProxyCountry": "US"
    }
};

// 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": 50,
    "proxyConfiguration": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
        "apifyProxyCountry": "US",
    },
}

# 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(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 '{
  "boards": [
    "Board of Pharmacy"
  ],
  "licenseTypes": [],
  "searchName": "Smith",
  "searchLicenseNumber": "",
  "counties": [],
  "maxRecords": 50,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "US"
  }
}' |
apify call haketa/ohio-elicense-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,haketa/ohio-elicense-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/WTOcxEDMXmnrBOno5/builds/mhERr3fXY9EWkrSnC/openapi.json
