The reef public API

A read-only window onto reef's biotech entity graph: 370,885 resolved companies, researchers, papers, patents, trials, and funding, each with provenance. No key required for the preview. This is the guide; the full, interactive API reference (params, schemas, language samples, try-it) is generated from the spec.

Overview

Base URL https://api.reef.bio/v1. Every endpoint is GET and returns JSON. Responses are stable, documented shapes (decoupled from our internals), and every record carries its source so you can judge it.

format
JSON; UTF-8
methods
GET only (read-only preview)
versioning
path-versioned at /v1

Auth & rate limits

No authentication in the preview tier. Call the endpoints directly. Requests are rate-limited per IP; every response carries the headers:

X-RateLimit-Limit
requests allowed per window
X-RateLimit-Remaining
requests left in the current window
X-RateLimit-Reset
unix time when the window resets

Over the limit returns 429 with a Retry-After header.

A free API key raises the per-minute ceiling to 20 requests/min and adds a 500 requests/month budget on top, enough to build and test against, not to run in production. Pro drops the monthly budget and raises the rate to a flat 60 requests/min, for $99/mo.

Quickstart

# coverage by kind
curl https://api.reef.bio/v1/stats
# search, then open a result
curl "https://api.reef.bio/v1/entities?q=glutaminase&kind=company"
curl https://api.reef.bio/v1/entities/<id>

MCP server

The same graph as agent tools, over the Model Context Protocol. Point Claude, ChatGPT, or any MCP-capable client at the endpoint and it can search, traverse, rank, and pull dossiers from the graph as native tools, no glue code. It is a thin wrapper over this REST API, so it carries the identical read-only, public, rate-limited contract.

MCP https://core.reef.bio/mcp/

Streamable-HTTP transport. Add it to your client's MCP server config (most take a URL); no key required in the preview tier.

The tools mirror the endpoints on this page: search_entities, get_entity, traverse, aggregate, whitespace, concentration, rank_people, collaborators, dossier, comparables, and catalyst_calendar, each returning the same JSON shapes documented below.

Autocomplete

GET /v1/autocomplete

Fast type-ahead: lean {id, name, kind, slug} suggestions for a partial query. The entry point: resolve a name to an entity, then look it up or search.

q
partial text (required; empty returns no suggestions).
kind
restrict to one kind.
limit
suggestions to return, default 10, max 25.
curl "https://api.reef.bio/v1/autocomplete?q=crispr"

Filter by mechanism

GET /v1/entities?modality=…

Filter the graph by biology, not just text. reef's mechanism-of-action enrichment, index-backed and combinable with q and kind. Get valid values from /facets.

target
molecular target (e.g. EGFR).
modality
e.g. antibody, small molecule.
mechanism
mechanism of action (e.g. agonist).
therapeutic_area
e.g. oncology.
curl "https://api.reef.bio/v1/entities?modality=antibody&therapeutic_area=oncology"

The response envelope echoes the active filters under filters.

target matches an entity by either its primary target or any additional target it carries. A bispecific antibody or a combination trial can act on more than one target: target=EGFR and target=ERBB2 both return a patent that targets both, even though only one is its primary.

modality works the same way: an antibody-drug conjugate is both an antibody and a small-molecule payload, so modality=antibody and modality=small molecule can both return it. The secondary form is returned under moa.secondary_modalities.

technology is a separate axis from the mechanism filters above: a named method or platform (e.g. CRISPR-Cas9, directed evolution), not a drug's therapeutic form. One entity can employ several technologies and one technology spans many entities, so it's a graph edge (employs), not a single-valued field. Combine it with kind the same way as the other filters.

curl "https://api.reef.bio/v1/entities?kind=patent&technology=CRISPR-Cas9"

Field selection

GET /v1/entities?fields=…

Trim any /entities query to just the top-level keys you need: smaller, faster payloads. id is always returned; sub-blocks (like moa) come whole. See /schema for the full field list.

curl "https://api.reef.bio/v1/entities?q=crispr&fields=id,name,moa"

Get one entity

GET /v1/entities/{id}

The full record including per-field provenance. Supports ETag / If-None-Match (a match returns 304); a merged duplicate 301s to the surviving entity.

curl https://api.reef.bio/v1/entities/78feeec8-25c7-4d7a-aa19-2db1c7aa1131

Example response (Merz Aesthetics GmbH, trimmed):

{
  "id": "78feeec8-25c7-4d7a-aa19-2db1c7aa1131",
  "kind": "company",
  "name": "Merz Aesthetics GmbH",
  "slug": "merz-aesthetics-gmbh",
  "identifiers": {
    "domain": null,
    "cik": null,
    "ticker": null,
    "uei": null,
    "orcid": null,
    "doi": null
  },
  "moa": {
    "target": null,
    "target_id": null,
    "target_symbol": null,
    "modality": null,
    "secondary_modalities": [],
    "mechanism": null,
    "therapeutic_area": null,
    "disease_id": null,
    "disease_label": null,
    "confidence": null
  },
  "platform_type": "therapeutics",
  "product": null,
  "pos": null,
  "funding": null,
  "signals": {
    "prominence": 51.6,
    "momentum": 0,
    "rank_in_kind": 5339,
    "degree": 5,
    "activity_90d": 0,
    "computed_at": "2026-08-11T06:17:19.634629+00:00"
  },
  "attributes": {
    "tech_extraction": {
      "count": 0,
      "scanned_at": "2026-07-17T23:39:47.936386+00:00"
    }
  },
  "provenance": {
    "source": "clinicaltrials",
    "observed_at": "2025-10-01T00:00:00+00:00",
    "identity_confidence": null,
    "fields": [
      {
        "field": "canonical_name",
        "source": "clinicaltrials",
        "confidence": 1,
        "observed_at": "2025-10-01T00:00:00+00:00"
      },
      {
        "field": "platform",
        "source": "llm:global.anthropic.claude-haiku-4-5-20251001-v1:0",
        "confidence": 0.65,
        "observed_at": "2026-08-12T02:04:09.204092+00:00"
      }
    ]
  },
  "updated_at": "2026-08-12T02:04:09.204092+00:00",
  "_links": {
    "self": "/v1/entities/78feeec8-25c7-4d7a-aa19-2db1c7aa1131",
    "edges": "/v1/entities/78feeec8-25c7-4d7a-aa19-2db1c7aa1131/edges"
  }
}

Get by slug

GET /v1/entities/by-slug/{kind}/{slug}

Resolve a stable, shareable URL (kind + slug) to the same record as the id form.

curl https://api.reef.bio/v1/entities/by-slug/company/<slug>

Edges

GET /v1/entities/{id}/edges

One-hop relationships, both directions, live edges only. The response carries the flat edges list plus by_relation: the same edges grouped into typed cards ({relation: {count, edges}}), and relation_counts.

Example response:

{
  "entity_id": "78feeec8-25c7-4d7a-aa19-2db1c7aa1131",
  "edges": [
    {
      "relation": "sponsors",
      "direction": "out",
      "observed_at": null,
      "neighbor": {
        "id": "5a0fb65f-a5ef-4466-b0f6-48177dd61f21",
        "kind": "trial",
        "name": "Investigation of the Effectiveness and Safety of MRZF111 in the Treatment of Décolleté Wrinkles",
        "slug": "nct-nct04177212",
        "_links": {
          "self": "/v1/entities/5a0fb65f-a5ef-4466-b0f6-48177dd61f21"
        }
      }
    },
    {
      "relation": "sponsors",
      "direction": "out",
      "observed_at": null,
      "neighbor": {
        "id": "bed5caab-20f6-4f34-ad15-1e61c291f9b3",
        "kind": "trial",
        "name": "A Study of NT 201 in Adults With Moderate to Severe Platysma Prominence in Europe (PLATINUM EU)",
        "slug": "nct-nct07210463",
        "_links": {
          "self": "/v1/entities/bed5caab-20f6-4f34-ad15-1e61c291f9b3"
        }
      }
    }
  ],
  "relation_counts": {
    "founded_by": 1
  }
}

Traverse

GET /v1/entities/{id}/traverse

Bounded multi-hop walk from an entity (depth + fan-out capped).

depth
hops, default 2 (max 4).
relations
optional comma-separated allowlist of edge relations to follow.
curl "https://api.reef.bio/v1/entities/<id>/traverse?depth=2"

Facets

GET /v1/facets

Valid mechanism-of-action filter values (target / modality / area) with counts. Call this first to discover what you can filter on.

curl "https://api.reef.bio/v1/facets?kind=company"

Schema

GET /v1/schema

The data dictionary in one call: entity kinds + counts, the selectable entity_fields (for field selection), the moa_facets you can filter by, and the relations that edges use.

curl https://api.reef.bio/v1/schema

Coverage

GET /v1/stats

Live per-kind counts: the honest coverage of the graph.

{
  "counts": {
    "paper": 134574,
    "person": 93280,
    "trial": 44776,
    "patent": 29848,
    "catalyst": 19641,
    "technology": 12371,
    "company": 11559,
    "product": 8757,
    "filing": 6170,
    "funding_event": 5376,
    "institution": 1938,
    "target": 1313,
    "approval": 760,
    "opportunity": 270,
    "funder": 242,
    "program": 10
  },
  "total": 370885
}

The entity object

Every entity serializes to this shape:

id
stable UUID
kind
one of the entity kinds
name
canonical name
slug
stable slug for the by-slug URL
identifiers
external ids: domain, cik, ticker, uei, orcid, doi
moa
mechanism: target, modality, mechanism, therapeutic_area, confidence
platform_type
chassis/platform classification (companies)
attributes
curated, source-specific fields (raw internal data is never exposed)
provenance
source, observed_at, identity_confidence, plus per-field fields[] on the detail endpoint
updated_at
last change
_links
self + edges URLs

provenance is first-class: every field can tell you where it came from. List items omit the per-field provenance.fields.

Entity kinds

company
biotech / life-science companies · 11,559
person
researchers, founders, inventors · 93,280
paper
publications (PMID / DOI) · 134,574
patent
patents + inventors / assignees · 29,848
trial
clinical trials (NCT) · 44,776
funder
funding organizations (VCs, agencies) · 242
funding_event
rounds + grants · 5,376
approval
FDA drug approvals · 760
opportunity
grant / funding opportunities · 270
program
drug programs / assets, clustered by target + modality · 10
product
the engineered/offered thing a company makes or sells · 8,757
institution
research institutions / universities / labs · 1,938
catalyst
a dated upcoming or recent milestone (readout, PDUFA date, approval) · 19,641
technology
a named method or platform (e.g. CRISPR-Cas9, directed evolution) · 12,371
target
a biological target (gene/protein/pathway) linked to entities beyond their primary target · 1,313

Errors & status codes

Errors use a consistent envelope:

{ "error": { "code": "not_found", "message": "entity not found" } }
200
ok
301
a merged entity → redirects to its surviving record
304
not modified (your If-None-Match ETag still matches)
400
bad_request: malformed id or parameter
404
not_found: no such entity
429
rate_limited: slow down (carries Retry-After)

Pagination

Browse responses (no q) carry an exact total plus offset, limit, and next_offset. Page by passing next_offset back as offset; when it's null, you've reached the end. Text search (q) is capped at 50 relevance-ranked results. Deep pagination over a ranking isn't meaningful.

Sync & change feed

GET /v1/changes

Keep a local mirror in sync without polling the whole graph. Pass since=<ISO 8601> for the first call, then echo back the response's next_cursor as after on each following call to walk forward without gaps or repeats. Rows are oldest-first; each carries op: upsert (created or updated) or merged (a duplicate folded into another record via merged_into, treat as a delete/redirect of id). This is a pull feed, not a push webhook: there is no server-initiated delivery, so poll on whatever cadence fits your use case.

curl "https://api.reef.bio/v1/changes?since=2026-01-01"

Continue from where you left off: ?after=<next_cursor>. One of since or after is required; limit caps at 200 per page.

Caching

GETs send Cache-Control and a weak ETag. Send the ETag back as If-None-Match to revalidate cheaply. An unchanged entity returns 304 with no body. Detail + list responses cache for 60s; aggregates (/stats, /facets) for 300s.