ClinicalTrials.gov Scraper – Patient Recruitment & NCT IDs
Pricing
$1.50 / 1,000 result items
ClinicalTrials.gov Scraper – Patient Recruitment & NCT IDs
Scrape ClinicalTrials.gov official NIH API: condition, intervention, sponsor, location, facility and lead-sponsor filters, plus six date windows. Optional one-row-per-site mode. No contact PII, ever. Optional watchLabel mode returns only studies new since your last run. $0.0015/result, no start fee.
Pricing
$1.50 / 1,000 result items
Rating
0.0
(0)
Developer
Fetch Smith
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
12 hours ago
Last modified
Categories
Share
Pulls studies from ClinicalTrials.gov, the US NIH/NLM clinical trial registry, using its own official API v2 — a clinical research API with no key, no login and no proxy. 602,520+ clinical trials covered.
What you get
34 flat fields per study, including the ones most ClinicalTrials.gov Actors skip:
| Field | Why it matters |
|---|---|
rowsPerStudy: "site" mode | One row per trial site instead of one per study — facility name, city, state, country, lat/lon and that site's own recruitment status, so a site-selection or patient-recruitment buyer doesn't have to explode the array themselves. A study averages 5 sites (max seen: 110), so this mode returns roughly 5x more billable rows for the same query. The per-site status is the one most buyers miss: it routinely disagrees with the study-level overallStatus — see the FAQ for measured rates. |
phases, studyType, overallStatus | Filterable and returned flat, not nested. |
enrollmentCount, sex, minimumAge, maximumAge, healthyVolunteers, standardAges | The eligibility snapshot without parsing free-text criteria. enrollmentType says whether that count is ACTUAL or ESTIMATED — an estimate on a running trial is a target, not a headcount. standardAges is the registry's own CHILD/ADULT/OLDER_ADULT bucketing, the same vocabulary the ageGroups filter uses. |
eligibilityCriteria | The full inclusion/exclusion text as the registry publishes it. We deliberately don't pre-split it into separate inclusion and exclusion lists the way some competitors do: the split is a guess at prose with no fixed format, and mislabelling an exclusion criterion as an inclusion one is the one error a trial-screening buyer cannot afford. |
primaryOutcomes, secondaryOutcomes | What the trial actually measures — measure, timeFrame and description per endpoint. These are what the outcomeMeasure filter searches, so you can see which endpoint matched your query instead of taking the filter on faith (the API searches the description too, not just the measure title). |
resultsFirstPostDate | When the results section was first posted — the field the resultsFirstPostedDateFrom/To window ranges over. Null until a trial actually posts results. |
leadSponsorClass | The lead sponsor's organization type (NIH, INDUSTRY, FED, etc.) — filterable via funderTypes. |
hasResults | Whether the trial has posted a results section — filterable via resultsAvailability (with or without). |
interventions | Type + name for every drug/device/procedure arm. |
studyUrl | Direct link to the public study page. |
With hasResults, primaryOutcomes and resultsFirstPostDate flat on every row, this doubles as a study results API: set resultsAvailability to return only trials that have actually reported, or only the ones that never did.
We do not ship contact people, phone numbers or emails — ever. ClinicalTrials.gov's own API returns named individuals and personal email addresses in centralContacts/location contacts (we found a real @gmail.com in a live sample). Several competitor Actors resell that as a "contact finder." We deliberately drop it; locations/site carries facility, that facility's recruitment status, city, state, country and geo-coordinates only. A medical data API can be useful without reselling named individuals.
Name a watchLabel and every later run on the same saved search returns only studies new since the last run, so a scheduled competitive-intelligence pull never re-delivers or re-charges for the same trial twice; add watchChanges and it also catches a study's status changing (e.g. Recruiting → Completed/Terminated), a protocol amendment updating its enrollment count, or its completion date slipping.
Who uses this
- Pharma / CRO business development tracking competitor trials by condition or sponsor.
- Site-selection teams finding which facilities run trials for a given condition (
rowsPerStudy: "site"). - Investor / market research watching a sponsor's pipeline by phase and status; add
watchChangesto get alerted when a trial they already logged is upgraded, terminated, or has its readout date slip. - Patient-advocacy and recruitment groups finding actively recruiting trials near a location.
- COVID-19 and long-COVID researchers pulling COVID trials by status, phase and site:
conditions: "COVID-19"matches 10,246 registered studies (338 currently recruiting) and"long COVID"matches 733, and the COVID data comes back as the registry's own record — see the FAQ below.
Input
{"conditions": "breast cancer","overallStatus": ["RECRUITING"],"phases": ["PHASE3"],"maxResults": 100}
Or skip the form entirely and paste the search URL from clinicaltrials.gov:
{"startUrl": "https://clinicaltrials.gov/search?cond=asthma&intr=budesonide&aggFilters=status:rec,phase:3&start=2020-01-01_","maxResults": 100}
| Input | Notes |
|---|---|
startUrl | Build the search on clinicaltrials.gov, copy the address bar, paste it here — the condition, other terms, intervention, sponsor, location, title, outcome, lead sponsor and study-ID boxes plus every sidebar filter (status, phase, study type, funder, sex, age, results, documents, FDAAA violations) and every date window come across as-is. An API URL (https://clinicaltrials.gov/api/v2/studies?query.cond=asthma&...) works too. The URL's search terms and date windows replace the matching fields below; anything it doesn't mention (maxResults, rowsPerStudy, watchLabel, and any filter below the URL doesn't carry) still applies. If the URL does carry a status/phase/study type/sex/healthy-volunteers/age group/funder-type code and you also type that same field below, your typed value wins outright — see the FAQ. A non-clinicaltrials.gov URL fails fast instead of quietly scraping nothing. |
nctIds | Comma/space/newline-separated NCT IDs (e.g. NCT04368728, NCT03854955) for a direct lookup by ID instead of a search — what other Actors call "search by direct URL". Exclusive mode: when set, every filter below is ignored (so a mixed batch of unrelated trials all come back), and a malformed or nonexistent ID is dropped individually with a named warning instead of failing the whole batch. |
conditions / interventions / sponsors / locations | Free-text, each maps to the API's own query.cond / query.intr / query.spons / query.locn. ANDed together. |
searchQuery | General free-text search across titles, outcomes and eligibility text. |
overallStatus | e.g. RECRUITING, COMPLETED, TERMINATED. All 14 official values supported. |
studyTypes | INTERVENTIONAL, OBSERVATIONAL, EXPANDED_ACCESS. |
phases | EARLY_PHASE1..PHASE4, NA. Only meaningful for interventional studies — about 1 in 5 studies overall have no phase at all. A study matches if the phase is anywhere in its phase list, so ["PHASE2"] also returns combined ["PHASE1","PHASE2"] trials. |
resultsAvailability | with = only studies that posted a results section; without = only studies that never reported (the FDAAA-compliance question). Blank = both. Supersedes the older boolean hasResultsOnly, which still works. without cannot be combined with a resultsFirstPostedDate window — see the FAQ. |
ageGroups | Standard age groups the study enrols: CHILD (0–17), ADULT (18–64), OLDER_ADULT (65+). Multiple = OR. Coarser and more reliable than ageRangeFromYears/ageRangeToYears, which only match studies that state a numeric bound. |
documentTypes | Only studies that uploaded one of these documents: prot (study protocol), sap (statistical analysis plan), icf (informed consent form). Multiple = OR. About 9% of studies have any. |
fdaRegulationViolation | Only studies carrying an FDA regulation (FDAAA 801) violation notice — 8 registry-wide, re-verified live 2026-09-26. Combine it with nothing else or you will get zero rows. |
sex | FEMALE or MALE — restrict to studies whose eligibility criteria specify that sex. Leave blank for all. |
acceptsHealthyVolunteers | Only studies that accept healthy volunteers, not just patients with the condition. |
lastUpdatePostedDateFrom / lastUpdatePostedDateTo | Absolute YYYY-MM-DD window on the record's last-updated date — a repeatable "what changed since I last pulled" query, either bound optional. Setting From after To fails fast with an error instead of silently returning 0 rows. |
ageRangeFromYears / ageRangeToYears | Only studies whose stated minimum/maximum eligibility age falls in this range, either bound optional. E.g. ageRangeToYears: 65 excludes studies with no senior-age cap. Setting From above To in the same unit fails fast with an error instead of silently returning 0 rows. |
ageRangeFromUnit / ageRangeToUnit | Unit for the two bounds above: Years (default), Months, Weeks, Days. Whole years are too coarse for neonatal and infant trials — ageRangeToYears: 18 + ageRangeToUnit: "Months" finds the studies that stop enrolling before the second birthday (108 alongside a cancer query, vs 2,229 for the 18-years reading of the same number). Each bound carries its own unit. |
studyStartDateFrom / studyStartDateTo | Absolute YYYY-MM-DD window on the study's start date. Either bound optional. Month-precision dates anchor to the 1st — see the partial-date FAQ below. |
primaryCompletionDateFrom / primaryCompletionDateTo | Window on the primary completion date — the readout date a competitive-intelligence pull is usually actually about. Month-precision dates anchor to the 1st — see the partial-date FAQ below. |
studyCompletionDateFrom / studyCompletionDateTo | Window on the overall completion date. Month-precision dates anchor to the 1st — see the partial-date FAQ below. |
firstPostedDateFrom / firstPostedDateTo | Window on the date the study was first posted to ClinicalTrials.gov — the "newly registered trials" query. |
resultsFirstPostedDateFrom / resultsFirstPostedDateTo | Window on the date results were first posted. Pairs with resultsAvailability: "with" (or a blank resultsAvailability). Pairing it with "without" fails fast with an error instead of silently returning 0 rows — see the FAQ. |
facilityName | Only studies running at a facility whose name matches, e.g. Mayo Clinic. This is the site/hospital name, not the city — locations is the city/state/country field. Multi-word values are matched as a phrase, not as loose terms. |
leadSponsorName | Only studies whose lead sponsor matches, e.g. Pfizer. Narrower than sponsors, which also matches collaborators. |
funderTypes | Lead sponsor organization type: NIH, FED (other US federal), OTHER_GOV, INDUSTRY, NETWORK, INDIV, OTHER (academic/nonprofit), UNKNOWN, AMBIG. |
titleOrAcronym | Search only the official/brief title and acronym — narrower than searchQuery. |
outcomeMeasure | Search only the study's stated outcome measures, e.g. "overall survival". |
sortBy | Order results before maxResults truncates them: most recently updated, most recently first-posted, or largest enrollment first. Default is the API's own relevance order. |
rowsPerStudy | "study" (default) or "site". |
maxResults | Up to 50,000. Token-based paging, no offset wall. |
watchLabel | Name a saved search to get only studies new since this label's last run (see FAQ). Leave empty for the normal full-match-set behaviour. Ignored when nctIds is set. |
watchChanges | boolean |
webhookUrl | Optional. POST a small JSON completion summary (rows pushed, studies scanned, pages walked, dataset ID, watch new/changed counts, plus the full summary completeness object) here when the run finishes — see FAQ. |
Sample output (rowsPerStudy: "study")
{"nctId": "NCT04137653","briefTitle": "Treatment of Triple-negative Breast Cancer With Albumin-bound Paclitaxel as Neoadjuvant Therapy: a Prospective RCT","overallStatus": "RECRUITING","studyType": "INTERVENTIONAL","phases": ["PHASE3"],"enrollmentCount": 1498,"enrollmentType": "ESTIMATED","leadSponsor": "Shengjing Hospital","conditions": ["Breast Cancer"],"interventions": [{"type": "DRUG", "name": "nab-Paclitaxel+carboplatin"}, {"type": "DRUG", "name": "Paclitaxel+carboplatin"}],"primaryOutcomes": [{"measure": "Pathologic complete response (PCR)", "timeFrame": "At 5 years of treatment", "description": "Pathologic complete remission refers to no invasive tumor cell remnants in the pathological examination of the primary mammary gland and axillary lymph nodes surgically removed..."}],"standardAges": ["ADULT", "OLDER_ADULT"],"eligibilityCriteria": "Inclusion Criteria:\n\n* breast cancer is confirmed by the mammography, and the im...","resultsFirstPostDate": null,"locationCount": 1,"studyUrl": "https://clinicaltrials.gov/study/NCT04137653"}
Sample output (rowsPerStudy: "site")
Same flat study fields as above, minus locations/locationCount, plus one site object per participating facility — so a 12-site study becomes 12 rows that differ only in site. Real row, pulled live 2026-10-19:
{"nctId": "NCT06246916","overallStatus": "RECRUITING","phases": ["PHASE3"],"leadSponsorClass": "INDUSTRY","site": {"facility": "Arizona Oncology Associates","status": "WITHDRAWN","city": "Tucson","state": "Arizona","country": "United States","lat": 32.22174,"lon": -110.92648},"studyUrl": "https://clinicaltrials.gov/study/NCT06246916"}
Note site.status (WITHDRAWN) against the study's overallStatus (RECRUITING) — that is not an error, and it is the field to filter on for site work. See the FAQ.
Watch-mode change fields, only on a watchChanges re-delivery: _watchChangeType (array, one or more of overallStatus/lastUpdatePostDate/enrollmentCount/primaryCompletionDate/completionDate), _watchPrevious (object with the previous value(s) for each changed field).
The pageSize trap
Ask the API for pageSize=1001 and it doesn't 400 — it silently returns 200 with only 1000 rows, no warning. This Actor always clamps to 1000 and pages with the API's own nextPageToken, which has no offset wall (5,000+ unique rows walked in one run during testing, no rate limiting).
Pricing
$0.0015 per result, no Actor-start fee. The niche's Store leader by users, parseforge/clinicaltrials-scraper (47 users), charges $0.16 to start plus $0.012/result on its free tier — about 8x more per row, with a start fee we don't charge at all. 1,000 studies costs $1.50 here vs. $12.16 there. All prices on this page were read from each Actor's live in-effect pricing record, verified 2026-10-03.
ClinicalTrials.gov is one of the most crowded niches on the Store — a 15-term Store sweep on 2026-10-03 returned 203 distinct listings, 128 of which name clinical trials in their own name or title — so here is the wider field rather than one flattering comparison. Dearer than us at every tier: logiover/clinicaltrials-gov-scraper (24 users, the niche's second-largest listing) at $0.005/row (FREE) down to $0.003 (GOLD+), no start fee — 3.3x our rate at FREE, 2x at the best tier; bovi/clinicaltrials-scraper at $0.0119 down to $0.011305; scrapesage/clinical-trials-scraper at $0.004/trial, $0.008/site lead, $0.006/sponsor record; dataio/clinicaltrials-sites-investigators at $0.006 down to $0.004 per trial site; malonestar/clinical-trials-meta-search at $0.006 down to $0.0018; thirdwatch/clinical-trials-gov-scraper at $0.004 down to $0.002; themineworks/clinicaltrials-scraper at $0.0035 down to $0.0025 plus a $0.005 start fee; scrapepilot/clinicaltrials-gov-aggregator-pharma-biotech-intelligence at $0.003 plus a $0.02 start fee; devilscrapes/clinicaltrials-gov-scraper at $0.002 plus a $0.20 start fee; ryanclinton/clinical-trial-tracker and pink_comic/clinicaltrials-gov-search at $0.002; scrapers_lat/clinicaltrials-scraper at a tiered $0.003 (FREE) down to $0.00255 (GOLD+), no start fee — 2x our rate at FREE, 1.7x at the best tier. Two listings market themselves on price and are still dearer than us: delectable_incubator/clinicaltrials-scraper-low-cost charges $0.00199 down to $0.00179, and scrapestorm/clinicaltrials-gov-listings-scraper---cheap charges $0.00299.
What we do not claim (every rate below re-read live and verified 2026-10-03). We are not the cheapest ClinicalTrials.gov Actor on the Store and we don't pretend to be. webdata_labs/clinical-trials-api charges $0.001/record (FREE) down to $0.00075 (GOLD+) with no start fee — cheaper than us at every tier and every volume. martc03/nih-clinical-trials (verified live 2026-10-06) charges just $0.00001/record with a $0.00005 start fee — despite the NIH-sounding name its own description confirms plain ClinicalTrials.gov coverage, ~150x under our rate; chrisp1211/clinicaltrials-scraper-max and bgfc97/clinicaltrials-scraper each charge a flat $0.001/record with no start fee, matching webdata_labs's rate. alizarin_refrigerator-owner/clinicaltrials-gov-api---clinical-study-data prices a run instead of a row — $0.10 to start plus $0.01 per search call plus $0.00001 per dataset item — so we are cheaper on small pulls but they are cheaper past roughly 75 rows in a single search ($0.12 vs. our $1.50 for 1,000 studies). quotient_variablebarrier/healthcare-data-scraper charges $0.05 to start plus a flat $0.001 per record — cheaper than our $0.0015/row past about 100 rows in a single run ($0.15 vs. our $1.50 for 1,000) — but it is not a like-for-like substitute: it only returns studies with overallStatus: RECRUITING (no completed/terminated/other-status trials), and that one event price is shared across its three bundled sources (CMS hospital ratings and FDA drug-shortage alerts alongside ClinicalTrials.gov), so a pull mixing sources charges the same $0.001 regardless of which source a row came from.
Correction, 2026-10-03. This page previously called martc03 "the cheapest listing found anywhere in this niche." That is no longer true and we are retracting it: the 2026-10-03 sweep found maximedupre/clinicaltrials-gov charging the same $0.00001 per study with no start fee at all, which is strictly cheaper than martc03 at every volume, and constant_quadruped/clinical-trials-fda-scraper charges $0 per row — the cheapest price that exists (it carries no pricing record at all; see the shape note below, rechecked 2026-10-09) — while covering ClinicalTrials.gov and openFDA adverse events, labels and recalls. The same sweep surfaced eight more previously-unnamed undercutters, every one of them cheaper than our $0.0015 at every tier. At $0.0005/result plus a $0.00005 start fee: copious_atoll/clinical-trials-scraper. At a tiered $0.001 (FREE) down to $0.0007 (GOLD+) with no start fee: datalayer/clinical-trials-failure-intel, which also classifies the whyStopped free text on terminated studies, a field we return raw. At a flat $0.001/row with no start fee: agentictools/clinical-trials-search, thriftykiwi/clinical-trials-scraper and brick_joey_yto/clinical-trials-monitor. At a flat $0.001/row plus a $0.00005 start fee: jovian_explorer/clinicaltrials-gov and alleserojje/clinical-trials-scraper. One rival is cheaper only above the free tier: hipersoft/clinicaltrials-scraper charges $0.0016/trial on FREE — slightly more than us — falling to $0.0008 at GOLD+, but it also bills $0.0005 per API request on top, so its true cost depends on how many searches your pull takes. All eleven of these prices were read from each Actor's own live pricing record and verified 2026-10-03.
One headline number can mislead, in both directions (verified 2026-10-03). cblu/clinical-trials-scraper advertises a $0.00001 dataset-item event, which makes it look like the niche's cheapest listing in any price-sorted comparison; its actual per-study charge is a separate study-record event at $0.003, twice our rate. We flag it because we nearly made that mistake ourselves. Conversely, four listings charge no per-result fee at all, leaving you only the platform compute — but in two different shapes, rechecked live 2026-10-09: labrat011/clinical-trials-scraper and bikram07/clinical-trials-feed have each filed an explicit FREE pricing entry (a deliberate, dated choice their owner would have to supersede with advance notice), while scrupulous_waterbird_m4w/clinical-trials-gov and constant_quadruped/clinical-trials-fda-scraper carry no pricing record at all — never monetized, which is also $0 per row today but can be given a price at any time without the notice an existing entry requires. And labrat011/clinical-trial-site-contact-finder undercuts our rowsPerStudy: "site" mode at $0.0007/row — but that is the contact-reselling product described above, which we deliberately do not ship. Its filed pricing record raises its start fee from $0.00005 to $0.005 on 2026-10-10, leaving the $0.0007 per-row rate untouched, so it stays cheaper than us per site row. We have not run any of these Actors ourselves; we read their published prices, not their output quality, field coverage or reliability. What we do claim is what the field table above lists: 34 flat fields, site-level rows, watchLabel/watchChanges incremental delivery, no contact PII, and no start fee.
Cycle update, 2026-10-05. A fresh full-cohort sweep (152 Store listings seen, 120 matched, 85 unnamed — the ≥3-user cut cleared only one listing, an MCP server out of scope, so every unnamed listing was live-priced instead) found nine more previously-unnamed rivals. Cheaper than us at every tier: chorelet/clinical-trials-scraper at $0.001/study (FREE) down to $0.0007 (GOLD+), plus a separate $0.0003 down to $0.00021 per-site-row event — the first other listing found that prices site rows separately, like our own rowsPerStudy: "site" mode; koalastuff/clinical-trials-recruiting-monitor at $0.001 down to $0.0007, but scoped only to overallStatus: RECRUITING (the same not-a-like-for-like caveat as quotient_variablebarrier above — no completed/terminated/other-status trials); themineworks/clinicaltrials-sponsor-intelligence and its sibling themineworks/clinicaltrials-bulk-exporter both at $0.001 down to $0.0006 plus a $0.005 one-time Actor-start fee; and datamule/clinicaltrials-gov-scraper, the cheapest of this batch, at $0.0005 down to $0.00025 with no start fee at all. Cheaper only from a paid tier up: maydit/clinicaltrials-gov-monitor at $0.002 (FREE/BRONZE) crossing under us at $0.0012 (GOLD+); cynix_dev/clinicaltrials-scraper at $0.005 (FREE) crossing under at $0.001 (GOLD+); xtracto/clinicaltrials-studies at $0.003 (FREE) plus a flat $0.005 start fee, crossing under our per-row rate at $0.001 (GOLD+); and arman-bd/clinicaltrials-scraper, an exact $0.0015 tie at FREE that falls to $0.00027 at DIAMOND. Two listings matched the niche's search terms but are not substitutes once read past their title: tolvan/harmoney-human-crispr-cas9-research is a CRISPR/Cas9 evidence product spanning ClinicalTrials.gov and PubMed, not a general trial scraper, and scrapesignal_labs/clinical-trial-site-leads resells site and investigator contact details — the same PII-reselling shape as labrat011/clinical-trial-site-contact-finder above, which we deliberately do not ship. All nine prices above were read from each Actor's own live pricing record, and the two shape-excluded listings were ruled out from their own description, not their title; all verified 2026-10-05. One candidate was deliberately not disclosed either way: s-r/clinicaltrials-scraper looks cheap at a glance ($0.001 primary event) but that event is a flat one-time run-start fee — its actual per-study event is a separate, non-tiered $0.0015, an exact tie with us plus their extra run fee, so it is dearer overall, not cheaper.
Cycle update, 2026-10-06. A fresh full-cohort sweep (153 Store listings seen, 121 matched, 74 unnamed) found no new undercutter — the niche grew slightly since 2026-10-05 (152/120) but completeness holds. The >=3-user cut cleared only two listings, both out of scope by shape: funnyvalentine69/fda-drug-pipeline-intelligence ($0.005/row) and red.cars/drug-intelligence-mcp ($0.03/tool call) are AI-synthesis/MCP products spanning ClinicalTrials.gov, openFDA and Drugs.com, not plain per-study scrapers, and both are dearer than us regardless. The full unnamed tail was live-priced anyway: two previously-unnamed exact ties at our own $0.0015/study rate, neither a clean undercut. aurenic/clinicaltrials-scraper matches our headline rate but also bills a $0.00005 Actor-start fee we don't charge, so it is slightly dearer overall despite the tie. realai_pl/recruiting-clinical-trials ties us with no start fee, but — the same not-a-like-for-like caveat as quotient_variablebarrier and koalastuff above — it only returns studies with overallStatus: RECRUITING, no completed/terminated/other-status trials. Two more sub-$0.002-headline traps were caught and correctly excluded: malekh/clinical-trial-protocol-amendments advertises a $0.00001 dataset-item event, but it's a diff-tracking product whose real charges are a separate $0.15 "study scanned" and $0.75 "amendment reported" event — not a row price; red.cars/clinical-trials-mcp advertises the same $0.00001 trap while its real pricing is $0.05-0.15 per MCP tool call. All previously-named headline rivals (parseforge, logiover, bovi, ryanclinton, pink_comic, devilscrapes, scrapepilot, alizarin_refrigerator-owner, quotient_variablebarrier, both labrat011 listings, webdata_labs) were re-read live and matched the figures already published here exactly, except parseforge ticking 46→47 users — inside tolerance, not restated. All prices above re-read live and verified 2026-10-06.
Cycle update, 2026-10-07. A fresh full-cohort sweep (143 Store listings seen, 123 matched, 70 unnamed — up from 121/74 at 2026-10-06) grew the >=3-user cut to six listings, all live-priced, all dearer than our flat $0.0015/study at every tier — no new undercutter. autofacts/clinical-trials-scraper and hichemdev/clinicaltrials-scraper each charge $0.003/study (hichemdev adds a $0.001 start fee); neuton/clinicaltrials-gov-studies-scraper and oblanceolate_mandola/clinical-trials-search charge $0.003 and $0.004/result respectively; foo121/clinical-trials-scraper charges $0.004/result item; crawlerbros/clinicaltrialsgov-scraper tiers $0.005 (FREE) down to $0.003 (GOLD+) plus a $0.005 start fee. Two sub-20-user counts elsewhere on this page had drifted and were removed rather than re-pinned, per this fleet's standing rule that a count under 20 users is too volatile to keep tracking exactly: ryanclinton/clinical-trial-tracker (was 6, live 7) and koalastuff/clinical-trials-recruiting-monitor (was 2, live 3) — both counts simply dropped, every price and scope claim in the same sentence left intact. All figures above re-read live and verified 2026-10-07.
2026-10-07 cleanup (cycle 1375): dropped every sub-20-user rival count from this README per the standing rule (an exact count is only published at 20+ users, since totalUsers churns in both directions and a one-user tick under 20 is automatically STALE at the checker's 10% tolerance) — 33 bare counts removed (32 (N users)/(N user) mentions plus one (N new in 30 days) growth figure for maximedupre/clinicaltrials-gov), every price claim, scope exclusion and feature comparison left intact. No superlative in this section was premised on a sub-20 count, so none needed rewording. Counts at 20 and above are untouched (parseforge/clinicaltrials-scraper 46, logiover/clinicaltrials-gov-scraper 24). This edit only removes numbers, it does not restate any — but every count left standing was re-verified against the live Store records on 2026-10-07, and none of them is stale.
Cycle update, 2026-10-08 (cycle 1403). Promoted this niche into our own internal multi-term sweep tool (it was one of a handful still running on a single base word), widening the live search from "clinicaltrials" alone to also cover "clinical trials"/"clinical trial" and "patient recruitment" — 154 Store listings seen, 133 matched (up from 124 on the single word), every previously-unseen match already named above except one. The >=3-user cut cleared exactly one new listing: fascinating_lentil/clinical-trials-drug-data-aggregator charges a flat $0.002/record plus a $0.00005 Actor-start fee — dearer than our $0.0015/study at every tier, no start fee. All previously-named headline rivals (parseforge, logiover, bovi) were spot-checked live against their current in-effect pricing record and matched the figures already published here exactly. All prices above re-read live and verified 2026-10-08.
Cycle update, 2026-10-09 (cycle 1472). A fresh full-cohort sweep — 145 Store listings seen, 128 matched, 72 of them never named on this page — found no new undercutter at any tier. This sweep had no >=3-user head at all: every one of the 72 unnamed listings sits at 1 or 2 users (Apify pins a brand-new listing at 2), so all 72 were live-priced rather than sampled. The closest any of them comes to our flat $0.0015/study is above it: haketa/clinicaltrials-scraper at $0.0025 (FREE) down to $0.00175 (GOLD+) and fayoussef/clinical-trials-intelligence at $0.002 down to $0.0018, with smilemask/clinical-trials-search at a flat $0.0018 — all dearer than us at every rung, most of them several times over ($0.002–$0.5/row). Three of the 72 advertise a price in their own title and all three are dearer than us: velvety_bedbug/clinical-trials-scraper ("$3/1k") at $0.003/row plus a $0.05 start fee, fortuitous_pirate/clinicaltrials-scraper ("$3.5/1k") at $0.0035 plus $0.05, and fortuitous_pirate/clinicaltrials-gov-scraper ("$5/1k") at $0.005 plus $0.001. Three listings have a future-dated pricing entry that every price tool filtering on "in effect now" would miss, so they were read forward explicitly: velvety_bedbug and fortuitous_pirate/clinicaltrials-scraper both cut only their start fee ($0.05 → $0.005) on 2026-10-13, and antishock/clinicaltrials-gov-studies-scraper restructures on 2026-10-16 to $0.002/item plus a $0.002 start — none of the three touches a per-row rate, and all three stay dearer than us afterwards. One listing was deliberately left out of the per-row comparison as unreadable from its record alone: datalantern/clinical-trials-search files two equal $0.005 events (actor-start and study) with no primary flag, so its headline rate is ambiguous — either reading puts it well above us. Headline named rivals were re-read live: parseforge ticked 46 → 47 users (restated above) and its $0.16 free-tier start fee is tiered, dropping to $0.05 at GOLD+ — worth stating because a flat read of the GOLD+ rung would wrongly look like the $0.16 figure had gone stale; logiover/clinicaltrials-gov-scraper (24 users, $0.005→$0.003), bovi, webdata_labs, martc03, maximedupre, datamule, alizarin_refrigerator-owner and both labrat011 listings all matched the figures already published here exactly. All prices above were read from each Actor's own live pricing record and verified 2026-10-09.
Cycle update, 2026-10-23 (cycle 2144) — the niche got materially cheaper, and we did not. A fresh full-cohort sweep found 337 Store listings seen, 182 matched, 117 of them never named on this page — up sharply from 145 seen / 128 matched on 2026-10-09, so this niche grew its matched field by about 42% in two weeks. Unlike the 2026-10-09 sweep, this one had a real >=3-user head (nine listings), but the whole 116-listing unnamed cohort was live-priced anyway rather than sampled. The honest headline: where the 2026-10-09 sweep found zero undercutters, this one found sixteen — and all but a few filed their pricing after 2026-10-14, i.e. they are new arrivals, not rivals we had previously missed. We are restating plainly what the section above already says: we are not the cheapest ClinicalTrials.gov Actor on the Store, and that is now true by a wider margin than before.
Cheaper than us at every tier, on a unit-matched per-study or per-result row (each rate re-read live and verified 2026-10-23): j0401/clinicaltrials-studies is the cheapest unit-matched listing now in this niche at a tiered $0.00005 per study record (FREE) down to $0.00003 (GOLD+) with no start fee — roughly 30–50x under our flat $0.0015, and $0.05 against our $1.50 for 1,000 studies (pricing filed 2026-10-19); lepidata/clinicaltrials at a flat $0.0001 per study on every tier, no start fee (filed 2026-10-16); tidyharvest/clinicaltrials-scraper at $0.0008 down to $0.00048 (GOLD+), no start fee (filed 2026-10-18); apt_marble/clinicaltrials-gov-studies-scraper at a flat $0.0009 per result, no start fee (filed 2026-10-14). Then a cluster at a flat $0.001 per row, all cheaper than us at every tier: pushbuild/clinical-trials with no start fee; gazidev/clinical-trials-search and deanko/clinicaltrials-scraper (whose own title advertises the same rate as "$1 per 1,000 trials") each plus a $0.00005 start fee; alaudinburki/clinical-trials-search plus $0.0001; one_dollar_store/clinical-trials-search plus $0.001; and the MCP monitors amygraphics/clinical-trials-monitor and amygraphics/clinicaltrials-gov-monitor, each $0.001 plus a $0.001 start fee.
Cheaper only from a paid tier up (they tie our $0.0015 at FREE, then cross under): litezero/clinical-trials-gov-scraper at $0.00135 (BRONZE) down to $0.00105 (GOLD+) plus a $0.00005 start fee, and larkmoor/clinical-trials-search at $0.001425 down to $0.001275 (GOLD+) — but with a $0.001 Actor-start fee we don't charge, so on a pull of a few hundred rows the two of us land close together.
Three of the sixteen are cheaper but not like-for-like, and we'd rather say so than pad the count. shahidirfan/ema-medicines-scraper carries an explicit FREE pricing entry ($0 per row), but it scrapes the European Medicines Agency, not ClinicalTrials.gov — a different registry with different studies; it matched this sweep on its description, not its source. litezero/pubmed-medical-research-scraper at $0.0012 down to $0.00084 returns PubMed literature, not registry study records. And fiery_dream/healthcare-intel advertises a $0.00001 dataset-item event plus a $0.00005 start fee, which price-sorts to the top of any comparison — it is the same misleading shape already flagged for cblu and malekh above: an AI healthcare-intelligence product spanning trials, FDA approvals and drug safety, not a per-study scraper, so that per-item rate does not buy you a study row.
Two future-dated pricing entries were read forward explicitly (every tool that filters on "in effect now" would miss them), and neither becomes an undercutter: benthepythondev/clinicaltrials-gov-scraper cuts only its Actor-start fee on 2026-11-04 ($0.029 → $0.00005 at FREE) and leaves its per-row rate at $0.003 down to $0.0024, dearer than us at every rung; entrepreneurial_lens_ehi/clinical-trials-eu-scraper re-files on 2026-10-30 at rates identical to its in-effect entry ($0.004 down to $0.0018, plus a $0.00005 start fee), a no-op change, and it covers the EU CTIS register rather than ClinicalTrials.gov. Nothing in the 116-listing cohort was left unresolvable and none of it is priced per run rather than per row. What we still claim on price is only this: we charge $0.0015 per result with no Actor-start fee, every rate on this page is read from the rival's own live pricing record rather than its marketing copy, and when someone undercuts us we name them here.
FAQ
I already have a list of NCT IDs — can I just fetch those?
Yes, set nctIds (e.g. "NCT04368728, NCT03854955") instead of the search filters. ClinicalTrials.gov's own API 400s the entire request if even one ID in a batch is malformed or doesn't exist — we've verified this live and handle it for you: bad IDs are dropped individually with a named warning, and the rest of your batch still comes back.
Does this cover COVID-19 and long-COVID trials?
Yes — COVID is just another condition here, so conditions: "COVID-19" matches 10,246 registered studies (338 of them currently recruiting, measured live 2026-09-28) and conditions: "long COVID" matches 733. Narrow with the ordinary filters: overallStatus: ["RECRUITING"] for open enrolment, phases for interventional stage, rowsPerStudy: "site" for one row per participating facility. Every row is copied straight from the registry, so the COVID data you get back is the sponsor's own record — no modelling and no case-count estimates — and that is why COVID trials, post-acute-sequelae (PASC) studies and vaccine follow-up studies all come through the same single condition filter.
I got a SUCCEEDED run with 0 rows and no explanation — what happened? As of v0.1.70 this always shows a reason in the run's status message (and in the run log), not just a blank green run. Three cases: (1) your filters are ANDed — combining a narrow condition, sponsor and location at once often genuinely matches nothing; drop one and retry. Also, phases only applies to interventional studies with a phase assigned; pairing it with studyTypes: ["OBSERVATIONAL"] always returns nothing. (2) a watchLabel run found nothing new since its last check — the expected, charge-nothing outcome most of the time. (3) the run did not actually complete (ClinicalTrials.gov stopped answering or the time budget ran out) — that says nothing about how many studies match your filters; re-run it. As of v0.1.71, the plain no-match status message is also a separate, shorter rendering of the same causes — Apify silently cuts a run's status message at 500 characters, and the original full 3-cause wording measured 539 (verified cut mid-word live before this fix); the full text still appears in the run log either way.
I pasted a startUrl with a sidebar filter AND typed the matching field — which one wins?
The typed field does, same as the search-term boxes. Before this was fixed (cycle 1325), the two were silently ANDed instead: a pasted URL carrying aggFilters=status:rec (Recruiting) plus a typed overallStatus: ["COMPLETED"] combined into a contradiction that matches nothing, verified live — declaredMatches: 0 with no warning naming the real cause, just the generic "no studies matched" advice above. That's fixed for all seven sidebar codes a startUrl can carry (status, phase, study type, sex, healthy-volunteers, age group, funder type): set the field in the input form and it overrides whatever the pasted URL said for that filter, exactly like editing the field yourself in the ClinicalTrials.gov UI after following the link. Only the search-term boxes (condition, intervention, sponsor, location, title, outcome, lead sponsor, NCT IDs) and date windows were never affected — those already fully replace rather than AND.
Why can't I combine resultsAvailability: "without" with a resultsFirstPostedDate window?
Because nothing can match it, so we stop the run and tell you instead of billing you for a search that was never going to return a row. "without" means the study has posted no results section, and a study with no results section has no results-posted date at all — there is nothing for the window to range over. Measured live registry-wide on 2026-09-30: resultsAvailability: "without" alone matches 524,648 studies and the widest conceivable results-posted window (1900→2100) matches 80,302, but the two together match 0 — as does an open-ended window with only a From or only a To bound. If you want trials that reported in a given period, use resultsAvailability: "with" (or leave it blank) with the window; if you want the FDAAA-compliance set of trials that never reported, keep "without" and filter on studyCompletionDateFrom/To or primaryCompletionDateFrom/To instead — those are the dates that actually exist on an unreported trial.
What happens if I type a date filter wrong?
As of v0.1.27 the run fails immediately with a clear error naming the field, e.g. "lastUpdatePostedDateFrom" is not a real calendar date: "2026/09/01". Use YYYY-MM-DD, e.g. 2026-08-01. Before this fix, any of the twelve ...DateFrom/...DateTo fields that didn't match YYYY-MM-DD exactly (a slash instead of a dash, US month/day order, a typo) was silently treated as if you'd left it blank — the bound vanished with no warning, the run returned more studies than you asked for, and you were billed for the extra rows. A value with leading/trailing whitespace (" 2026-09-01 ") is still trimmed and accepted.
There are six date filters — which one do I want?
firstPosted* = when the trial was registered (new-trial alerts). studyStart* = when dosing/enrolment begins. primaryCompletion* = the primary-endpoint readout date, which is the one most competitive-intelligence pulls actually mean. studyCompletion* = last visit of the last patient. resultsFirstPosted* = when results were published. lastUpdatePosted* = when the record changed at all, which is the right one for an incremental "what's new since my last pull" job. Every pair is independent, ANDed with the rest, and either bound can be left blank for an open range.
Why did a date window skip trials whose date looks like it's inside it?
Because ClinicalTrials.gov lets sponsors enter a month without a day, and a month-precision date behaves as the 1st of that month in every date window. Measured live (lung cancer, recruiting, Phase 2): primaryCompletionDateFrom: "2026-06-01" / To: "2026-06-04" — a four-day window — returns the trials whose primaryCompletionDate is the bare string "2026-06" (NCT05902988, NCT05913089), while From: "2026-06-05" / To: "2026-06-25" returns only full-date rows and drops them. So a "second half of June" query silently misses every June trial that never stated a day.
Which fields this hits: the three sponsor-entered dates — startDate, primaryCompletionDate, completionDate — are frequently month-only (on one live 10-row recruiting sample: completionDate 6/10, startDate 1/10). The three registry-generated dates — studyFirstPostDate, lastUpdatePostDate, resultsFirstPostDate — were full YYYY-MM-DD on 10/10 of that same sample, so firstPostedDate*, lastUpdatePostedDate* and resultsFirstPostedDate* windows are exact. Workaround for the sponsor-entered three: start the window on the 1st of the month (widening it at most to the start of that month) and filter the rows yourself afterwards, since the row carries the date exactly as the registry publishes it.
What's the difference between facilityName and locations?
locations is the city/state/country text ("Boston, Massachusetts"); facilityName is the site name ("Mayo Clinic"). Use facilityName for site-selection and KOL work where you care which institution is running the trial, not where it sits. Multi-word values are sent as a quoted phrase, so Mayo Clinic does not also match a study at "Cleveland Clinic" in Mayo, Florida.
Are phases, maximumAge and collaborators always present?
No. Measured on a live 50-study sample: phases populated on ~70%, maximumAge on ~48%, collaborators on ~24%. Don't treat a missing value as a scraping error — most studies genuinely don't set these fields. phases is also a list, and the filter matches if your value is anywhere in it: filtering ["PHASE2"] legitimately returns rows reading ["PHASE1","PHASE2"] (2 of 10 on a live lung-cancer run), because ClinicalTrials.gov tags a combined Phase 1/2 trial with both.
Does this return contact names, phone numbers or emails?
No, by design — see above. If you need to contact a trial's coordinator, use the studyUrl to view the listing directly on ClinicalTrials.gov.
Is this legal? Yes. ClinicalTrials.gov is run by the US National Library of Medicine and publishes this API for public reuse. All returned data (excluding the contact fields we deliberately drop) is public-interest study/sponsor/site metadata, not personal data about trial participants.
How do I get only new studies on a schedule, not the whole match set every time?
Set watchLabel to any name, e.g. "my-oncology-watch". The first run under that label is a free baseline: it records every study currently matching your other filters and returns zero rows, charged nothing. Every later run with the same label AND the same other filters returns only studies not already recorded — new since the last run — and only those are charged. Change any filter (a condition, a status, a date window) and that combination gets its own fresh baseline, since it's now a different saved search. The baseline lives in your own Apify account, not ours, so it survives between scheduled runs. Ignored (with a warning) if nctIds is set — a direct-id lookup always returns exactly the ids you asked for, so there's no "new since last run" concept for it.
Baseline size cap. A baseline holds up to 60,000 nctIds in one saved record. If a label's baseline grows past that, the oldest-first-seen ids are dropped — and a dropped id is no longer recognised, so that study comes back as "new" on a later run and is charged again. The run that drops them says so explicitly: a warning in the log, a note on the run's status message, and baselineTruncated / baselineTruncatedTotal (this run / the whole life of the label) in RUN_SUMMARY, on the webhookUrl payload and in the saved record. If you see it, narrow the watch query (conditions, interventions, sponsors, locations, overallStatus, phases, the date windows) or split it across several labels so each baseline stays under the cap. A baseline run also stops recording at 20,000 studies, and already warns separately when it hits that.
My watch says "nothing new" every single run — is it broken?
Check whether you set an upper date bound on a registry-event date. firstPostedDateTo, resultsFirstPostedDateTo and lastUpdatePostedDateTo all filter on when ClinicalTrials.gov did something — and anything new always carries today's date. So once the real calendar date passes a fixed cutoff, no newly-registered study can ever satisfy it again: the registry stops returning new rows entirely, and your watch keeps reporting a perfectly healthy "nothing new" forever. Any run that combines watchLabel with one of these now says so in the log and lists the offenders in RUN_SUMMARY.watchDeadDateBounds (an empty array means you're fine) — alert on that field rather than trusting a quiet run. The fix is to drop the upper bound from the watched query; the matching ...From lower bound is always safe to keep, since it only limits how far back the first baseline reaches.
One wrinkle that makes this easy to miss: with firstPostedDateTo or resultsFirstPostedDateTo, studies already inside your window that are merely revised still match, so watchChanges events keep arriving and the watch looks alive while being blind to every new trial (measured 2026-10-12: 2,677 studies first posted on or before 2020-01-01 had an update posted on or after 2026-09-01). lastUpdatePostedDateTo has no such wrinkle — it blinds the revisions too.
The trial's own dates are a different story and are deliberately not flagged: studyStartDateTo, primaryCompletionDateTo and studyCompletionDateTo describe the trial's timeline, not a registry event, so a study registered today can perfectly well carry a start date from years ago and a fixed upper bound on them keeps admitting new rows indefinitely. Measured 2026-10-12: of 5,660 studies first posted on or after 2026-09-01, 80 have a start date on or before 2020-01-01. They can still be change-blind in the sense described above, which is a separate warning.
A study says RECRUITING — are all of its sites recruiting? (No, and it is not close.)
overallStatus is the study's status; every site row additionally carries that facility's own site.status, and the two routinely disagree. A multi-site trial opens and closes sites on a rolling schedule, so a trial that is globally recruiting can have sites that have not opened yet, have finished accrual, or were withdrawn before enrolling anyone — the sample row above is a real WITHDRAWN site inside a RECRUITING phase-3 study (NCT06246916, pulled live 2026-10-19), and a single overallStatus: ["RECRUITING"] query can return NOT_YET_RECRUITING, ACTIVE_NOT_RECRUITING, COMPLETED, WITHDRAWN, SUSPENDED and TERMINATED site rows alongside the open ones.
There is no input filter for this — ClinicalTrials.gov's own sidebar filters on the study, not the site — so filter site.status yourself on the returned rows: keep RECRUITING for "can refer a patient today", add NOT_YET_RECRUITING for a pipeline view, and drop WITHDRAWN/TERMINATED/SUSPENDED before anyone contacts a facility. Do not assume this is a rounding error: the share of non-recruiting site rows varies widely by therapeutic area, so measure it on your own query rather than taking a single headline rate — two examples, measured live and reproduced identically 28 hours apart (2026-10-19):
Query (overallStatus: ["RECRUITING"]) | Studies | Site rows | Non-recruiting | Breakdown |
|---|---|---|---|---|
conditions: "melanoma" | 41 | 400 | 11.0% | 30 NOT_YET_RECRUITING, 10 ACTIVE_NOT_RECRUITING, 4 COMPLETED |
conditions: "type 2 diabetes" | 103 | 400 | 41.2% | 87 NOT_YET_RECRUITING, 54 ACTIVE_NOT_RECRUITING, 12 WITHDRAWN, 9 COMPLETED, 2 SUSPENDED, 1 TERMINATED |
Does rowsPerStudy: "site" change what watchLabel tracks?
No — "new" is always decided per study (nctId), never per site row. A trial that was already delivered stays excluded even if rowsPerStudy or another display-only setting changes between runs; only filters that change which studies match start a fresh baseline.
What does watchChanges add, and does it cost extra to turn on?
No extra fee — a changed study is billed at the same per-row price as a new one (exploded per-site same as any other row if rowsPerStudy: "site"). Plain watchLabel only ever tells you about studies it has never delivered before; it stays silent forever about one it already sent you, even if that trial later stops recruiting or its enrollment target changes. Set watchChanges: true and each run also compares every already-delivered study's overallStatus, lastUpdatePostDate, enrollmentCount, primaryCompletionDate and completionDate against what they looked like last time; if any moved, the row is re-delivered tagged with _watchChangeType (which field(s) changed) and _watchPrevious (what they used to be). Verified live: seeding a baseline, editing 2 studies' recorded status/enrollment count directly, then rerunning returned exactly those 2 rows with the correct change tags and nothing else — and a plain unchanged rerun after that returned 0 rows again. Existing watch labels created before this feature shipped work immediately; the first run under watchChanges just starts detecting drift from that point forward rather than reporting an artificial backlog.
Important: don't filter on the field you're watching for changes (overallStatus catches most people).
Change detection can only compare a study that is still in this run's match set — so a filter on a field watchChanges tracks is self-defeating: the very change you're watching for is what removes the row from view, and you never hear about it. The clearest case: a watch scoped to overallStatus: ["RECRUITING"] (the exact "watch a sponsor's pipeline" use case this README advertises) can never report a study moving Recruiting → Completed/Terminated, because that move is what drops it out of the filter. The same trap applies to lastUpdatePostedDateTo (a fresh update pushes lastUpdatePostDate past your upper bound and the study vanishes — lastUpdatePostedDateFrom is safe, since an update only ever moves the date later) and to primaryCompletionDateFrom/primaryCompletionDateTo/studyCompletionDateFrom/studyCompletionDateTo (a revised readout date moves outside the window). None of these filters is on by default, so you have to opt into the trap, but it's easy to do without noticing.
What to do: for each field you want alerts on, drop or widen the filter on that field for this watch label and filter your own copy of the rows afterward instead. Widening costs nothing to backfill: a label's first run on a new filter set is a free baseline (0 studies charged), so every historical study the wider query newly matches lands in that baseline for free and is never charged; only genuinely new and genuinely changed studies are billed from then on. Each run that has a change-blind filter set says so in two log warnings and lists them in RUN_SUMMARY.watchChangeBlindFilters (an empty array means nothing is blinding change detection), so a scheduled caller can assert on that field before trusting a quiet "no changes this run".
How is webhookUrl different from Apify's own platform webhooks?
Apify's platform webhooks are configured separately per Task/Actor via the Console or the Webhooks API — useful if you already live in the Apify Console, but extra setup if you're calling this Actor's API directly and just want a completion ping. webhookUrl is a plain input field: set it on the run itself and it POSTs a JSON body (actorRunId, defaultDatasetId, finishedAt, pushed, scanned, pages, a full summary object identical to the RUN_SUMMARY record below, and — if watchLabel is set — watchSeeding/watchNewCount/watchChangedCount) once the run finishes and every row is already pushed and charged. It's best-effort — a slow or failing webhook only logs a warning, it never fails the run, changes the result set, or affects billing.
How do I tell a complete result set from a truncated one?
Read the RUN_SUMMARY key-value record — no webhook needed:
GET https://api.apify.com/v2/actor-runs/<runId>/key-value-store/records/RUN_SUMMARY
{"mode": "search","declaredMatches": 123498,"scanned": 5,"delivered": 5,"pages": 1,"complete": false,"incompleteReason": "max-results","incompleteDetail": "maxResults=5","maxResults": 5,"watchChangeBlindFilters": null}
declaredMatches is ClinicalTrials.gov's own totalCount for your filters — the number the registry says matches — so delivered vs declaredMatches is a comparison you can make in code rather than by eye. It is null when no total was ever obtained, and is never 0 in that case: a request that failed cannot support the claim "the registry has no such trial". complete is deliberately separate from the run's status, because a run can SUCCEED and be truncated at the same time — that pair is exactly what this record exists for. When complete is false the run's status message says so too.
incompleteReason | What happened |
|---|---|
max-results | Your maxResults stopped the run before the match set ran out. |
charge-limit | The run hit the charging limit set on it. |
seed-cap | A watchLabel baseline walk hit the 20,000-study seed cap. |
search-request-failed | ClinicalTrials.gov stopped answering mid-walk (after 4 retries). The rows you got are real, but they are not all of them. |
lookup-request-failed | One or more nctIds could never be checked. |
empty-page-with-token | A page came back empty while pagination said there was more. |
watchChangeBlindFilters (null unless watchChanges is on) lists any filter in this run that narrows on a field watchChanges tracks and therefore hides those changes — an empty array means nothing is blinding change detection this run; see "don't filter on the field you're watching" above.
In nctIds mode, what's the difference between notFoundIds, malformedIds and failedIds?
They are three different facts and folding them together produces a false one. notFoundIds means we asked, the registry answered, and it does not hold that study — a fact you can act on. malformedIds means ClinicalTrials.gov rejected the id's format outright. failedIds means we never got an answer for it (timeout or repeated 5xx): those studies are not known to be missing, and re-running for just those ids is usually all that's needed. notReachedIds lists ids the run never got to because maxResults or a charge limit stopped it first — their absence from the other three lists would otherwise read as "we checked and there was nothing there".
Related guides
- Eight government JSON APIs that need no key — and the specific way each one lies to you — how this API's silent-failure shape compares across all eight free government JSON APIs we scrape.
- The ClinicalTrials.gov API silently caps pageSize at 1000 — and its phase filter doesn't exist where you'd look for it
- A 4-day window on ClinicalTrials.gov returns more trials than a 21-day one — because 42% of its dates have no day — month-only completion dates collapse onto the 1st inside a
RANGE[]filter, so a window starting after the 1st silently drops them. - Eight ways an "only new since last run" watch mode silently stops working — the general failure modes
watchLabelis built to avoid. - We nearly charged our own buyers twice for rows they'd already paid for — a capped watch-mode baseline can silently evict old-but-current ids on a high-volume run, re-delivering (and re-billing) rows already paid for. Reproduced on this Actor, closed with truncation tracking.
- All FetchSmith tools
- Source code