Trial0

API & MCP · v1 · live registry data

Your agent can call this
and get real availability back.

The same matching engine behind the site, as a JSON API and a hosted MCP server — patient profile in, ranked frontier options out, queried live from ClinicalTrials.gov. A published sandbox key means no signup step to get stuck on: a model that reads this page can make a correct call on its first attempt.

Public sandbox key — no signup

fc_test_sandbox

Send it as Authorization: Bearer fc_test_sandbox. Shared and rate-limited to 25 requests per 24 hours per IP. The counter lives in memory, so it also resets whenever the deployment restarts — fine for evaluation, not for production.

Authentication

One header, one key

Every endpoint takes Authorization: Bearer <key>. An unrecognised key is rejected with a structured 401 rather than silently downgraded to the sandbox tier — a caller with a stale key must be told, not quietly served under different limits.

# Base URL
https://trial0.com/api/v1

# Every request
-H 'authorization: Bearer fc_test_sandbox'

CORS is open for GET and POST from any origin, so browser-side agents can call it directly.

Endpoints

Three routes, all live

Every command below is complete and runnable as written. Responses are trimmed for readability but the field names are exact; each one also carries source (the exact registry query URL) and disclaimer.

POST/api/v1/match

Match a patient profile

The primary endpoint. Queries ClinicalTrials.gov live and returns every open frontier option — interventional trials and expanded-access programs — with a per-criterion match state. Only `condition` is required; every profile field you omit simply produces fewer checks. States from free-text criteria are signposts (mentioned/unknown), never verdicts — only the site decides eligibility.

curl -s https://trial0.com/api/v1/match \
  -H 'authorization: Bearer fc_test_sandbox' \
  -H 'content-type: application/json' \
  -d '{
    "condition": "glioblastoma",
    "stage": "Recurrent",
    "biomarkers": ["MGMT methylated", "IDH wild-type"],
    "age": 54,
    "location": {"lat": 42.3601, "lng": -71.0589, "radius_miles": 200},
    "limit": 5
  }'
{
  "total_found": 87,
  "options": [
    {
      "nct_id": "NCT0XXXXXXX",
      "title": "A Phase 2 Study of ... in Recurrent Glioblastoma",
      "study_type": "interventional",
      "overall_status": "RECRUITING",
      "phases": ["PHASE2"],
      "criteria_checks": [
        { "criterion": "Study open to new participants", "state": "matched",
          "evidence": "Registry overall status: RECRUITING" },
        { "criterion": "Age within published bounds", "state": "matched",
          "evidence": "Published bounds: 18 Years – none; patient age 54." },
        { "criterion": "Biomarker: MGMT methylated", "state": "mentioned_inclusion",
          "evidence": "This biomarker appears in the published inclusion criteria text. A promising signpost — the site must confirm it applies." }
      ],
      "unknown_count": 1,
      "sites": [
        { "facility": "Massachusetts General Hospital", "city": "Boston",
          "registry_status": "RECRUITING", "distance_miles": 0.8 }
      ],
      "registry_url": "https://clinicaltrials.gov/study/NCT0XXXXXXX"
    }
  ],
  "verified_fields_included": false,
  "verified_site_counts": { "NCT0XXXXXXX": 2 },
  "source": {
    "registry": "ClinicalTrials.gov",
    "query_url": "https://clinicaltrials.gov/api/v2/studies?query.cond=...",
    "retrieved_at": "2026-08-24T12:00:00.000Z"
  },
  "disclaimer": "Trial0 is a navigation service, not a medical provider. ..."
}
GET/api/v1/availability/{nctId}

Site-level availability for one study

Every site the registry lists for a study, with its published per-site status. Pro-plan keys additionally get the verified overlay — observed recruiting status with a last-verified date and whether the investigator accepts outside referrals. Other keys get verified_site_count: how many sites carry verified observations, without their contents.

curl -s https://trial0.com/api/v1/availability/NCT04575922 \
  -H 'authorization: Bearer fc_test_sandbox'
{
  "study": {
    "nct_id": "NCT04575922",
    "title": "...",
    "overall_status": "RECRUITING",
    "sites": [
      { "facility": "...", "city": "...", "registry_status": "RECRUITING" }
    ]
  },
  "verified_fields_included": false,
  "verified_site_count": 1,
  "source": { "registry": "ClinicalTrials.gov", "query_url": "...", "retrieved_at": "..." }
}
GET/api/v1/conditions

Launch areas & intake vocabulary

The disease areas with structured intake — their slugs, registry expressions and suggested stage/biomarker vocabulary — plus the rare-disease free-text path. Call this first if you are unsure what to pass as `condition`; any free-text condition name is also accepted.

curl -s https://trial0.com/api/v1/conditions \
  -H 'authorization: Bearer fc_test_sandbox'
{
  "launch_areas": [
    { "slug": "alzheimers", "name": "Alzheimer's disease & early-stage dementia",
      "stages": ["Mild cognitive impairment (MCI)", "..."],
      "biomarkers": ["Amyloid PET positive", "..."] },
    { "slug": "glioblastoma", "name": "Glioblastoma", "stages": ["..."], "biomarkers": ["..."] },
    { "slug": "pancreatic-cancer", "name": "Pancreatic cancer", "stages": ["..."], "biomarkers": ["..."] }
  ],
  "rare_disease_path": { "slug": "rare-disease", "name": "Rare disease (any condition)" }
}

Model Context Protocol

Hosted MCP server

JSON-RPC 2.0 over HTTP (Streamable HTTP transport) at https://trial0.com/api/mcp. Stateless — no session to resume, so a dropped connection is a non-event. Implements initialize, tools/list, tools/call and ping.

{
  "mcpServers": {
    "trial0": {
      "type": "http",
      "url": "https://trial0.com/api/mcp",
      "headers": {
        "Authorization": "Bearer fc_test_sandbox"
      }
    }
  }
}
  • match_frontier_options

    The /match endpoint as a tool: patient profile in, ranked frontier options with per-criterion match states out.

  • get_site_availability

    Site-level availability for one NCT id — registry status for everyone, the verified overlay on Pro keys.

  • list_launch_conditions

    The launch disease areas and intake vocabulary. Call it when unsure what `condition` accepts.

Every tool returns a readable text summary plus the full payload as structuredContent. The text keeps the match-state caveats attached, so a model relaying results to a human carries the “verify with the site” framing with it.

curl -s https://trial0.com/api/mcp \
  -H 'authorization: Bearer fc_test_sandbox' \
  -H 'content-type: application/json' \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
curl -s https://trial0.com/api/mcp \
  -H 'authorization: Bearer fc_test_sandbox' \
  -H 'content-type: application/json' \
  -d '{
    "jsonrpc": "2.0",
    "id": 2,
    "method": "tools/call",
    "params": {
      "name": "match_frontier_options",
      "arguments": {
        "condition": "pancreatic-cancer",
        "stage": "Metastatic",
        "biomarkers": ["KRAS G12C"],
        "limit": 5
      }
    }
  }'

Protocol errors follow JSON-RPC: -32601 for an unknown method, -32602 for bad params — and a -32602 carries the same known_keys the REST API would return.

Errors

Every error is self-correcting

Errors are always {"error": {"code", "message", "docs_url", ...}}. When the offending field is an enum, the body carries known_keys — the complete list of accepted values — so a failed call can be fixed on the next attempt without a human reading these docs.

$ curl -s https://trial0.com/api/v1/match \
    -H 'authorization: Bearer fc_test_sandbox' \
    -H 'content-type: application/json' \
    -d '{"condition": "glioblastoma", "sex": "F"}'

{
  "error": {
    "code": "invalid_request",
    "message": "`sex` must be one of the known values.",
    "field": "sex",
    "known_keys": ["male", "female"],
    "docs": "https://trial0.vercel.app/docs/api#errors"
  }
}
CodeHTTPMeaning
invalid_request400A field is malformed or unknown. `field` names it; `known_keys` lists valid values for enums.
unauthorized401Missing or unrecognised key. The body includes the public sandbox key.
not_found404No such study exists on ClinicalTrials.gov.
rate_limited429Limit reached. Retry-After says when; issued keys have monthly quotas.
idempotency_key_reuse409Same Idempotency-Key, different request body.
internal_error5xxUnexpected failure — including the registry being unreachable. Nothing is cached or invented in its place; retry later.

Idempotency

Retries are safe

Send an Idempotency-Key header on any POST. A replay with the same key and the same body returns the byte-identical original response, plus Idempotency-Replayed: true — so a caller that lost the first reply can tell a repeat from a fresh result. Reusing a key with a different body is a 409 rather than a silent wrong answer. Keys are held for 24 hours in memory, per instance.

curl -s https://trial0.com/api/v1/match \
  -H 'authorization: Bearer fc_test_sandbox' \
  -H 'content-type: application/json' \
  -H 'idempotency-key: case-7f3a-attempt-1' \
  -d '{"condition": "alzheimers", "stage": "Mild / early-stage", "limit": 10}'

Rate limits

Headers on every response

The sandbox key allows 25 requests per rolling 24 hours per IP. Every response — success or error — carries X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset (unix seconds). A 429 adds Retry-After. The counter is in-memory and per-instance, so it resets on deploy; account keys will use a durable store. See pricing for production volumes.

Contract

What this API will not do

It will not decide free text

matched/not_matched come only from structured registry fields. A biomarker in the criteria text is reported as a mention to verify with the site — software asserting free-text eligibility would be a guess dressed as a verdict.

It will not invent results

Every option comes from a live ClinicalTrials.gov query whose exact URL ships in source.query_url. If the registry is down, the answer is an error — never a cached or remembered list.

It will not blur provenance

Registry-published fields and Trial0's first-hand observations are separate structures. A verified badge always means we checked with the site, and it always carries its last-verified date.

Trial0 is a navigation service, not a medical provider. Nothing returned by this API is medical advice; eligibility is always decided by the trial site, and treatment decisions belong with the patient and their own doctors.