# openFDA Drug Labels (SPL) Scraper (`devilscrapes/openfda-drug-labels-scraper`) Actor

Export the openFDA Structured Product Labeling drug-label corpus as clean, flat rows — filterable by brand name, generic name, manufacturer, product NDC, or route, with control over which label sections (active ingredient, warnings, dosage, and more) land in your dataset as JSON, CSV, or Excel.

- **URL**: https://apify.com/devilscrapes/openfda-drug-labels-scraper.md
- **Developed by:** [DevilScrapes](https://apify.com/devilscrapes) (community)
- **Categories:** Lead generation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per event

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

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

## What's an Apify Actor?

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

## How to integrate an Actor?

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

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

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

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

# README

<div align="center">
  <img src=".actor/icon.svg" width="160" alt="Devil Scrapes mark" />

## openFDA Drug Labels (SPL) Scraper

**💰 $3.20 / 1 000 results**  ·  pay only for results  ·  no credit card to try

*We do the dirty work so your dataset stays clean.* 😈

Export the openFDA Structured Product Labeling drug-label corpus as clean, flat rows — filterable by brand name, generic name, manufacturer, product NDC, or route, with control over which label sections (active ingredient, warnings, dosage, and more) land in your dataset as JSON, CSV, or Excel.

</div>

***

### 🎯 What this scrapes

openFDA's `/drug/label.json` endpoint is the only place the FDA publishes the full structured product label (SPL) text — active ingredient, purpose, indications, warnings, dosage, and eleven more official sections — for every drug on the US market, keyed by brand name, generic name, manufacturer, NDC code, or route. The raw API ships every section as full paragraphs whether you need them or not, hides an intermittent cold-query 500, and caps pagination at 25 000 skipped records. This Actor normalises all of that into one dependable row shape with a section-level field-selection toggle, so a compliance or competitive-intel watchlist becomes a spreadsheet instead of a scripting project.

### 🔥 What we handle for you

- 🛡️ **Browser fingerprint rotation** — `curl-cffi` impersonates real Chrome / Firefox / Safari TLS handshakes on every request.
- 🔁 **Retries with exponential backoff** on `408 / 429 / 5xx`, including openFDA's documented cold-query timeouts — up to 5 attempts per page.
- 🧱 **Stops cleanly at openFDA's pagination ceiling** instead of looping on a 400, and fails loud (not silently empty) if the endpoint goes down for 3 consecutive pages.
- 🧊 **Per-record fault isolation** — one malformed record never takes down the run; we skip it, log it, and keep paging.
- 🧊 **Section-level field selection** — pick only the label sections you need; unset sections come back null instead of billing you for paragraphs you'll never read.
- 💰 **Pay-Per-Event pricing** — you only pay for results that hit your dataset. No data, no charge.

### 💡 Use cases

- Pull the official label text (warnings, dosage, active ingredients) for every SKU your marketplace or pharmacy sells, and flag listings whose copy drifts from the FDA's own record.
- Track how a manufacturer's label language changes across label versions for a drug you compete with.
- Build a regulatory/competitive-intel watchlist filtered by brand, generic name, manufacturer, NDC, or route.
- Feed a research dataset of OTC vs. prescription label sections (active ingredient, purpose, warnings) across thousands of records.

### ⚙️ How to use it

1. Click **Try for free** at the top of the page.
2. Fill in the input form — most fields have sensible defaults.
3. Click **Start**. Output streams into the run's dataset.
4. Export from **Storage → Dataset** as JSON, CSV, or Excel — or fetch via the API.

### 📥 Input

| Field | Type | Required | Default | Notes |
|---|---|:--:|---|---|
| `brandName` | `string` | no | 'Ibuprofen' | Filter: brand name (matches openfda.brand\_name). Only field with a prefill value in this Actor — combine with other… |
| `genericName` | `string` | no | '—' | Filter: generic name (matches openfda.generic\_name). |
| `manufacturerName` | `string` | no | '—' | Filter: manufacturer name (matches openfda.manufacturer\_name). |
| `productNdc` | `string` | no | '—' | Filter: product NDC code (matches openfda.product\_ndc). |
| `route` | `string` | no | '—' | Filter: administration route (matches openfda.route), e.g. ORAL. |
| `searchQuery` | `string` | no | '—' | Raw openFDA search expression, ANDed with the structured filters above (same convention as… |
| `fields` | `array` | no | \['active\_ingredient', 'purpose', 'indications\_and\_usage', 'warnings'] | Which label content sections to populate in each output row. openfda identity fields (brand/generic name, manufacturer,… |
| `maxResults` | `integer` | no | 100 | Stop after this many records. Each record is one billed result row. |
| `proxyConfiguration` | `object` | no | {'useApifyProxy': False} | openFDA is a public keyless government API and does not need a proxy. Leave this off unless your account requires… |

#### Example input

```json
{
  "brandName": "Ibuprofen",
  "maxResults": 3,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

### 📤 Output

Every row is one dataset item.

| Field | Type | Notes |
|---|---|---|
| `id` | `string` | openFDA record identifier. |
| `set_id` | `string` | openFDA label set identifier (groups versions of the same label). |
| `version` | `string` | Label version number within its set\_id. |
| `effective_time` | `string` | Label effective date, YYYYMMDD. |
| `brand_name` | `string` | Brand name (openfda.brand\_name\[0]). |
| `generic_name` | `string` | Generic name (openfda.generic\_name\[0]). |
| `manufacturer_name` | `string` | Manufacturer name (openfda.manufacturer\_name\[0]). |
| `product_ndc` | `array` | Product NDC code(s). |
| `product_type` | `string` | Product type, e.g. HUMAN OTC DRUG. |
| `route` | `array` | Administration route(s). |
| `substance_name` | `array` | Active substance name(s). |
| `application_number` | `string` | FDA application number, e.g. part341. |
| `active_ingredient` | `string` | Active ingredient(s) and strength. Null unless requested via `fields` and present on the source label. |
| `purpose` | `string` | Stated purpose of the product (OTC labels). Null unless requested via `fields` and present on the source label. |
| `indications_and_usage` | `string` | What the product is indicated/used for. Null unless requested via `fields` and present on the source label. |
| `warnings` | `string` | Warnings section text. Null unless requested via `fields` and present on the source label. |
| `do_not_use` | `string` | Do-not-use contraindications. Null unless requested via `fields` and present on the source label. |
| `ask_doctor` | `string` | Ask-a-doctor-before-use guidance. Null unless requested via `fields` and present on the source label. |
| `ask_doctor_or_pharmacist` | `string` | Ask-a-doctor-or-pharmacist guidance. Null unless requested via `fields` and present on the source label. |
| `when_using` | `string` | Guidance for while using the product. Null unless requested via `fields` and present on the source label. |
| `stop_use` | `string` | Conditions under which to stop use. Null unless requested via `fields` and present on the source label. |
| `pregnancy_or_breast_feeding` | `string` | Pregnancy/breastfeeding guidance. Null unless requested via `fields` and present on the source label. |
| `keep_out_of_reach_of_children` | `string` | Child-safety storage warning. Null unless requested via `fields` and present on the source label. |
| `dosage_and_administration` | `string` | Dosage and administration instructions. Null unless requested via `fields` and present on the source label. |
| `storage_and_handling` | `string` | Storage and handling instructions. Null unless requested via `fields` and present on the source label. |
| `inactive_ingredient` | `string` | Inactive ingredient(s) list. Null unless requested via `fields` and present on the source label. |
| `questions` | `string` | Questions/contact section text. Null unless requested via `fields` and present on the source label. |
| `package_label_principal_display_panel` | `string` | Principal display panel text. Null unless requested via `fields` and present on the source label. |

#### Example output

```json
{
  "id": "d3f8a2b1-9c44-4e11-8a77-2f6b1c9e0a12",
  "set_id": "b4e2f9c0-1234-4a56-9abc-def012345678",
  "version": "3",
  "effective_time": "20240115",
  "brand_name": "IBUPROFEN",
  "generic_name": "IBUPROFEN",
  "manufacturer_name": "Acme Pharmaceuticals Inc.",
  "product_ndc": [
    "12345-678-90"
  ],
  "product_type": "HUMAN OTC DRUG",
  "route": [
    "ORAL"
  ],
  "substance_name": [
    "IBUPROFEN"
  ],
  "application_number": "part341",
  "active_ingredient": "Ibuprofen 200 mg (NSAID)",
  "purpose": "Pain reliever/fever reducer",
  "indications_and_usage": "temporarily relieves minor aches and pains...",
  "warnings": "Allergy alert: Ibuprofen may cause a severe allergic reaction...",
  "do_not_use": null,
  "ask_doctor": null,
  "ask_doctor_or_pharmacist": null,
  "when_using": null,
  "stop_use": null,
  "pregnancy_or_breast_feeding": null,
  "keep_out_of_reach_of_children": null,
  "dosage_and_administration": null,
  "storage_and_handling": null,
  "inactive_ingredient": null,
  "questions": null,
  "package_label_principal_display_panel": null
}
```

### 💰 Pricing

Pay-Per-Event — you pay only when these events fire:

| Event | USD | What it is |
|---|---:|---|
| `actor-start` | $0.2 | One-off warm-up charge per run |
| `result` | $0.003 | Per dataset item |

Example: 1 000 results at the rates above ≈ **$3.20**. No subscription, no minimum, no card to start — Apify gives every new account $5 of free credit.

### 🚧 Limitations

- openFDA caps pagination at 25 000 records per query on this endpoint — narrow the search to go deeper.
- Filter values aren't escaped for embedded double-quote characters — avoid quotes inside brand, generic, manufacturer, NDC, or route filters.
- This Actor emits the current label record as returned by openFDA for a given query; it does not walk historical version history for a given set\_id.

### ❓ FAQ

**Do I need an API key?**

No. openFDA serves label data keylessly; the Actor stays inside the anonymous rate limit (240 requests/min, 1000/day per IP).

**Why are so many fields null on my rows?**

Two reasons, both intentional: a content section is null when you didn't select it in fields, and it's also null when the source label simply never populated that section (most prescription-only labels skip OTC-style sections like do\_not\_use or ask\_doctor entirely).

**What's the difference between this and the other openFDA Actors?**

This Actor is the only one covering /drug/label.json — the full structured product label text. openfda-adverse-events-scraper covers FAERS adverse-event reports, fda-510k-clearances-scraper covers device clearances, and fda-recalls-scraper covers recalls/enforcement actions. No output-row overlap between any of them.

### 💬 Your feedback

Spotted a bug, hit a weird edge case, or need a new field? Open an
issue on the Actor's **Issues** tab on Apify Console — we ship
fixes weekly and we read every report.

***

<div align="center">

Built by **[Devil Scrapes](https://apify.com/DevilScrapes)** 😈 — a small fleet of
opinionated public-data Actors. Honest pricing, real engineering, zero fine print.

</div>

# Actor input Schema

## `brandName` (type: `string`):

Filter: brand name (matches openfda.brand\_name). Only field with a prefill value in this Actor — combine with other filters cautiously since all set filters are ANDed together.

## `genericName` (type: `string`):

Filter: generic name (matches openfda.generic\_name).

## `manufacturerName` (type: `string`):

Filter: manufacturer name (matches openfda.manufacturer\_name).

## `productNdc` (type: `string`):

Filter: product NDC code (matches openfda.product\_ndc).

## `route` (type: `string`):

Filter: administration route (matches openfda.route), e.g. ORAL.

## `searchQuery` (type: `string`):

Raw openFDA search expression, ANDed with the structured filters above (same convention as fda-510k-clearances-scraper). Leave empty to skip.

## `fields` (type: `array`):

Which label content sections to populate in each output row. openfda identity fields (brand/generic name, manufacturer, NDC, route, substance, application number) and id/set\_id/version/effective\_time are always included regardless of this selection.

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

Stop after this many records. Each record is one billed result row.

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

openFDA is a public keyless government API and does not need a proxy. Leave this off unless your account requires egress through Apify Proxy.

## Actor input object example

```json
{
  "brandName": "Ibuprofen",
  "fields": [
    "active_ingredient",
    "purpose",
    "indications_and_usage",
    "warnings"
  ],
  "maxResults": 100,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

## `datasetItems` (type: `string`):

All dataset items as JSON.

## `datasetItemsCsv` (type: `string`):

Same data exported to CSV.

## `datasetView` (type: `string`):

Open the run dataset in the Console.

# 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 = {
    "brandName": "Ibuprofen"
};

// Run the Actor and wait for it to finish
const run = await client.actor("devilscrapes/openfda-drug-labels-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 = { "brandName": "Ibuprofen" }

# Run the Actor and wait for it to finish
run = client.actor("devilscrapes/openfda-drug-labels-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 '{
  "brandName": "Ibuprofen"
}' |
apify call devilscrapes/openfda-drug-labels-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,devilscrapes/openfda-drug-labels-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/HDcVH6nSKFQdHIRIn/builds/70tgH4ppdisYYcN3h/openapi.json
