import type { Metadata } from 'next'; import Link from 'next/link'; import { PageHeader, Section, Note } from '@/components/ui/section'; import { SITE_URL } from '@/lib/site'; export const metadata: Metadata = { title: 'Developers', description: 'CancerIndex public API: envelope, rate limits, examples and OpenAPI docs.' }; export default function DevelopersPage() { return (
Interactive documentation is served by the API at{' '}
/api/v1/docs
. Base URL: {SITE_URL}/api/v1.
Every successful response wraps its payload with the sources that contributed and the data release, so attribution travels with the data:
{`{
"data": { ... } | [ ... ],
"sources": [ { "slug": "oncotree", "name": "OncoTree", "license": "CC BY 4.0", "retrievedAt": "2026-09-08T09:07:07Z" } ],
"dataRelease": "2026-09-08",
"meta": { "page": 1, "pageSize": 50, "total": 865 } // on paginated endpoints
}`}
Errors return {`{ "error": { "code": "not_found", "message": "…" } }`} with the matching HTTP status.
Authorization: Bearer …): per-key limit according to tier (free 60/min, research and institutional tiers on request).
X-RateLimit-Limit, X-RateLimit-Remaining and Retry-After on 429.
{`# One cancer entity with codes, aliases and counters
curl -s ${SITE_URL}/api/v1/cancers/diffuse-glioma | jq .
# Search across entities
curl -s "${SITE_URL}/api/v1/search?q=glioma" | jq '.data[] | {type, title}'
# Current ranking snapshot for a metric and scope
curl -s "${SITE_URL}/api/v1/rankings/active_trials?scope=geo=WORLD|sex=all|age=all|year=latest|level=top" | jq .
# Same ranking as CSV with attribution rows (served by the web app)
curl -sL "${SITE_URL}/api/export/rankings.csv?metric=active_trials" -o active_trials.csv`}
Public IDs look like CI-CAN-00000123 (cancer), CI-GENE-…, CI-VAR-…, CI-DRUG-…, CI-TRIAL-…, CI-PUB-…, CI-SOURCE-…. They are minted once and never reused; merged entities redirect to the surviving record. Upstream identifiers (NCIt, OncoTree, ICD-10, DOID, HGNC, NCT, PMID) are returned alongside with their match type.