For developers · Change feed

Every change to the catalogue, since a timestamp.

Poll GET /changes?since=… for every insert, update, delete and recall since your last sync — or register a webhook and have them pushed, signed. Keep a local copy current without re-crawling the registers.

GET /v1/changes?since={ts}

  1. AddedCyproteron und Estrogen + Sertindole interactionePHA interactions
  2. UpdatedRecall D-0972-2018 · VALSARTAN AND HYDROCHLOROTHIAZIDE · status ongoingopenFDA drug recalls
  3. UpdatedZubsolvLyfjastofnun / IMA
  4. UpdatedGLIMEPIRIDE VIATRIS 3 mg, compriméBDPM CIS_COMPO (ATC bridge)
  5. UpdatedAmlodipine CT 10 mg, tablettenCBG-MEB
  6. AddedMonograph 68544 (de)Swissmedic AIPS
  7. RecallEnoxaparin Sodium InjectionFDA drug shortages
The latest change from each of the most recently active registers.How change tracking works

Since a timestamp

Poll with a cursor, miss nothing.

Ask for everything since your last sync: each change comes back once, in order, with its source, its time and the values before and after.

  • 20,850,573 changes recorded since ingestion began
  • Start from any timestamp, then page on with the cursor — nothing skipped, nothing twice
  • Signed change webhooks from the Starter API plan
3 Nov 19:06since 4 Nov 06:164 Nov 14:28

Returned by this poll

  1. UpdatedZubsolvLyfjastofnun / IMA · 4 Nov UTC
  2. UpdatedRecall D-0972-2018 · VALSARTAN AND HYDROCHLOROTHIAZIDE · status ongoingopenFDA drug recalls · 4 Nov UTC
  3. AddedCyproteron und Estrogen + Sertindole interactionePHA interactions · 4 Nov UTC
The newest change from each of the most recently active registers, in time order (UTC). A poll with this cursor returns the 3 after it.

How it works

How to stay in sync

  1. Start from a timestamp

    After your initial import, call GET /changes?since= with the time you imported.

  2. Page with the cursor

    Apply each change in order. While next_cursor is set, pass it back as cursor= to resume exactly where the last page ended.

  3. Or get pushed

    From the Starter plan, register a URL and receive the same events as they happen, signed with your webhook secret.

In detail

What the change feed records

Every time an ingestion run promotes new data from a register, each row that changed is written to the feed: the table, the natural key (a register number, an ATC pair, a GTIN), the change type — insert, update, delete or recall — the values before and after, the source and the time. You consume it as an ordered diff instead of re-downloading the catalogue.

Pages are ordered by time and a tie-breaking id, so no change is skipped or repeated when many land in the same second. The cursor is opaque; since is any ISO 8601 timestamp, so you can replay from any point.

01 · Diff

Before and after

Each change carries the row’s values before and after, so you can apply it, audit it or alert on the fields you care about.

02 · Push

Signed webhooks

POST /v1/webhooks with a URL and events (changes.insert, .update, .delete, .recall). The secret is shown once; deliveries are HMAC-SHA256 signed with it.

03 · Validation

Bad files never promote

Each run is validated before promotion. When a register’s file changes shape, the run is blocked and the last good data stays, marked stale.

04 · Freshness

Health on every response

_meta.sources reports each source’s last good sync and schema state (ok, stale, never_run) — freshness is a field, not a guess.

Try it

A real call, end to end.

Pass a timestamp the first time, then the cursor — and get back only what changed.

What is tracked

  • Every promoted change across the registers we ingest — the running total is on this page, read live.
  • Change types: insert, update, delete and recall (recall notices from the registers that publish them).
  • Ordered by occurred_at and id; up to 500 changes per page (default 100).
  • Validated before promotion: a register whose file stopped validating is held back and reported stale.
  • The feed needs the changes:read scope, which every plan includes.
  • Webhooks need the webhooks:write scope, from the Starter plan.
GET /v1/changes?since=…
curl 'https://drug-database.com/v1/changes\
?since=2026-09-01T00:00:00Z&limit=100' \
  -H 'Authorization: Bearer dd_live_…'

# next page
curl 'https://drug-database.com/v1/changes?cursor=eyJhdCI6…' \
  -H 'Authorization: Bearer dd_live_…'
response
{
  "changes": [
    {
      "id": "…",
      "table_name": "drugs",
      "natural_key": { "swissmedic_no": "…" },
      "drug_id": "3f8a1c2d-…",
      "change_type": "update",
      "before": { "is_reimbursed": false },
      "after":  { "is_reimbursed": true },
      "source": "bag_sl",
      "occurred_at": "2026-…"
    }
  ],
  "next_cursor": "eyJhdCI6…",
  "_meta": { "sources": { … } }
}

FAQ

Questions about the change feed

How do I keep a local copy in sync?
Import once, then call GET /v1/changes?since={your import time}. Apply the changes in order, and while next_cursor is set pass it back as cursor= for the next page. Store the last cursor (or timestamp) for your next run.
Is the cursor a timestamp?
No. next_cursor is an opaque token that encodes the position after the last change returned, so paging never skips or repeats a change. since accepts any ISO 8601 timestamp when you want to start from a point in time.
Can I get changes pushed instead of polling?
Yes, from the Starter plan. POST /v1/webhooks with your URL and the events you want (changes.insert, changes.update, changes.delete, changes.recall). The signing secret is shown once at creation and every delivery is HMAC-SHA256 signed with it.
What happens when a register changes its file format?
Each ingestion run is validated before promotion. If too many rows fail validation, promotion is blocked, the last good data keeps being served and the source is reported as stale in _meta.sources.
How many changes are tracked?
Every promoted change since ingestion began. The running total is shown on this page and on the home page, counted from the database.

Ready when you are

Try change feed & webhooks on real data.

Create a key in minutes. The free plan is enough to see real responses and judge the data; the paid plans are where you build.

Free to try · 10 lookups in total · No card required