# ClinicalTrials.gov Study Monitor (new & status changes) (`keepable_injera/clinical-trials-monitor`) Actor

Find and monitor clinical studies from the ClinicalTrials.gov API v2 by condition, term, sponsor and status, with onlyNew change detection for new trials, status changes and posted results. Source: ClinicalTrials.gov; data processing date included. Unofficial; not affiliated with NLM or NIH.

- **URL**: https://apify.com/keepable_injera/clinical-trials-monitor.md
- **Developed by:** [Chee Chiaw Lim](https://apify.com/keepable_injera) (community)
- **Categories:** Business, Automation, Developer tools
- **Stats:** 2 total users, 1 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$3.00 / 1,000 study results

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

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

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

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

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## ClinicalTrials.gov Study Monitor (new & status changes)

Search ClinicalTrials.gov studies and monitor new studies and changes to status, phases, enrollment, completion dates, results availability, and last update dates. The Actor reads the public documented API v2. It fetches current study data on every run and adds the ClinicalTrials.gov data processing timestamp to every study record.

### Input examples

Fetch up to five recruiting studies, including studies returned by earlier runs.

```json
{
  "condition": "type 2 diabetes",
  "statuses": ["RECRUITING"],
  "maxItems": 5,
  "onlyNew": false
}
```

Monitor new and changed phase 2 or phase 3 studies updated within the last 19 days.

```json
{
  "term": "immunotherapy",
  "sponsor": "National Cancer Institute",
  "location": "United States",
  "phases": ["PHASE2", "PHASE3"],
  "updatedWithinDays": 19,
  "maxItems": 100,
  "onlyNew": true,
  "stateStoreName": "clinical-trials-monitor-state"
}
```

### Input fields

| Field | Default | Meaning |
| --- | --- | --- |
| `condition` | Empty | API `query.cond`. |
| `term` | Empty | API `query.term`. |
| `sponsor` | Empty | API `query.spons`. |
| `location` | Empty | API `query.locn`. |
| `statuses` | `[]` | API `filter.overallStatus`, as a comma list. |
| `phases` | `[]` | Local phase filter, matched against any listed phase. `NA` also matches absent phases. |
| `updatedWithinDays` | Unset | Positive integer, up to 36500. Uses `AREA[LastUpdatePostDate]RANGE[YYYY-MM-DD,MAX]`, starting that many UTC calendar days ago. |
| `maxItems` | `100` | Maximum study pushes, from 1 to 10000. |
| `onlyNew` | `true` | Emit only new or changed studies. Set false to include unchanged studies. |
| `stateStoreName` | `clinical-trials-monitor-state` | Named KV store containing the `STUDIES` history. |

At least one nonempty `condition`, `term`, or `sponsor` is required. `location` alone is insufficient. Search strings use the source API's query syntax.

Supported statuses are `RECRUITING`, `NOT_YET_RECRUITING`, `ACTIVE_NOT_RECRUITING`, `COMPLETED`, `TERMINATED`, `WITHDRAWN`, `SUSPENDED`, `ENROLLING_BY_INVITATION`, and `UNKNOWN`.

Supported phases are `EARLY_PHASE1`, `PHASE1`, `PHASE2`, `PHASE3`, `PHASE4`, and `NA`.

### Output sample

This illustrative record uses fictional study details.

```json
{
  "nctId": "NCT01234567",
  "briefTitle": "Study of Drug A in Type 2 Diabetes",
  "officialTitle": "A Randomized Study of Drug A in Adults With Type 2 Diabetes",
  "overallStatus": "RECRUITING",
  "phases": [
    "PHASE2",
    "PHASE3"
  ],
  "studyType": "INTERVENTIONAL",
  "conditions": [
    "Type 2 Diabetes"
  ],
  "interventions": [
    {
      "name": "Drug A",
      "type": "DRUG"
    }
  ],
  "leadSponsor": "Example University",
  "collaborators": [
    "Example Research Institute"
  ],
  "enrollment": 120,
  "startDate": "2026-01-01",
  "primaryCompletionDate": "2027-01",
  "completionDate": "2027-06",
  "firstPostDate": "2025-12-01",
  "lastUpdatePostDate": "2026-10-19",
  "hasResults": false,
  "locationCountries": [
    "United States",
    "Canada"
  ],
  "locationsCount": 3,
  "studyUrl": "https://clinicaltrials.gov/study/NCT01234567",
  "source": "ClinicalTrials.gov",
  "dataProcessedAt": "2026-10-20T06:00:00",
  "checkedAt": "2026-10-20T12:30:00.000Z",
  "changeType": "new",
  "changedFields": []
}
```

Missing scalar fields are `null`. Missing lists are empty arrays, and missing `hasResults` is false. Dates retain the source precision, such as `2027-01`.

The default dataset holds studies and uncharged error records. The overview view exposes study fields. The errors view projects `recordType`, `error`, `source`, and `checkedAt` for diagnostics. The default KV store holds `RUN_SUMMARY`, including start and finish times, counts of fetched, eligible, and pushed studies, charge-limit status, and any error. Fetched counts cover studies yielded after the local phase filter and before stopping.

### Change detection

The named state store maps NCT IDs to SHA-256 fingerprints and the corresponding tracked field values. Fingerprints use stable JSON of `overallStatus`, `phases`, `enrollment`, `primaryCompletionDate`, `completionDate`, `hasResults`, and `lastUpdatePostDate`. Phase order does not affect the fingerprint.

A study absent from history has `changeType: "new"` and `changedFields: []`. A different fingerprint produces `changeType: "changed"` and the names of changed tracked fields. An equal fingerprint produces `changeType: "unchanged"` and an empty list. Changes to other fields do not trigger an output with `onlyNew: true`.

The Actor saves history only after a successful study push. Duplicate IDs within one run emit once. Charge limits and failed pushes leave unpushed studies eligible for a later run. With `onlyNew: false`, successful pushes also update history.

Dataset pushes and state writes are separate operations. A crash between them can produce duplicates on retry. Avoid overlapping runs sharing a state store. Use separate store names for independent histories. Reset history by removing the `STUDIES` record. History grows with the number of emitted NCT IDs.

### Pricing

The PPE event is `study`, priced at **$0.003 per emitted study**, or $3 per 1,000 studies. There is no run fee. Unchanged studies emitted with `onlyNew: false` incur the same event charge. Skipped studies and error records are not charged. Error records use `Actor.pushData` without a charge event. Local runs do not bill PPE. Store pricing requires manual configuration as described in `PUBLISHING.md`.

### Limits

- Requests start at most once per second per host. HTTP 429 and 5xx responses retry up to three times with exponential backoff and `Retry-After` support. Requests time out after 30 seconds.
- API requests use `pageSize=100`, JSON format, and `nextPageToken` pagination. The API supports larger pages, but this Actor uses 100.
- `maxItems` limits emitted studies. Phase filters and unchanged records may require reading more studies or all matching pages. A run stops at its output or charge limit. Each run starts a fresh search, so repeated capped runs can revisit earlier pages.
- Results depend on source availability and the source's latest processed dataset. They are not a complete record of every intermediate change. Removed studies and studies that no longer match the query do not emit deletion events.
- This tool provides study data, not medical advice, enrollment eligibility, or a guarantee that a study is still recruiting. Check the linked study page for current details.

### Attribution and terms

Data source: **ClinicalTrials.gov**, a United States Government database. ClinicalTrials.gov provides the source data without charge. This Actor's price covers its collection and monitoring service. No proprietary rights are asserted over the database.

`dataProcessedAt` clearly displays the date the data were processed by ClinicalTrials.gov. It comes from the `dataTimestamp` in `/api/v2/version`, fetched once per run. `checkedAt` is the Actor's record processing time. Keep the ClinicalTrials.gov attribution and data processing date visible when distributing these records. Runs retrieve current data each time; refresh exported data to keep it current.

ClinicalTrials.gov data carry an international copyright outside the United States and its Territories or Possessions. Some data may be subject to third-party copyright. Use and redistribution remain subject to the [ClinicalTrials.gov terms and conditions](https://clinicaltrials.gov/about-site/terms-conditions). The local [TERMS.md](TERMS.md) records the terms check used for this implementation.

### Modifications

The Actor selects the fields shown in the output sample and flattens nested API modules. It extracts sponsor and collaborator names, intervention names and types, enrollment counts, and date strings without date-type metadata. It reduces locations to a location count and unique country names. It omits contact objects entirely, including all contact emails and phone numbers. Other source fields are omitted.

It supplies null or empty defaults for missing fields, adds the study URL, attribution, source processing timestamp, check time, and change metadata, and applies optional local phase filters. It sorts and deduplicates phases only for fingerprint comparison. Study content is otherwise retained in the source's form. It does not output the full source database.

### Disclaimer

This Actor is unofficial and is not affiliated with or endorsed by ClinicalTrials.gov, the U.S. National Library of Medicine, or the National Institutes of Health. It uses public documented APIs only, no logos, and no login.

### Local development

Node 20 or newer is required. Tests use `node:test`, stubbed fetch, and local temporary storage. They do not need network access.

```sh
npm test
```

A normal `npm start` reads the sample input and calls the live API. For an offline local run, preload `scripts/offline-fetch.mjs` to replace fetch with small realistic API fixtures. Use an isolated storage directory with an `INPUT.json` under `key_value_stores/default/`.

```sh
mkdir -p storage/offline-verification/key_value_stores/default
cp storage/key_value_stores/default/INPUT.json storage/offline-verification/key_value_stores/default/INPUT.json
APIFY_TOKEN='' APIFY_IS_AT_HOME='' APIFY_PURGE_ON_START=false \
CRAWLEE_STORAGE_DIR=storage/offline-verification \
APIFY_LOCAL_STORAGE_DIR=storage/offline-verification \
NODE_OPTIONS='--import ./scripts/offline-fetch.mjs' npm start
```

Read `datasets/default/` and `key_value_stores/default/RUN_SUMMARY.json` beneath that directory after the run. The preload is a verification tool and is never imported by production code.

# Actor input Schema

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

Condition query. At least one of condition, term, or sponsor is required.

## `term` (type: `string`):

Free text study query.

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

Sponsor query.

## `location` (type: `string`):

Location query.

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

Overall status filter sent to the API.

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

Local phase filter. NA matches studies with no applicable phase.

## `updatedWithinDays` (type: `integer`):

Optional UTC LastUpdatePostDate range through MAX.

## `maxItems` (type: `integer`):

Maximum study records pushed this run.

## `onlyNew` (type: `boolean`):

Skip unchanged studies in the named state store.

## `stateStoreName` (type: `string`):

Separate named stores keep independent study histories.

## Actor input object example

```json
{
  "condition": "type 2 diabetes",
  "statuses": [],
  "phases": [],
  "maxItems": 10,
  "onlyNew": true,
  "stateStoreName": "clinical-trials-monitor-state"
}
```

# Actor output Schema

## `studies` (type: `string`):

No description

## `errors` (type: `string`):

No description

## `runSummary` (type: `string`):

No description

# 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": "type 2 diabetes",
    "maxItems": 10
};

// Run the Actor and wait for it to finish
const run = await client.actor("keepable_injera/clinical-trials-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": "type 2 diabetes",
    "maxItems": 10,
}

# Run the Actor and wait for it to finish
run = client.actor("keepable_injera/clinical-trials-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": "type 2 diabetes",
  "maxItems": 10
}' |
apify call keepable_injera/clinical-trials-monitor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,keepable_injera/clinical-trials-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/HgBkASgPJxzLnWSbO/builds/DjDdjP7PyyMKT9UtO/openapi.json
