# Clinical Trials & FDA API — ClinicalTrials.gov + openFDA (`insight.solutions/clinical-trials-fda-api`) Actor

Query ClinicalTrials.gov v2 and openFDA from one Actor. Search 600,000+ registered studies by condition, drug, sponsor, phase, status or location, and get FDA drug and device recalls, labels, NDC entries, approvals, 510(k) clearances and adverse-event reports for the same drug. No API key.

- **URL**: https://apify.com/insight.solutions/clinical-trials-fda-api.md
- **Developed by:** [Insight Solutions](https://apify.com/insight.solutions) (community)
- **Categories:** Business, Developer tools, AI
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.30 / 1,000 trial returneds

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?

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

## Clinical Trials & FDA API — ClinicalTrials.gov + openFDA

Give it a condition, a drug, a sponsor or a device name. Get back **600,000+
registered clinical studies** from ClinicalTrials.gov API v2 and records from
**fourteen openFDA datasets** — drug and device recalls, labels, approvals,
510(k) clearances, the NDC directory and adverse-event reports — in **one table
with one schema**.

No API key. No login. No browser. Both sources are free, public, keyless US
federal APIs, and one run answers *"every trial of semaglutide, plus every FDA
label, recall, approval and adverse-event report that names it"* without writing
two integrations.

**From $0.0005 per trial and $0.0003 per FDA record**, and every diagnostic row
is free.

```json
{
  "ok": true,
  "rowType": "trial",
  "nctId": "NCT04368728",
  "title": "Study to Describe the Safety, Tolerability, Immunogenicity, and Efficacy of RNA Vaccine Candidates Against COVID-19 in Healthy Individuals",
  "status": "COMPLETED",
  "statusVerifiedDate": "2026-03",
  "phases": ["PHASE2", "PHASE3"],
  "studyType": "INTERVENTIONAL",
  "leadSponsor": "BioNTech SE",
  "sponsorClass": "INDUSTRY",
  "collaborators": [{ "name": "Pfizer", "class": "INDUSTRY" }],
  "conditions": ["SARS-CoV-2 Infection", "COVID-19"],
  "meshConditions": ["COVID-19"],
  "interventions": [{ "type": "BIOLOGICAL", "name": "BNT162b1" }],
  "enrollment": 46969,
  "enrollmentType": "ACTUAL",
  "masking": "TRIPLE",
  "startDate": "2020-04-29",
  "primaryCompletionDate": "2023-02-10",
  "lastUpdatedAt": "2026-03-25",
  "hasResults": true,
  "locationsCount": 175,
  "countries": ["United States", "Argentina", "Brazil", "Germany", "South Africa", "Turkey (Türkiye)"],
  "primaryOutcomesCount": 64,
  "minAge": "12 Years",
  "stdAges": ["CHILD", "ADULT", "OLDER_ADULT"],
  "sourceUrl": "https://clinicaltrials.gov/study/NCT04368728"
}
```

### Try it in 30 seconds

**Input** — this is the Store prefill; paste it and run:

```json
{
  "conditions": ["type 2 diabetes"],
  "statuses": ["RECRUITING"],
  "maxTrials": 25,
  "fdaEndpoints": ["drug/enforcement"],
  "drugNames": ["semaglutide"],
  "maxFdaRecords": 25
}
```

Three HTTP requests, about fifteen seconds, **$0.021**, no key.

**Output** — one `trial` row per study and one `fda-record` row per FDA record, in one table; the fields you will use most are `rowType`, `nctId`, `title`, `status`, `phases` and `leadSponsor` on a trial, `endpoint`, `recordId`, `recordDate` and `title` on an FDA record (full list under *Output reference*). Anything that could not be read comes back as a free diagnostic row (`ok: false`, `errorType`, `error`) instead of a charge.

**Price** — $0.50 per 1,000 trials and $0.30 per 1,000 FDA records on the FREE tier (+$0.001 per run); `aggregate` rows, `summary` rows, every diagnostic row, and a query that matched nothing are all free. Pay-per-event, no API key, no browser, limited permissions — works over the Apify MCP server (`mcp.apify.com`) and with agentic (x402) payments.

**From code** — `client.actor("insight.solutions/clinical-trials-fda-api").call(run_input={"conditions": ["type 2 diabetes"], "maxTrials": 25})` with `apify-client`, or `POST https://api.apify.com/v2/acts/insight.solutions~clinical-trials-fda-api/run-sync-get-dataset-items`.

Three more that show the shape of the thing:

```json
// A sponsor's late-stage pipeline
{ "sponsors": ["Novo Nordisk"], "phases": ["PHASE3"], "statuses": ["RECRUITING"], "maxTrials": 200 }
```

```json
// A device clearance and recall feed for the current year
{ "fdaEndpoints": ["device/510k", "device/recall"], "fdaDateFrom": "2026-01-01", "maxFdaRecords": 500 }
```

```json
// A drug's top 100 reported reactions — 100 free rows, $0.001 for the whole run
{
  "fdaEndpoints": ["drug/event"],
  "drugNames": ["semaglutide"],
  "fdaCount": ["patient.reaction.reactionmeddrapt.exact"],
  "maxFdaRecords": 0
}
```

### Two sources, one dataset

| | ClinicalTrials.gov API v2 | openFDA |
|---|---|---|
| What it holds | 602,735 registered studies, worldwide | Drug and device recalls, SPL labels, approvals, 510(k)s, PMAs, the NDC directory, device classifications, and adverse-event reports for drugs, devices, food and animals |
| Row type | `trial` | `fda-record` |
| Search by | condition, intervention, sponsor, lead sponsor, title, outcome, NCT id, status, phase, study type, location or radius, start date, last update | drug name, device name, date window, raw openFDA expression |
| Price | $0.0005 | $0.0003 |
| Endpoints used | `/studies`, `/studies/<nctId>`, `/version` | the fourteen `<endpoint>.json` datasets |
| Key needed | no | no (an optional free key raises the limits) |

`rowType` also takes `aggregate` (a free `count=` facet term), `summary` (one
free per-source roll-up) and `diagnostic` (a free explanation). Every row carries
all 222 columns, null where they do not apply, so the export is one rectangular
table however the run was configured.

### Coverage and freshness, as numbers

ClinicalTrials.gov: **602,735 studies**, measured from `GET /api/v2/stats/size`
on 2026-09-15. Every run reads `GET /api/v2/version` and puts the registry's own
`apiVersion` and `dataTimestamp` on the summary row, so the freshness claim is a
fact from the source rather than a promise from us.

openFDA publishes a `last_updated` per dataset and they are **not** the same. It
is on every `fda-record` row as `endpointLastUpdated`. Measured 2026-09-15:

| Endpoint | Data as of | Behind by |
|---|---|---|
| `drug/label` | 2026-09-12 | days |
| `device/recall` | 2026-09-12 | days |
| `drug/ndc` | 2026-09-11 | days |
| `drug/drugsfda` | 2026-09-11 | days |
| `drug/enforcement` | 2026-09-09 | days |
| `device/enforcement` | 2026-09-09 | days |
| `food/enforcement` | 2026-09-09 | days |
| `device/event` (MAUDE) | 2026-09-08 | a week |
| `device/510k` | 2026-09-07 | a week |
| `device/pma` | 2026-09-07 | a week |
| `device/classification` | 2026-09-07 | a week |
| **`drug/event` (FAERS)** | **2026-07-30** | **six weeks** |
| **`food/event` (CAERS)** | **2026-07-07** | **ten weeks** |
| **`animalandveterinary/event`** | **2026-07-02** | **eleven weeks** |

The three adverse-event files lag by six weeks to a quarter, and that is the
single most useful thing to know before building a monitor on them. Concretely:
on 2026-09-15, `drug/event?search=receivedate:[20260801 TO 20260901]` returned
**no matches at all**. The same query across `[20260101 TO 20260901]` returned
tens of thousands. Enforcement and device endpoints are current within a week.

### Input reference

**ClinicalTrials.gov**

| Field | Type | Default | What it does |
|---|---|---|---|
| `conditions` | string\[] | `[]` | Condition or disease. Several entries are OR-ed; a phrase is quoted for you |
| `interventions` | string\[] | `[]` | Drug, device, procedure or behavioural intervention |
| `terms` | string\[] | `[]` | Free text across the whole record; also accepts raw Essie expressions |
| `sponsors` | string\[] | `[]` | Lead sponsor **or** collaborator |
| `leadSponsors` | string\[] | `[]` | Lead sponsor only |
| `titles` | string\[] | `[]` | Words in the brief or official title |
| `outcomes` | string\[] | `[]` | Words in the outcome measures |
| `nctIds` | string\[] | `[]` | Look up specific studies. Ignores the other search fields |
| `statuses` | enum\[] | `[]` | 14 values, `RECRUITING` through `WITHHELD` |
| `phases` | enum\[] | `[]` | `EARLY_PHASE1`, `PHASE1`–`PHASE4`, `NA` |
| `studyTypes` | enum\[] | `[]` | `INTERVENTIONAL`, `OBSERVATIONAL`, `EXPANDED_ACCESS` |
| `locationName` | string | `null` | A place name matched against study sites (`Boston`, `Texas`, `Germany`) |
| `location` | object | `null` | A radius: `{"lat":40.7128,"lon":-74.0060,"radiusMiles":50}` |
| `updatedSince` | date | `null` | Registry record last updated on or after |
| `startDateFrom` / `startDateTo` | date | `null` | Study start window |
| `sort` | enum | `LastUpdatePostDate:desc` | Also start date, enrolment, primary completion, relevance |
| `maxTrials` | integer | `100` | `0` means everything the filters allow |
| `trialFields` | enum | `summary` | `full` adds the complete `raw` study record, free |
| `includeResults` | boolean | `false` | The only setting that costs an extra request per study |
| `maxResultFetches` | integer | `25` | Ceiling on those extra requests |
| `maxLocationsPerTrial` | integer | `50` | `locationsCount` always reports the real number |

**openFDA**

| Field | Type | Default | What it does |
|---|---|---|---|
| `fdaEndpoints` | enum\[] | `[]` | Which of the fourteen datasets to search |
| `drugNames` | string\[] | `[]` | Brand, generic or substance name |
| `deviceNames` | string\[] | `[]` | Trade or generic device name |
| `fdaSearch` | string | `null` | Raw openFDA `search=` expression, AND-ed on |
| `fdaDateFrom` / `fdaDateTo` | date | `null` | Applied to each endpoint's own date field |
| `fdaCount` | string\[] | `[]` | Up to 20 fields to aggregate, and at most 20 count requests per run — free `aggregate` rows |
| `maxFdaRecords` | integer | `100` | **Per endpoint.** `0` fetches none, which is how you get aggregates only |
| `fdaFullText` | boolean | `false` | Keep full label text (rows can exceed 400 KB) |
| `fdaRaw` | boolean | `true` | Keep the trimmed source record in `raw` |
| `openFdaApiKey` | secret string | `null` | Optional. Raises the limits; everything works without it |

**Shared:** `includeSummary` (default true), `maxConcurrency` (2),
`maxRunSecs` (240), `proxyConfiguration` (`{"useApifyProxy": true}`).

### Output reference

A `trial` row carries the whole study: identity and titles, status and the month
the sponsor last verified it, phases, study type, design (allocation, model,
masking, who was masked), enrolment and whether it is actual or estimated, every
date with its `ACTUAL`/`ESTIMATED` type beside it, conditions and keywords plus
the registry's derived MeSH terms, interventions, sponsor and collaborators,
eligibility criteria and age range, sites (capped, with the true `locationsCount`
kept), countries, contacts, outcome measures and counts, references and PubMed
ids, and `unpostedEvents` when the registry has recorded that results are late.

A `fda-record` row:

```json
{
  "ok": true,
  "rowType": "fda-record",
  "endpoint": "drug/enforcement",
  "recordId": "D-0815-2026",
  "recordIdType": "recall_number",
  "recordDate": "2026-09-09",
  "recordDateField": "report_date",
  "endpointLastUpdated": "2026-09-09",
  "title": "Clindamycin Injection USP in 5% Dextrose, 900 mg per 50 mL…",
  "recallNumber": "D-0815-2026",
  "recallClassification": "Class II",
  "recallStatus": "Ongoing",
  "recallReason": "CGMP Deviations",
  "recallingFirm": "Baxter Healthcare Corporation",
  "firmCity": "Deerfield",
  "firmState": "IL",
  "firmCountry": "United States",
  "distributionPattern": "US Nationwide; Puerto Rico; and Chile.",
  "voluntaryMandated": "Voluntary: Firm initiated",
  "brandNames": ["CLINDAMYCIN PHOSPHATE"],
  "applicationNumbers": ["ANDA208084"],
  "productNdcs": ["0338-3410", "0338-3612", "0338-3814"],
  "reportedDates": {
    "recallInitiationDate": "2026-08-06",
    "centerClassificationDate": "2026-08-28",
    "terminationDate": null
  }
}
```

**Free row types.** `aggregate` — one term of a `count=` facet, with its rank and
the facet total. `summary` — one per source: status, phase, sponsor, country and
intervention-type breakdowns over the trials the run returned, the median
enrolment, the registry's `dataTimestamp`, and how many records matched upstream
per FDA endpoint. `diagnostic` — one per thing that could not be read or filter
that could not be applied, with an `errorType` from `no-results`, `not-found`,
`invalid-input`, `unsupported-filter`, `api-key-required`, `rate-limit`,
`forbidden`, `blocked`, `timeout`, `budget-exhausted`, `upstream-shape`,
`network`, `http`, `parse`.

### Pricing

Pay per event. No subscription, no per-request charge, no usage add-on.

| Event | FREE | BRONZE | SILVER | GOLD |
|---|---|---|---|---|
| Run started (`actor-start`) | $0.001 | $0.001 | $0.001 | $0.001 |
| **Clinical trial returned (`trial`)** | **$0.0005** | **$0.0005** | **$0.0004** | **$0.0003** |
| FDA record returned (`fda-record`) | $0.0003 | $0.0003 | $0.00024 | $0.00018 |

**$0.50 per 1,000 trials · $0.30 per 1,000 FDA records.**

| You ask for | You get | You pay |
|---|---|---|
| The example above | 25 trials + 25 FDA records + 2 free summaries | **$0.021** |
| 1,000 trials for one sponsor | 1,000 trials | **$0.501** |
| 1,000 FAERS reports for one drug | 1,000 FDA records | **$0.301** |
| A drug's top 100 reported reactions | 100 free aggregate rows | **$0.001** |
| 200 trials + 100 records across 4 FDA endpoints | 200 trials + 400 FDA records | **$0.221** |

#### What you are never charged for

- `aggregate` rows — a `count=` facet is up to 100 terms for one request.
- `summary` rows.
- Every diagnostic row, whatever the `errorType`.
- A query that matched nothing: openFDA's `404 NOT_FOUND`, or
  ClinicalTrials.gov's `{"totalCount": 0, "studies": []}`.
- A filter an endpoint cannot apply — a date window on `device/classification`, a
  drug name on a device endpoint — and every other `unsupported-filter` row.
- Rows that could not be written because your run budget or the time budget ran out.
- **Anything at all on a run that got no usable answer from either API.** The
  start fee is billed only after the first successful response *and* the first
  paid row, so an outage costs nothing and the run finishes FAILED.

### Rate limits and the optional API key

**openFDA, without a key:** 240 requests a minute and **1,000 a day, per IP
address**. Pages hold at most 100 records — ask for more and the API answers
`403 API_KEY_MISSING`, so this Actor never asks.

**openFDA, with a free key** from
<https://open.fda.gov/apis/authentication/>: 240 a minute and **120,000 a day,
per key**, and pages hold 1,000. Put it in `openFdaApiKey` (a secret field; it is
stripped from every `queryUrl` before a row is written). Nothing in this Actor
requires it.

**The keyless quota is counted per IP, and the Apify datacenter proxy shares
exits.** That is the one place the default proxy setting is a liability rather
than hygiene. For heavy openFDA work without a key, **clear
`proxyConfiguration`** — both APIs answer unproxied, and ClinicalTrials.gov was
nine times faster that way in testing (64 ms against 566 ms).

**ClinicalTrials.gov** publishes no rate limit and sends no rate-limit headers.
This Actor holds itself to 3 requests a second and pauses 250–600 ms between
pages of one query.

No response from either API tells us how much quota is left, so the Actor
self-limits: without a key it stops after 200 openFDA requests in a run and says
so in a free `rate-limit` row.

### This is registry data, not medical advice

These are public registry and regulatory records, reproduced as published.
**Nothing here is medical advice and nothing here is a safety finding.** openFDA's
own terms say to assume all results are unvalidated and not to rely on them for
decisions about medical care.

**Adverse-event reports — FAERS (`drug/event`), MAUDE (`device/event`), CAERS
(`food/event`) and the veterinary file — are voluntary, unverified reports.** The
FDA states that submitting one is not an admission that the product caused the
event, that the reports have not been scientifically or otherwise verified as to
a cause-and-effect relationship, and that they **cannot be used to estimate how
often something happens or to estimate risk**. A report naming a drug does not
mean the drug caused anything. Duplicate reports exist: `recordVersion` and
`recordDuplicate` are on every row so you can see them, and a single report can
name dozens of drugs — the one you searched for is often not the first.

**Trial registry records are submitted by sponsors**, not verified by the NIH or
the National Library of Medicine. `statusVerifiedDate` tells you the month the
sponsor last confirmed the status, and a study can sit at `RECRUITING` long after
it stopped. Results are posted late or not at all; `unpostedEvents` is on the row
when the registry has recorded that.

**Contact names, phone numbers and email addresses on trial rows are published by
the registry as the study's public contact points** and are reproduced unchanged,
including the placeholder addresses some sponsors submit (`xxxxxxxxxx@xxx.com`
appears in the registry itself). Adverse-event records are stripped of free-text
patient narrative and of manufacturer-contact address blocks before they are
written. No personal data is inferred, enriched or added.

Recall classes are hazard classifications, not verdicts: **Class I** a reasonable
probability of serious harm or death, **Class II** temporary or reversible harm,
**Class III** unlikely to cause harm.

### Limitations

- **The upstream format may change.** Both APIs are versioned and stable, but a
  new module or a renamed field would show up as nulls in a column rather than as
  an error. Diagnostic rows carry `upstream-shape` when a response is not the
  shape we parse.
- **`filter.geo` selects studies, not sites.** A 50-mile radius around New York
  returns studies with at least one site in range; their `locations` array is
  untouched. The top hit for "cancer near New York" has 279 sites worldwide, the
  first of them in Alabama. `locationsCount` is on the row so you can see it.
- **A 404 from openFDA cannot distinguish "no matches" from a field-name typo.**
  The body is byte-identical for both. If you use `fdaSearch`, check your field
  names against <https://open.fda.gov/apis/>.
- **`skip` paging stops at 25,000 records.** Above that the Actor uses openFDA's
  `search_after` cursor, which the two methods being incompatible means is chosen
  before the first request — set `maxFdaRecords` above 25,000 and the whole walk
  uses it.
- **`device/classification` has no date field**, so `fdaDateFrom`/`fdaDateTo` are
  declined there with a free diagnostic rather than silently ignored.
- **`fields=` is not used.** See the FAQ.
- **Not covered in v1:** non-US registries (EU CTIS, ISRCTN, the WHO ICTRP), FDA
  drug shortages, warning letters, the Orange Book, CPSC and USDA recalls.
  Posted-results parsing is limited to four counts and the document list.

### FAQ

**Why does one request return 100 trials with everything on them?**
Because the ClinicalTrials.gov list endpoint returns the complete
`protocolSection` and `derivedSection` for every study — verified across all 50
studies of a captured page. There is no per-study detail fetch to pay for, which
is why `trial` sits at $0.0005 and why `trialFields: "full"` costs nothing extra.

**Why does the Actor not send `fields=`?**
Because `fields=` returns the *same nested structure, pruned* — not a flat row —
so the parser has to walk the same paths either way, and the byte saving is about
20%. The field-name to path mapping is also non-obvious (`Phase` →
`phases[]`, `EnrollmentCount` → `enrollmentInfo.count`), and the `*DateStruct`
wrappers lose their `type`. One shape, parsed once, is the better trade.

**Why is the drug I searched for not the first entry in `drugNames`?**
openFDA matches *any* drug on an adverse-event report. A report returned for
`OZEMPIC` may list LANTUS SOLOSTAR first because the patient was taking both. All
the drugs on the report are in `drugNames`, `brandNames` and `genericNames`
(capped at 25), rather than one being picked and the rest quietly dropped.

**Why is my drug label truncated?**
Unless `fdaFullText` is on, each text section is cut to 5,000 characters and the
raw HTML `*_table` sections are dropped — a single SPL record runs 180–404 KB, of
which 30 KB can be one HTML table. `textTruncated` and `truncatedFields` say what
was cut, and `labelSections` lists every section the label has, including the
dropped ones. Turn `fdaFullText` on to keep it all.

Recall lot lists are capped at 2,000 characters **whatever you set**: one real
`device/recall` record carries 5,255,633 characters of lot numbers in a single
`code_info` string, and Apify's per-item ceiling is 9 MB.

**What do `Class I`, `II` and `III` mean on a recall?**
See the section above — they are the FDA's hazard classification of the recalled
product, not a judgement about the company.

**Why does `drug/drugsfda` have no date?**
Because the endpoint has no top-level date field. The only dates live inside
`submissions[]`, so `recordDate` is the latest `submission_status_date` and
`recordDateField` says so. That array is *not* in date order, so "latest" is a
sort, not `submissions[0]`.

**When is an empty result a failure?**
Never, by itself. A query that matched nothing gets a free `no-results`
diagnostic and no charge. A run whose *every* query came back empty finishes
FAILED — so a scheduled job tells you it found nothing rather than reporting
success over an empty dataset — and still bills nothing.

### Use it from an AI agent, or from code

One JSON input, one flat JSON array out — the shape agent runtimes handle best.
No browser, no proxy requirement, no credentials of any kind. The Actor is
configured for x402 agentic payments: pay-per-event pricing, event-only charging,
limited permissions, no Standby mode. It is callable by name over the **Apify MCP
server**, and the **Integrations** tab pushes results to Slack, a webhook,
Zapier, Make, Google Sheets, Snowflake or BigQuery.

```bash
curl -X POST "https://api.apify.com/v2/acts/insight.solutions~clinical-trials-fda-api/run-sync-get-dataset-items?token=$APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"interventions":["semaglutide"],"phases":["PHASE3"],"maxTrials":50,"fdaEndpoints":["drug/label","drug/enforcement"],"drugNames":["semaglutide"],"maxFdaRecords":50}'
```

```python
## pip install apify-client
from apify_client import ApifyClient

client = ApifyClient("<APIFY_TOKEN>")
run = client.actor("insight.solutions/clinical-trials-fda-api").call(run_input={
    "conditions": ["type 2 diabetes"],
    "statuses": ["RECRUITING"],
    "maxTrials": 100,
    "fdaEndpoints": ["drug/event"],
    "drugNames": ["semaglutide"],
    "fdaCount": ["patient.reaction.reactionmeddrapt.exact"],
    "maxFdaRecords": 0,
})

for row in client.dataset(run["defaultDatasetId"]).iterate_items():
    if row["rowType"] == "trial":
        print(row["nctId"], row["status"], row["leadSponsor"])
    elif row["rowType"] == "aggregate":
        print(row["aggregateRank"], row["aggregateTerm"], row["aggregateCount"])
```

**Agent patterns that work well:** answer "what is being tried for X" with
`conditions` plus `statuses: ["RECRUITING"]` and a small `maxTrials`, citing
`sourceUrl` per row; check a drug's regulatory footprint with `drugNames` across
`drug/label`, `drug/drugsfda` and `drug/enforcement` in one call; get a reaction
profile for free with `fdaCount` and `maxFdaRecords: 0`; resolve a specific study
with `nctIds` before reasoning about it; and always read `endpointLastUpdated`
before saying how current an adverse-event answer is.

### Legal

Not affiliated with, endorsed by or sponsored by the U.S. Food and Drug
Administration, the National Institutes of Health or the National Library of
Medicine. "openFDA", "FDA", "ClinicalTrials.gov", and every brand, device and
company name in the data are used descriptively to identify the records those
organisations publish.

Both sources are US federal public-domain records published explicitly for
programmatic access: ClinicalTrials.gov API v2
(<https://clinicaltrials.gov/data-api/api>) and openFDA
(<https://open.fda.gov/apis/>). No login is used, no paywall is bypassed, no
personal account is involved, and no data is collected beyond what those APIs
return. openFDA's disclaimer and terms travel with every record in the `raw`
column.

### Our other Actors

Every Insight Solutions Actor is pay-per-result with no browser, no login and no API key, and every one of them returns free diagnostic rows instead of billing for failures. Prices are per 1,000 results.

**Video, audio & social**

- [YouTube Transcript API](https://apify.com/insight.solutions/youtube-transcript-api) — captions as timed segments, text, SRT or VTT, with language fallback and translation.
- [YouTube Comments API](https://apify.com/insight.solutions/youtube-comments-api) — comments and replies with likes, pinned and hearted flags, newest or top sort.
- [YouTube Channel API](https://apify.com/insight.solutions/youtube-channel-api) — a channel's videos, Shorts and live streams, plus YouTube search.
- [Podcast Search, Episodes & Charts API](https://apify.com/insight.solutions/podcast-api) — Apple Podcasts search, charts and full episode feeds.
- [Bluesky Scraper](https://apify.com/insight.solutions/bluesky-scraper) — profiles, posts, followers and follows from the public AT Protocol API.
- [Telegram Channel Scraper](https://apify.com/insight.solutions/telegram-channel-scraper) — posts, views and channel stats from public Telegram channels.
- [Substack Scraper](https://apify.com/insight.solutions/substack-scraper) — posts with full free text, comments and publication profiles.
- [Hacker News API](https://apify.com/insight.solutions/hacker-news-api) — stories, comments, users, front page and a structured "Who is hiring?" parser from the official HN APIs.

**News, documents & the web**

- [Google News Search, Topics & Real Article URLs](https://apify.com/insight.solutions/google-news-api) — news search and topic feeds with the publisher's real URL decoded.
- [Website to Markdown — Content Extractor for LLMs & RAG](https://apify.com/insight.solutions/website-content-extractor) — any site as clean Markdown, text and heading-aware chunks.
- [Internet Archive API](https://apify.com/insight.solutions/internet-archive-api) — archive.org search, item metadata, files and reviews.
- [Wayback Machine Toolkit](https://apify.com/insight.solutions/wayback-toolkit) — archived URL inventories, snapshots and text diffs between dates.
- [Website Technology Detector](https://apify.com/insight.solutions/website-tech-detector) — the tech stack behind any site, with the evidence for each detection.
- [Domain Intelligence API](https://apify.com/insight.solutions/domain-intelligence-api) — DNS, RDAP registration, TLS certificate and HTTP facts in one row per domain.
- [SEO Page Audit](https://apify.com/insight.solutions/seo-page-audit) — sitemap crawl with on-page checks, structured data and broken-link reports.
- [Keyword Suggestions API](https://apify.com/insight.solutions/keyword-suggestions-api) — Google, YouTube, Bing, Amazon and eBay autocomplete with alphabet and question expansions.
- [Website Contact Extractor](https://apify.com/insight.solutions/website-contact-extractor) — emails, phone numbers and social profiles from any list of websites.
- [Web Search Results API](https://apify.com/insight.solutions/web-search-api) — Bing and DuckDuckGo organic results with snippets, no key, no browser.
- [Company Enrichment API](https://apify.com/insight.solutions/company-enrichment-api) — a domain in, a company profile out: firmographics, contacts, tech stack, DNS and hiring signal.

**Business, finance & jobs**

- [Congress & Insider Trades API](https://apify.com/insight.solutions/congress-insider-trades-api) — STOCK Act periodic transaction reports and SEC Form 4 insider trades in one schema.
- [Federal Contracts, Grants & Lobbying API](https://apify.com/insight.solutions/federal-contracts-grants-api) — SAM.gov opportunities, USAspending awards, Grants.gov notices and Senate lobbying filings in one schema.
- [SEC EDGAR API](https://apify.com/insight.solutions/sec-edgar-api) — filings, XBRL financials and full-text search by ticker or CIK.
- [Product & Vehicle Recalls API](https://apify.com/insight.solutions/product-recalls-api) — CPSC, NHTSA, FDA and USDA recalls, vehicle complaints and ratings, plus a VIN decoder.
- [Y Combinator Companies, Batches & Founders](https://apify.com/insight.solutions/yc-companies-directory) — the YC directory with founders and social links, filterable by batch, industry and hiring status.
- [Career Site Jobs API](https://apify.com/insight.solutions/ats-jobs-api) — jobs straight from Greenhouse, Lever, Ashby, Workable and 10+ other ATS career sites.
- [New Job Postings Monitor](https://apify.com/insight.solutions/job-postings-monitor) — new, closed and changed postings on the career sites you watch.
- [Remote Jobs API](https://apify.com/insight.solutions/remote-jobs-api) — RemoteOK, Remotive, We Work Remotely, Himalayas, Jobicy and more in one schema, deduplicated.
- [Shopify Products API](https://apify.com/insight.solutions/shopify-products-api) — any Shopify store's catalogue, variants, prices and stock signals.
- [Shopify Store Monitor](https://apify.com/insight.solutions/shopify-store-monitor) — price drops, sales, restocks, sell-outs and new products on any Shopify store, one row per change.

**Apps & games**

- [App Store & Google Play Reviews API](https://apify.com/insight.solutions/app-reviews-api) — reviews from both stores with ratings, versions and developer replies.
- [App Store Top Charts & App Search API](https://apify.com/insight.solutions/app-charts-api) — Apple top charts by country and genre, plus app search and details.
- [App Store Keyword Rank Tracker](https://apify.com/insight.solutions/app-store-keyword-rank-tracker) — where any app ranks for any keyword on the App Store and Google Play, with rank changes and ASO suggestions.
- [Steam Reviews API](https://apify.com/insight.solutions/steam-reviews-api) — Steam reviews with playtime, helpfulness and game details.
- [Steam Game Data API](https://apify.com/insight.solutions/steam-store-stats-api) — prices, tags, review scores, live player counts and top charts.

# Actor input Schema

## `conditions` (type: `array`):

Search ClinicalTrials.gov by condition — `type 2 diabetes`, `alzheimer`, `breast cancer`. Several entries are OR-ed, and an entry containing a space is quoted for you so `type 2 diabetes` is one phrase rather than three words.

## `statuses` (type: `array`):

Restrict to these overall statuses. Leave empty for every status. Remember that a status is what the sponsor last told the registry — `statusVerifiedDate` on every row says how recently.

## `maxTrials` (type: `integer`):

How many trial rows to return at most. `0` means every trial the filters allow, bounded by the time budget and by your maximum total charge. One request returns 100 trials with every module on them, so a big number is not a slow run.

## `trialFields` (type: `string`):

`summary` returns the flattened columns. `full` adds a `raw` column holding the study's complete `protocolSection` and `derivedSection` — the same request, just more of the answer kept. It costs no extra HTTP call and no extra charge.

## `sort` (type: `string`):

How ClinicalTrials.gov orders the results before the cap is applied.

## `interventions` (type: `array`):

Search by intervention — a drug, device, procedure or behavioural intervention. `semaglutide` returns the 177 phase-3 studies that name it.

## `terms` (type: `array`):

Searched across the whole study record. Also accepts raw Essie expressions such as `AREA[Phase]PHASE3` if you already know the registry's query language.

## `sponsors` (type: `array`):

Matches the lead sponsor **or** any collaborator. `Pfizer` matches 6,083 studies.

## `leadSponsors` (type: `array`):

Matches the lead sponsor only, ignoring collaborators. Use this when "sponsors" returns too many studies a company merely helped with.

## `nctIds` (type: `array`):

Look up specific studies by registry id, e.g. `NCT04368728`. Each id is read from `/studies/<nctId>`, so an id list ignores the other search fields. An entry that is not `NCT` plus eight digits gets a free diagnostic row instead of a request.

## `phases` (type: `array`):

Restrict to these phases. A study can be registered in two phases at once — `PHASE2` and `PHASE3` on the same record — so `phases` is a list on the output as well as the input.

## `studyTypes` (type: `array`):

Restrict to interventional, observational or expanded-access studies.

## `locationName` (type: `string`):

A place name matched against study sites, e.g. `Boston`, `Texas` or `Germany` (ClinicalTrials.gov `query.locn`). Leave empty to search everywhere. Use "Location radius" for a lat/lon circle instead.

## `updatedSince` (type: `string`):

Only studies whose registry record was last updated on or after this date (`YYYY-MM-DD`). The freshest data available is the registry's own `dataTimestamp`, which is on every summary row.

## `fdaEndpoints` (type: `array`):

Which openFDA datasets to search. Every selected endpoint produces `fda-record` rows in the same table as the trials.

## `drugNames` (type: `array`):

Brand, generic or substance name. Applied to every drug, food and veterinary endpoint; the device endpoints carry no drug names, so selecting one of those with a drug name gets a free `unsupported-filter` row saying so.

## `deviceNames` (type: `array`):

Trade or generic device name — `pacemaker`, `stent`. Applied to the six device endpoints only.

## `fdaSearch` (type: `string`):

For power users: a raw openFDA `search=` expression, AND-ed onto whatever the fields above built. Example: `serious:1 AND patient.patientsex:2`. Note that openFDA answers an unrecognised field name with the same 404 it uses for "no matches", so a typo here looks like an empty result.

## `fdaCount` (type: `array`):

Up to 20 field names to aggregate instead of listing, and at most 20 count requests per run across every endpoint. Each one costs a single request and returns up to 100 free `aggregate` rows — e.g. `patient.reaction.reactionmeddrapt.exact` gives a drug's top reported reactions. Use the `.exact` suffix on any text field, or the API counts individual words rather than whole phrases.

## `maxFdaRecords` (type: `integer`):

Applied per endpoint, so four endpoints at 100 is 400 rows. `0` fetches none, which is how you get aggregates only. Above 25,000 the run switches to openFDA's cursor paging, because skip-based paging stops there.

## `fdaDateFrom` (type: `string`):

`YYYY-MM-DD`, applied to each endpoint's own date field — `receivedate` for FAERS, `report_date` for recalls, `decision_date` for 510(k)s, and so on. `device/classification` has no date field at all and gets a free `unsupported-filter` row instead.

## `fdaDateTo` (type: `string`):

`YYYY-MM-DD`. The upper end of the FDA date window, applied to each endpoint's own date field, same as **FDA records dated on or after**. Leave it empty for no upper bound. Useful for pinning a quarter on the adverse-event datasets, which lag the present by six weeks or more.

## `fdaFullText` (type: `boolean`):

Off by default, every text section is trimmed to 5,000 characters and the raw HTML `*_table` sections are dropped. On, a drug label row keeps everything and can exceed 400 KB. Recall lot lists (`code_info`) and recall actions stay capped either way — one real `device/recall` record carries 5.2 MB of lot numbers in a single string.

## `fdaRaw` (type: `boolean`):

Keep the trimmed openFDA record in the `raw` column. Turn it off for a much smaller dataset when the flattened columns are enough.

## `openFdaApiKey` (type: `string`):

Optional. A free key from open.fda.gov raises openFDA's limits from 1,000 to 120,000 requests a day and lets pages hold 1,000 records instead of 100. Everything here works without one.

## `location` (type: `object`):

A radius as `{"lat": 40.7128, "lon": -74.0060, "radiusMiles": 50}`. Selects **studies that have at least one site inside it** — the study's own `locations` array is left whole, so a 279-site study still reports all 279 in `locationsCount`. For a place name use "Location name" instead.

## `startDateFrom` (type: `string`):

`YYYY-MM-DD`. Filters on the study start date the sponsor registered, which may be an estimate.

## `startDateTo` (type: `string`):

`YYYY-MM-DD`. The upper end of the study-start window; pair it with **Study started on or after** to pin a period. Leave it empty for no upper bound. Like the lower bound, it filters on the start date the sponsor registered, which may still be an estimate.

## `titles` (type: `array`):

Matches the brief and official titles only.

## `outcomes` (type: `array`):

Matches the primary, secondary and other outcome measures.

## `includeResults` (type: `boolean`):

Fetch the posted results for studies that have them, adding serious-event, other-event, death and enrolment counts and the study documents. This is the **only** setting that costs an extra HTTP request per study, and it is spent only on studies whose row already says `hasResults: true`.

## `maxResultFetches` (type: `integer`):

Ceiling on those extra requests, so "include posted results" on a thousand-trial run cannot become a thousand extra round trips.

## `maxLocationsPerTrial` (type: `integer`):

How many study sites to keep in the `locations` column. `locationsCount` always reports the real number, whatever the cap.

## `includeSummary` (type: `boolean`):

Add one free `summary` row per source: status, phase, sponsor and country breakdowns over the trials this run returned, the registry's `dataTimestamp`, and how many records matched upstream per FDA endpoint.

## `maxConcurrency` (type: `integer`):

How many sources may be in flight at once. There are only two, so this matters mainly for politeness.

## `maxRunSecs` (type: `integer`):

When this runs out the run stops fetching, keeps everything already written, adds one free `timeout` row and finishes successfully.

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

Both APIs answer unproxied, and ClinicalTrials.gov was nine times faster that way in testing. The datacenter proxy is on by default for IP hygiene. **Clear this field for heavy openFDA work without a key** — openFDA counts its keyless quota of 1,000 requests a day per IP address, and a shared datacenter exit is a small pool.

## Actor input object example

```json
{
  "conditions": [
    "type 2 diabetes"
  ],
  "statuses": [
    "RECRUITING"
  ],
  "maxTrials": 25,
  "trialFields": "summary",
  "sort": "LastUpdatePostDate:desc",
  "interventions": [],
  "terms": [],
  "sponsors": [],
  "leadSponsors": [],
  "nctIds": [],
  "phases": [],
  "studyTypes": [],
  "fdaEndpoints": [
    "drug/enforcement"
  ],
  "drugNames": [
    "semaglutide"
  ],
  "deviceNames": [],
  "fdaCount": [],
  "maxFdaRecords": 25,
  "fdaFullText": false,
  "fdaRaw": true,
  "titles": [],
  "outcomes": [],
  "includeResults": false,
  "maxResultFetches": 25,
  "maxLocationsPerTrial": 50,
  "includeSummary": true,
  "maxConcurrency": 2,
  "maxRunSecs": 240,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

## `results` (type: `string`):

Every row this run wrote — clinical trials from ClinicalTrials.gov API v2 and records from fourteen openFDA endpoints, in one table, plus the free aggregate, summary and diagnostic rows. Delivered as JSON items in the default dataset.

# 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 = {
    "conditions": [
        "type 2 diabetes"
    ],
    "statuses": [
        "RECRUITING"
    ],
    "maxTrials": 25,
    "trialFields": "summary",
    "sort": "LastUpdatePostDate:desc",
    "interventions": [],
    "terms": [],
    "sponsors": [],
    "leadSponsors": [],
    "nctIds": [],
    "phases": [],
    "studyTypes": [],
    "fdaEndpoints": [
        "drug/enforcement"
    ],
    "drugNames": [
        "semaglutide"
    ],
    "deviceNames": [],
    "fdaCount": [],
    "maxFdaRecords": 25,
    "fdaFullText": false,
    "fdaRaw": true,
    "titles": [],
    "outcomes": [],
    "includeResults": false,
    "maxResultFetches": 25,
    "maxLocationsPerTrial": 50,
    "includeSummary": true,
    "maxConcurrency": 2,
    "maxRunSecs": 240,
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("insight.solutions/clinical-trials-fda-api").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 = {
    "conditions": ["type 2 diabetes"],
    "statuses": ["RECRUITING"],
    "maxTrials": 25,
    "trialFields": "summary",
    "sort": "LastUpdatePostDate:desc",
    "interventions": [],
    "terms": [],
    "sponsors": [],
    "leadSponsors": [],
    "nctIds": [],
    "phases": [],
    "studyTypes": [],
    "fdaEndpoints": ["drug/enforcement"],
    "drugNames": ["semaglutide"],
    "deviceNames": [],
    "fdaCount": [],
    "maxFdaRecords": 25,
    "fdaFullText": False,
    "fdaRaw": True,
    "titles": [],
    "outcomes": [],
    "includeResults": False,
    "maxResultFetches": 25,
    "maxLocationsPerTrial": 50,
    "includeSummary": True,
    "maxConcurrency": 2,
    "maxRunSecs": 240,
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("insight.solutions/clinical-trials-fda-api").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 '{
  "conditions": [
    "type 2 diabetes"
  ],
  "statuses": [
    "RECRUITING"
  ],
  "maxTrials": 25,
  "trialFields": "summary",
  "sort": "LastUpdatePostDate:desc",
  "interventions": [],
  "terms": [],
  "sponsors": [],
  "leadSponsors": [],
  "nctIds": [],
  "phases": [],
  "studyTypes": [],
  "fdaEndpoints": [
    "drug/enforcement"
  ],
  "drugNames": [
    "semaglutide"
  ],
  "deviceNames": [],
  "fdaCount": [],
  "maxFdaRecords": 25,
  "fdaFullText": false,
  "fdaRaw": true,
  "titles": [],
  "outcomes": [],
  "includeResults": false,
  "maxResultFetches": 25,
  "maxLocationsPerTrial": 50,
  "includeSummary": true,
  "maxConcurrency": 2,
  "maxRunSecs": 240,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call insight.solutions/clinical-trials-fda-api --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,insight.solutions/clinical-trials-fda-api"
        }
    }
}
```

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/L208eF4EDwYbUKXBv/builds/U06UkGFBJnLUqZfne/openapi.json
