Urgent.News

One page, thousands of outlets. See who else covered it.

Editions

API

API reference

The coverage-metadata endpoints — authentication, rate limits, every parameter and field, with real examples. The open story feeds need no key and are listed at /api.json.

Two layers

The open layer — the story feeds, country editions, RSS and the MCP server — is free, unmetered and needs no key. This page documents the metered layer: the propagation record, which is what a key unlocks. Three endpoints, all authenticated, all JSON, all CORS-open.

EndpointReturnsPlan
GET /api/propagation.jsonWhat is spreading fastest now, ranked by how many outlets carry itany key
GET /api/coverage/{id}.jsonThe full propagation record for one storyany key
GET /api/bulk/coverage.jsonCoverage records for up to 500 stories in one requestDesk

Authentication

Send your key in the Authorization header on every request:

curl -H "Authorization: Bearer un_your_key_here" \
  https://urgent.news/api/propagation.json?hours=24

An x-api-key: un_… header is accepted as an alternative. Never put the key in the URL — query strings land in server logs, browser history and Referer headers. Keys are issued instantly at /keys, never expire, and an account may hold up to 10. A missing or bad key does not fail the open endpoints — it is served at the anonymous tier; on the metered endpoints it returns 401.

Rate limits

Calls are counted per account, per UTC day — pooled across all your keys, not per key. Every response, success or error, reports where you stand:

Response headerMeaning
x-planThe plan that answered — open, signal or desk
x-ratelimit-limitYour daily allowance
x-ratelimit-remainingCalls left today
x-key-statusok, or why a key resolved to a lower tier (e.g. unknown-key)

Allowances: Open 1,000/day · Signal 25,000/day · Desk 250,000/day (see /pricing). Over the limit is a clean 429 with the reset time — never a silently truncated result. Open-endpoint requests are not counted. Responses are Cache-Control: private, no-store and vary by key: cache them locally per key if you cache at all, never in a shared cache.

GET /api/propagation.json

The stories moving fastest across outlets right now, ranked by distinct outlet count. This is the discovery endpoint — use it to find clusters, then follow each coverage link for the full record.

ParameterDefaultRangeNotes
hours241 – 168Look-back window. Capped by plan: Open 24h, Signal/Desk up to 168h. Ask for more than your plan allows and the response tells you it was capped.
min_outlets32 – 50Only clusters carried by at least this many outlets.
limit251 – 100Maximum clusters returned.

ExampleGET https://urgent.news/api/propagation.json?hours=24&min_outlets=4&limit=1

{
  "self": "https://urgent.news/api/propagation.json",
  "window_hours": 24,
  "min_outlets": 4,
  "generated": "2026-08-18T11:58:43.705Z",
  "count": 1,
  "stories": [
    {
      "cluster_id": 1615193,
      "title": "At least 2 hit in a shooting at a school in the southern Philippines",
      "url": "https://urgent.news/2026/08/18/at-least-2-hit-in-a-shooting-at-a-school",
      "outlets": 10,
      "countries": 9,
      "first_seen": "2026-08-18T02:20:50.000Z",
      "latest_seen": "2026-08-18T04:17:03.000Z",
      "span_hours": 1.94,
      "velocity_outlets_per_hour": 5.16,
      "coverage": "https://urgent.news/api/coverage/1615193.json"
    }
  ],
  "plan": "signal"
}

When your requested hours exceeds your plan cap, the response adds window_capped_from, window_cap_reason and an upgrade pointer, and window_hours reflects what you actually got.

GET /api/coverage/{id}.json

The complete propagation record for one story, keyed by its cluster_id (or any story id in the cluster). Takes no query parameters.

ExampleGET https://urgent.news/api/coverage/1615193.json

{
  "story": { "id": 1615193, "title": "…", "url": "https://urgent.news/2026/08/18/…" },
  "cluster_id": 1615193,
  "outlets": 10,
  "outlets_discovered": 0,
  "countries": 9,
  "first_seen": "2026-08-18T02:20:50.000Z",
  "latest_seen": "2026-08-18T03:47:02.000Z",
  "span_hours": 1.44,
  "velocity_outlets_per_hour": 6.96,
  "by_country": [ { "country": "IN", "name": "India", "outlets": 2 }, … ],
  "by_tier":    [ { "tier": "news", "outlets": 7 }, … ],
  "timeline": [
    {
      "outlet": "Kyiv Independent",
      "slug": "kyiv-independent",
      "domain": "kyivindependent.com",
      "country": "UA",
      "tier": "news",
      "first_seen": "2026-08-18T02:20:50.000Z",
      "minutes_after_first": 0,
      "discovered": false
    }, …
  ],
  "plan": "signal",
  "disclaimer": "…"
}

An unknown or hidden story returns 404 {"error":"unknown story"}.

GET /api/bulk/coverage.json — Desk plan

The coverage record — timelines included — for many stories in a single request, so a caller ingesting a window of news makes one call instead of one per story. Requires the Desk plan; other keys receive 403.

ParameterDefaultRangeNotes
hours241 – 168Look-back window (Desk allows the full 168h).
min_outlets22 – 50Only clusters with at least this many outlets.
limit1001 – 500Maximum stories returned. truncated: true in the response means more matched than were returned.

Each entry in stories[] has the same shape as a single coverage response (outlets, countries, span, velocity, and a full timeline).

Field glossary

FieldWhat it means
outletsDistinct outlets this pipeline polled that carried the story. This is the headline "N outlets" figure.
outlets_discoveredAdditional outlets seen in the discovered corpus (a wider crawl) rather than polled directly. Reported separately because it carries a different clock — do not add the two into one figure.
countriesDistinct countries among the carrying outlets (ISO-3166 alpha-2).
span_hoursHours between the first and last outlet filing.
velocity_outlets_per_hourOutlets per hour across the span. null — never Infinity — when every outlet filed in the same instant.
minutes_after_firstIn the timeline: minutes between the first outlet and this one. The corroboration clock.
discoveredtrue if this outlet came from the discovered corpus rather than a direct poll.
tierOutlet class: news, wire, or community.

A note on honesty: the outlet count is not yet reprint-adjusted — a wire story and the outlets that reprint it verbatim currently each count as one outlet. Treat the count as carrying breadth, not independent corroboration. A distinct independent figure is planned.

Errors

StatusWhenBody
401No key, or an unrecognised one, on a metered endpointerror: "api key required" + a get_one pointer
429Daily quota exhaustederror: "daily quota exceeded" + used, limit, resets (ISO), upgrade
403Bulk endpoint on a non-Desk planerror: "plan does not include bulk export" + upgrade
404Unknown or hidden story on the coverage endpointerror: "unknown story"

A worked example

Find what is spreading, then pull the full record for the top story:

# 1. what is moving right now
curl -H "Authorization: Bearer un_…" \
  "https://urgent.news/api/propagation.json?hours=6&min_outlets=5"

# 2. take a cluster_id from the response and get its record
curl -H "Authorization: Bearer un_…" \
  https://urgent.news/api/coverage/1615193.json

Using the data

Use it in your products, research and reporting, including commercially. Please attribute urgent.news as the source of the coverage data, keep the publisher links intact where you show individual stories, and don't present machine-written summaries as human reporting. The full terms are at /terms. Every response carries a disclaimer field restating that the prose is machine-written from other outlets' reporting.