# ClinicalTrials.gov Change Monitor - Trial Diff Feed (`splendorous_astrolabe_xs9/clinicaltrials-change-monitor`) Actor

Change-detection feed for ClinicalTrials.gov: new trials, status flips (Recruiting to Terminated), new sites and PIs, enrollment changes. The diff, not another snapshot.

- **URL**: https://apify.com/splendorous\_astrolabe\_xs9/clinicaltrials-change-monitor.md
- **Developed by:** [Public Signal Data](https://apify.com/splendorous_astrolabe_xs9) (community)
- **Categories:** AI, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$10.00 / 1,000 change records

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

Learn more: https://docs.apify.com/platform/actors/running/actors-in-store#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

## ClinicalTrials.gov Change Monitor — New Trials, Status Flips, New Sites

> **Docs & recipes:** [pricing math, use-case playbooks and copy-paste API/MCP snippets](https://publicsignaldata.com/actors/clinicaltrials-change-monitor/)

A **change-detection feed** over the official ClinicalTrials.gov v2 API (~600k registered studies). Point it at a therapeutic area, a sponsor, or a country; schedule it daily; and get only what **changed** since the last run:

| `change` | What it means | Who cares |
|---|---|---|
| `new_trial` | A study matching your filters was just registered/entered your funnel | CI teams: a competitor just started a program |
| `status_change` | **Recruiting → Active / Terminated / Completed / Suspended…** (with `previous_status`, `new_status`, `why_stopped`) | CRO BD: enrollment closing = rescue/next-phase conversations; termination = white space |
| `sites_added` | New facilities joined an existing trial — facility name, city, state, country, site status, and PI name exactly as published by CT.gov | Site-selection intel, KOL mapping, "who is expanding where" |
| `enrollment_change` | Target/actual enrollment moved (`previous_enrollment`, `new_enrollment`, `enrollment_delta`) | Enrollment trouble (cuts) or confidence (raises) — both are signals |
| `baseline` | First delta run only — seeds the tracked universe, flagged `is_baseline: true` | Your starting book of record |
| `snapshot` | Snapshot mode rows (no diffing, no state) | One-off pulls, backfills |

**This is not another snapshot scraper.** Snapshot actors re-dump the same thousands of rows every run and leave the diffing to you — and they structurally *cannot* catch the most valuable event: a study whose status flips **out** of your filter (Recruiting → Terminated) simply vanishes from a snapshot. This actor persists a fingerprint of every tracked study in a key-value store and **re-checks tracked studies by NCT ID with no status filter**, so the flip itself is delivered as a record.

### Why not the free CT.gov email alerts?

ClinicalTrials.gov's own "saved search" emails are fine for a patient tracking one disease. They are not a BD tool:

| | Free CT.gov email alerts | This actor |
|---|---|---|
| Format | HTML email, one study per blob | **Structured JSON** records, stable schema |
| What changed | "This study was updated" — go click and guess | Field-level diff: exact status flip, enrollment delta, the specific new sites |
| Site-level detail | No | Facility + city/state/country + site status + PI name (as published) |
| Multi-filter portfolios | One saved search per email | Any number of watchlists via `stateKey`, one per schedule |
| Automation | Copy-paste from your inbox | Dataset → API/webhooks/Zapier/Sheets; schedulable; callable from AI agents via MCP |
| Sponsor watch | Weak | Lead-sponsor filter (`sponsor: "Merck Sharp & Dohme"`) |

And why not Citeline/TrialTrove? Five figures a year. A daily watch here costs pennies (pricing below).

### Input (everything optional — empty input works)

```json
{
  "condition": "non-small cell lung cancer",
  "sponsor": "AstraZeneca",
  "phases": ["PHASE2", "PHASE3"],
  "statuses": ["RECRUITING"],
  "countries": ["United States", "Germany"],
  "mode": "delta",
  "daysBack": 7,
  "stateKey": "nsclc-az"
}
```

Defaults: `condition: "oncology"`, phases 2–3, `RECRUITING`, worldwide, `mode: "delta"`, 7-day baseline window. An **empty** `{}` input is a valid run (live-tested: ~450 baseline studies).

- `condition` takes full CT.gov Essie syntax (`"multiple myeloma" OR "plasma cell myeloma"`).
- `statuses` defines which studies **enter** tracking. Once tracked, a flip to *any* status is still reported — that's the product.
- `daysBack` bounds only the **first** (baseline) run and snapshot mode: "studies updated in the last N days". Subsequent delta runs automatically use the time since the previous run (+3-day safety margin). Set `daysBack: 3650` to baseline the entire matching universe at once.
- `stateKey`: one independent watchlist per key. Changing filters on an existing key is detected (fingerprint) and safely re-baselines instead of emitting a garbage diff.

### Output sample (a status change — the money record)

```json
{
  "change": "status_change",
  "nct_id": "NCT00000001",
  "url": "https://clinicaltrials.gov/study/NCT00000001",
  "brief_title": "A Phase 2 Study of ...",
  "lead_sponsor": "Example Pharma",
  "previous_status": "RECRUITING",
  "new_status": "ACTIVE_NOT_RECRUITING",
  "still_in_tracked_statuses": false,
  "why_stopped": null,
  "phases": ["PHASE2"],
  "conditions": ["Non-Small Cell Lung Cancer"],
  "enrollment_count": 120,
  "enrollment_type": "ESTIMATED",
  "num_sites": 34,
  "countries": ["United States", "Germany"],
  "last_update_post_date": "2026-08-05",
  "primary_completion_date": "2027-06-30",
  "observed_at": "2026-08-06T22:34:59+00:00"
}
```

`new_trial`/`baseline`/`snapshot` records also carry the full `sites` array; `sites_added` records carry `new_sites` (just the additions) plus `sites_added_count`. Fields not published by CT.gov are `null`, never missing. See `sample_output.json` for real live-run output.

### Use cases

- **CRO business development** — schedule one run per therapeutic area every morning. `status_change` to `ACTIVE_NOT_RECRUITING` means enrollment just closed (time the next-phase pitch); `TERMINATED` with `why_stopped` is a displacement opening; `sites_added` in your geography is a warm site-network lead.
- **Pharma competitive intelligence** — one `stateKey` per competitor via the `sponsor` filter: every new program, phase population, enrollment cut, and geographic expansion, as JSON, the day CT.gov posts it.
- **Site selection / KOL mapping** — `sites_added` with facility, city, state, and PI name (only as published professionally by CT.gov).
- **Investors / biotech analysts** — enrollment cuts and quiet terminations often front-run press releases.

### Delta mode details

State lives in the named key-value store `clinicaltrials-change-monitor-state` under your `stateKey`. Each run: (1) fetches studies matching your filters updated since the last run, (2) re-checks tracked NCT IDs missing from that fetch — with **no status filter** — so out-of-filter flips are caught, (3) diffs, pushes only changes, and updates state. Studies not updated at all are carried forward silently at zero cost. A study that leaves your filter set is reported once (`status_change` with `still_in_tracked_statuses: false`, or `left_filter`) and then dropped; if it later re-enters, it shows up as `new_trial`.

### Using from Claude / MCP

Once published, call it through the Apify MCP server (`https://mcp.apify.com`):

```json
{
  "tool": "your-username/clinicaltrials-change-monitor",
  "input": { "sponsor": "Pfizer", "condition": "", "stateKey": "pfizer-watch" }
}
```

Ask your agent: *"What changed in Pfizer's trial portfolio this week?"* — that's one delta run.

### Pricing

**$0.01 per record.** Delta records are few and high-value — a daily watch on a whole therapeutic area typically emits a handful to a few dozen records ($0.05–$0.50/day); a single-sponsor watch usually costs under $0.10/day. The one-time baseline on default filters is ~450 records (~$4.50); bound it with `daysBack`/`maxStudies` if you want a cheaper start. Snapshot mode is priced the same — use `daysBack` and `maxStudies` to control row counts.

### Fair use & data notes

Data comes from the official, free, keyless ClinicalTrials.gov v2 API (data.gov terms), fetched politely: capped page sizes, inter-page pauses, exponential backoff honoring `Retry-After`. Records are public regulatory disclosures. Person names (site PIs/contacts) are included only exactly as published professionally by ClinicalTrials.gov, and only in their professional capacity; no other personal data is collected or enriched.

# Actor input Schema

## `condition` (type: `string`):

Condition or therapeutic-area search (ClinicalTrials.gov <code>query.cond</code> — full Essie syntax works, e.g. <code>oncology</code>, <code>non-small cell lung cancer</code>, <code>"multiple myeloma" OR "plasma cell myeloma"</code>). Set to an empty string to match any condition (combine with a sponsor filter, or expect a very large universe).

## `sponsor` (type: `string`):

Restrict to studies whose <b>lead sponsor</b> name matches this text (e.g. <code>Merck Sharp & Dohme</code>, <code>Pfizer</code>, <code>AstraZeneca</code>). Leave empty for all sponsors. Matches the lead sponsor only, not collaborators.

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

Trial phases to include. Leave empty for all phases (includes phase-less observational/expanded-access studies).

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

Studies enter the tracked universe while in one of these statuses. Once tracked, a study that flips to <i>any other</i> status still gets reported as a <code>status\_change</code> (that's the point) and then leaves the tracked set.

## `countries` (type: `array`):

Keep only studies with at least one site in one of these countries, exactly as ClinicalTrials.gov names them (e.g. <code>United States</code>, <code>Germany</code>, <code>Japan</code>). Leave empty for worldwide.

## `mode` (type: `string`):

<b>delta</b> (default) compares against this actor's previous run (persisted in a named key-value store) and pushes only changes: <code>new\_trial</code>, <code>status\_change</code>, <code>enrollment\_change</code>, <code>sites\_added</code>. The first delta run seeds the baseline and emits every matching study as <code>change: "baseline"</code>. <b>snapshot</b> pushes every matching study every run and touches no state.

## `daysBack` (type: `integer`):

For the <b>first</b> delta run (the baseline) and for <b>snapshot</b> mode: only include studies whose ClinicalTrials.gov record was updated within this many days. Bounds the size (and cost) of the baseline; the tracked universe then grows organically as more studies get touched. Subsequent delta runs ignore this and use the time since the previous run instead. Set high (e.g. 3650) to baseline the entire matching universe at once.

## `maxStudies` (type: `integer`):

Safety cap on studies fetched from the API per run.

## `stateKey` (type: `string`):

Advanced: name of the record inside the <code>clinicaltrials-change-monitor-state</code> key-value store used for delta comparison. Use a different key per watchlist/schedule so they don't cross-contaminate. If you change the filters for an existing key, the actor detects it and safely re-baselines.

## Actor input object example

```json
{
  "condition": "oncology",
  "sponsor": "",
  "phases": [
    "PHASE2",
    "PHASE3"
  ],
  "statuses": [
    "RECRUITING"
  ],
  "countries": [],
  "mode": "delta",
  "daysBack": 7,
  "maxStudies": 2000,
  "stateKey": "default"
}
```

# 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 = {
    "condition": "oncology",
    "phases": [
        "PHASE2",
        "PHASE3"
    ],
    "statuses": [
        "RECRUITING"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("splendorous_astrolabe_xs9/clinicaltrials-change-monitor").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 = {
    "condition": "oncology",
    "phases": [
        "PHASE2",
        "PHASE3",
    ],
    "statuses": ["RECRUITING"],
}

# Run the Actor and wait for it to finish
run = client.actor("splendorous_astrolabe_xs9/clinicaltrials-change-monitor").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 '{
  "condition": "oncology",
  "phases": [
    "PHASE2",
    "PHASE3"
  ],
  "statuses": [
    "RECRUITING"
  ]
}' |
apify call splendorous_astrolabe_xs9/clinicaltrials-change-monitor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,splendorous_astrolabe_xs9/clinicaltrials-change-monitor"
        }
    }
}

```

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/6XwNEOmQbkm0JfU87/builds/e5nIBvs5aidbdn4Gq/openapi.json
