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

tr0_test_sandbox

Send it as Authorization: Bearer tr0_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://www.trial0.com/api/v1

# Every request
-H 'authorization: Bearer tr0_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. Each returns real, request-time registry data — this page intentionally does not substitute fabricated sample studies. The OpenAPI document defines every response field; each live answer 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://www.trial0.com/api/v1/match \
  -H 'authorization: Bearer tr0_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
  }'
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://www.trial0.com/api/v1/availability/NCT04575922 \
  -H 'authorization: Bearer tr0_test_sandbox'
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://www.trial0.com/api/v1/conditions \
  -H 'authorization: Bearer tr0_test_sandbox'

Model Context Protocol

Hosted MCP server

JSON-RPC 2.0 over HTTP (Streamable HTTP transport) at https://www.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://www.trial0.com/api/mcp",
      "headers": {
        "Authorization": "Bearer tr0_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://www.trial0.com/api/mcp \
  -H 'authorization: Bearer tr0_test_sandbox' \
  -H 'content-type: application/json' \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
curl -s https://www.trial0.com/api/mcp \
  -H 'authorization: Bearer tr0_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://www.trial0.com/api/v1/match \
    -H 'authorization: Bearer tr0_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.com/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://www.trial0.com/api/v1/match \
  -H 'authorization: Bearer tr0_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.