Data may be out of date. Our scheduled checks are behind: the last successful check of the sources finished 20 Oct 2026, 21:00 UTC (16 days ago), more than 36 hours ago. Checks normally run twice daily, so figures on this page may be out of date. See the service status.
ShortageSignal

Developers

The open shortage API

Everything on this site is available as structured JSON — keyless and free under fair use. One envelope across every endpoint, and on every record a source_url where the source publishes a document (SSP entries taken from the register itself carry none), a stable ss_id, a recorded_at timestamp (with recorded_at_precision: no_later_than where we only know the latest it can have been, and recorded_route: whether that first capture was ours or the Internet Archive's) and a provenanceobject — capture route, capture time, sha256 of the captured bytes, the exact Internet Archive capture where a post was recovered, parser version and the parser's anomaly notes. The machine-readable contract lives at /api/openapi.json; changes are listed on the changelog (current schema version 1.2).

Endpoints

GET /api/v1/shortages — every tracked England notice, newest change first.
GET /api/v1/events?since_id=evt:0 — the change feed and the integration path: poll it with since_id and never miss an update. Events are delivered in the order they entered the record, so a fact recorded late (an Internet-Archive recovery with an old date) lands after your cursor, never behind it; when a page is full, meta.next.since_id is the cursor to send back. The original ?since=YYYY-MM-DD + after_key cursor still works unchanged. ?as_of=YYYY-MM-DD returns the record as it stood on that date (only as complete as capture was then; an event whose recorded time is only an upper bound is left out of dates before that bound). Webhooks are not offered. An evt: id is not a permanent citation: when versioned re-parsing re-emits an event it gets a new id and the old one leaves the feed (that is how a correction reaches you). To cite the notice behind an event, use its record_ss_id (post:…), which survives re-parsing.
GET /api/v1/ssps — Serious Shortage Protocols with extension counts and current expiry.
GET /api/v1/status — what the scheduled checks actually did, from the run log: the last successful check of the sources and whether it, or the capture job feeding it, is more than 36 hours behind; the latest parse (observed start and finish, gate state, parked posts and why); and the alerts mode (dry-run until a sent email is recorded). runs holds the newest 30 runs across all jobs, not the whole 30 days. The same figures are on /status; sources are checked twice daily by scheduled jobs, never continuously.GET /api/v1/scotland — NHS Scotland Medicine Supply Alert Notices with the products each lists; a separate surface, never added to England counts.
GET /api/v1/medicines/{slug}— one medicine as the site groups it: its England notices with provenance, and Scotland's separately.GET /api/v1/search?q= — the site search as data: medicine pages, England notices, protocols, NHS Scotland alerts (their own group and count) and discontinuations, with a why_matched sentence on every hit.GET /api/v1/identifiers?q=medikinet — match a brand or generic name to BNF chemical and SNOMED identifiers from NHSBSA open data (Open Government Licence), with the medicine pages on record for that chemical; ?slug=returns one page's asserted identifiers. Name matching, not NHS dictionary coding; see /identifiers.

shortages, ssps and scotland take ?q=; shortages also ?kind= and ?tier=1–4, scotland also ?year=; unknown filter values return 400 rather than being silently ignored.

Exports & feeds

Prefer a spreadsheet or a reader? shortagesignal.xlsx is the whole record as one formatted workbook — shortages, Serious Shortage Protocols and discontinuations on separate sheets, ready for Excel. shortages.csv is the England register as CSV, events.csv is the complete change feed as CSV — every recorded event, no row cap — and scotland.csv is every readable NHS Scotland notice; /feed.xml is RSS of the newest 50 changes, for readers. Every medicine and protocol page has its own Atom feed (/medicine/{slug}/feed.xml, /ssp/{number}/feed.xml), the protocols in force are a subscribable calendar of their scheduled end dates (/ssp/expiries.ics), and the weekly digest is served as a page per ISO week, an Atom feed (/digest/feed.xml) and JSON (/api/v1/digest/latest.json, /api/v1/digest/{week}). Feeds and calendars follow the twice-daily scheduled checks; they are not live. Each notice also has a citable permalink at /notice/{reference} — e.g. /notice/msn-2026-031.

Example

curl -s "https://shortagesignal.co.uk/api/v1/events?since_id=evt:0&limit=200"

{
  "meta": { "service": "ShortageSignal API v1", "schema_version": "1.2", "count": 200,
            "next": { "since_id": "evt:2417" }, "license": "...", "coverage_note": "...", ... },
  "results": [{
    "id": "1", "ss_id": "evt:1", "event_key": "3f9c…",
    "event_type": "ssp_extended",
    "event_date": "2026-07-10",
    "title": "SSP61 – Creon® 25000 gastro-resistant capsules further extended",
    "ssp_number": 61,
    "payload": { "prev_expiry": "2026-07-10", "new_expiry": "2026-10-02" },
    "source_url": "https://cpe.org.uk/our-news/...",
    "recorded_at": "2026-07-10T16:47:23.000Z",
    "recorded_at_precision": "exact", "recorded_route": "first_hand",
    "provenance": { "route": "first_hand", "basis": "cpe_post",
                    "captured_at": "2026-07-10T16:47:23.000Z",
                    "sha256": "…64 hex…", "witness_url": null,
                    "parser_version": 2, "anomalies": [] }
  }]
}

Caching, rate limit & honesty

Every /api/v1 response, including the CSV and XLSX exports, carries Cache-Control (five minutes at the CDN; max-age=0, so your own client revalidates every time) and an ETag: send If-None-Match and an unchanged response costs you a 304 and no body. Sources are checked twice daily, so polling faster than every few minutes gains nothing. A fair-use brake allows 60 requests a minute per client IP on /api/v1/*; over it you get 429 with Retry-After. That brake is counted per function instance, not globally — it is a courtesy limit, not a quota, and we say so rather than pretend otherwise. Please send an identifying User-Agent.

Records are notice-level, grouped by the medicine name in the notice title; they are not yet linked to product codes. The sha256in each record proves what was captured, not that the source page was correct; anomalies describe the source as captured and are never corrected in place. Fuzzy source dates stay fuzzy. Our derived facts and statistics are CC BY 4.0 with attribution; NHSBSA and gov.uk material is reused under the Open Government Licence. If you build something on this, we'd genuinely like to hear about it.

Building on the API?

It's keyless and free today. Leave your email and what you're building and we'll let you know about higher-volume access and any changes to the contract — and we'd love to hear about your project.