SPB Git

spb/cancerindex

Public
8.3 KB · 163 lines markdown
1# CancerIndex — Public API23Base URL: `https://www.cancerindex.io/api/v1` (proxied by the web app to the Fastify service on4`http://127.0.0.1:8251/v1`). Read-only. JSON only.56**The reference documentation is generated from the route schemas and is always current:**78- Swagger UI: `/v1/docs`9- OpenAPI 3.1: `/v1/openapi.json`1011This page only explains the conventions and gives examples. Do not edit endpoint descriptions here;12change the zod schemas in `apps/api/src/routes/*.ts` (CLAUDE.md §357).1314## Conventions1516### Envelope1718Every `/v1` response is wrapped:1920```json21{22  "data": …,23  "sources": [{ "id": "CI-SOURCE-00000001", "slug": "oncotree", "name": "OncoTree", "license": "CC BY 4.0", "attribution": "OncoTree (MSKCC), CC BY 4.0, https://oncotree.mskcc.org", "url": "https://oncotree.mskcc.org" }],24  "dataRelease": "CancerIndex 2026-09",25  "generatedAt": "2026-09-08T09:33:19.433Z",26  "total": 865, "limit": 50, "offset": 0, "hasMore": true27}28```2930`sources` are the distinct upstream sources behind `data` (license and attribution included so you31can comply with redistribution terms). Pagination fields appear on list endpoints; `limit` ≤ 200.3233### Identifiers3435Entities are addressed by their public id (`CI-CAN-00000364`) or a human reference: cancer slug36(`lung-adenocarcinoma`), HGNC symbol (`BRAF`, aliases accepted), variant slug, drug slug, `NCT` id,37PMID. Merged cancers redirect to the surviving entity.3839### Errors4041```json42{ "error": { "code": "not_found", "message": "cancer \"foo\" not found" }, "requestId": "…" }43```4445Codes: `bad_request` (400, with zod `details`), `unauthorized` (401), `not_found` (404),46`rate_limited` (429), `service_unavailable` (503), `internal` (500). Every response carries47`x-request-id`; quote it when reporting a problem.4849### Rate limits and API keys5051Anonymous: 60 requests/minute per IP. With `Authorization: Bearer <key>`: the key's own limit52(`api_keys.rate_limit_per_minute`). Headers: `x-ratelimit-limit`, `x-ratelimit-remaining`,53`x-ratelimit-reset`, `retry-after` on 429. Mint a key:5455```bash56pnpm --filter @cancerindex/api create-key -- --label "Lab X" --email lab@example.org --tier research --rpm 60057```5859Only the sha256 hash and a prefix are stored; the key is printed once.6061### Scientific labels6263Payload objects that carry a number also carry the category (`observed_data`, `curated_evidence`,64`regulatory_status`, `computed_metric`) and a `provenance` object (source, dataset, version,65`retrievedAt`, URL). Missing data is `null` or an empty list — never `0` substituted for "unknown".66`counters: null` means counters have not been computed yet.6768## Endpoints (summary)6970| Method + path | Purpose |71|---|---|72| `GET /healthz` | `{ ok, dataRelease, db }` |73| `GET /v1/cancers` | list; `q`, `level=top|all`, `type`, `malignant`, `hematologic`, `pediatric`, `rare`, `sort=name|active_trials|publications_5y`, `limit`, `offset` |74| `GET /v1/cancers/:id` | entity + aliases + codes + hierarchy (parents/children per hierarchy type, breadcrumbs) + anatomy + counters + completeness + current rankings + last 20 changes |75| `GET /v1/cancers/:id/statistics` | epidemiology observations with per-row provenance + charting `series`; filters `metric`, `geography`, `sex` |76| `GET /v1/cancers/:id/survival` | survival observations with provenance |77| `GET /v1/cancers/:id/genes` | cohort frequencies (denominators) + curated evidence per gene, cancer + descendants |78| `GET /v1/cancers/:id/variants` | variants with evidence counts by level/direction/type |79| `GET /v1/cancers/:id/drugs` | evidence by drug (level/direction/significance) + jurisdiction-aware approvals |80| `GET /v1/cancers/:id/trials` | trials for the cancer and descendants; `status`, `phase`, `interventionalOnly` |81| `GET /v1/cancers/:id/publications` | publication edges + literature count windows (query stored) |82| `GET /v1/genes` · `GET /v1/genes/:symbol` | genes; detail with cancers (curated + cohort), variants, drugs, literature |83| `GET /v1/variants/:id` | variant, ClinVar interpretations, CIViC evidence grouped by cancer, edges |84| `GET /v1/drugs` · `GET /v1/drugs/:id` | drugs; detail with evidence by cancer, approvals, trials |85| `GET /v1/trials` · `GET /v1/trials/:nct` | trial search (`q`, `status`, `phase`, `cancer`, `country`); detail with condition/intervention mappings, locations (200), publications |86| `GET /v1/publications/:pmid` | publication + entity edges (abstract truncated, links) |87| `GET /v1/rankings/metrics` | metric catalog with available scopes |88| `GET /v1/rankings` | `metric`, `geography`, `sex`, `age`, `year`, `level`; snapshot metadata + metric definition + rows |89| `GET /v1/rankings/:metric/:cancerId/explain` | "Why this rank?": inputs, previous rank, neighbours, lineage trace |90| `GET /v1/search?q=` | cross-entity search, ≤ 20 typed results, exact > alias > prefix > fuzzy |91| `GET /v1/sources` · `GET /v1/sources/:slug` | registry, license status, connector health, runs, counts |92| `GET /v1/stats` | live counts (60 s cache) |93| `GET /v1/changes` | change events; `entityType`, `entityId`, `kind`, `since` |94| `GET/POST /v1/admin/*` | operators only (`x-admin-token`): connectors, run/pause/resume, runs/:runId, unresolved + resolve, trace, jobs/counters, jobs/rank, audit |9596## Examples9798```bash99API=http://127.0.0.1:8251100101# health102curl -s $API/healthz103104# homepage ticker105curl -s $API/v1/stats | jq .data106107# cancers whose name/alias starts with "glio", sorted by active trials108curl -s "$API/v1/cancers?q=glio&sort=active_trials&limit=5" | jq '.data[] | {id, slug, name, counters}'109110# one cancer with hierarchy and current rankings111curl -s $API/v1/cancers/lung-adenocarcinoma | jq '.data | {name, primaryNcitCode, hierarchy: .hierarchy.parents, rankings}'112113# epidemiology observations with provenance for a top-level cancer, US only114curl -s "$API/v1/cancers/lung-cancer/statistics?geography=USA&sex=all" | jq '.data.series[0]'115116# trials recruiting for a cancer and its subtypes117curl -s "$API/v1/cancers/lung-adenocarcinoma/trials?status=RECRUITING&limit=10" | jq '.data[] | {nctId, briefTitle, phases}'118119# a gene and its cancers120curl -s $API/v1/genes/BRAF | jq '.data | {symbol, counters, cancers: .cancers.curatedEvidence[0:3]}'121122# a variant: evidence separated by cancer123curl -s $API/v1/variants/braf-v600e | jq '.data.evidenceByCancer[] | {cancer: .cancer.name, acceptedItems}'124125# ranking: active trials, top-level set126curl -s "$API/v1/rankings?metric=active_trials&level=top&limit=10" | jq '{snapshot: .data.snapshot, top: [.data.rows[] | {rank, name: .cancer.name, value}]}'127128# why this rank? (lineage down to raw records)129curl -s "$API/v1/rankings/active_trials/lung-cancer/explain?level=top" | jq '.data | {rank, previousRank, inputs, trace}'130131# search132curl -s "$API/v1/search?q=glio" | jq '.data[] | {type, name, match}'133134# sources and license status135curl -s $API/v1/sources | jq '.data[] | {slug, licenseStatus, status, health: .connector.health, records: .counts.sourceRecords}'136137# authenticated request (higher limit)138curl -s -H "Authorization: Bearer cix_…" $API/v1/stats -D - -o /dev/null | grep -i x-ratelimit139140# admin: enqueue a connector run, then watch it141curl -s -X POST -H "x-admin-token: $ADMIN_TOKEN" -H 'content-type: application/json' \142  -d '{"mode":"incremental","maxMinutes":30,"reason":"manual refresh"}' $API/v1/admin/connectors/clinicaltrials/run143curl -s -H "x-admin-token: $ADMIN_TOKEN" $API/v1/admin/connectors | jq '.data[] | {connectorId, health, lastSuccessAt, recentRuns: .recentRuns[0].status}'144145# admin: curation queue and resolution146curl -s -H "x-admin-token: $ADMIN_TOKEN" "$API/v1/admin/unresolved?entityKind=cancer&limit=20"147curl -s -X POST -H "x-admin-token: $ADMIN_TOKEN" -H 'content-type: application/json' \148  -d '{"cancerId":"CI-CAN-00000364","reason":"synonym per NCIt"}' $API/v1/admin/unresolved/123/resolve149```150151Resolving an unresolved label adds a curated `cancer_aliases` row (`alias_type = synonym`,152`source_terminology = curation`, `source_id` = the label's source), re-maps `trial_conditions` rows153that carry the same normalised text, writes `audit_log` and a `change_events` row; every other154connector picks the alias up on its next run through `CancerResolver`.155156## Local development157158```bash159pnpm dev:api                 # tsx watch, http://127.0.0.1:8251160pnpm --filter @cancerindex/api test      # unit tests + read-only smoke tests (skipped if DB unreachable)161pnpm --filter @cancerindex/api typecheck162```163
Files · 4354e64ec39d7ace4e77b667021fe28e703ed10d
Menu

Theme