API · v1

The Spread News API

A read-only JSON API for researchers, newsrooms and developers: every BC story we track, who covered it and who owns them, blind spots, outlets and owners. The same data as the site, with the same rules.

What's in it, and what isn't

  • Story metadata, our neutral titles, coverage counts and blind-spot types.
  • Each version of a story: outlet, owner, the publisher's headline, its link and publish time, and whether it's wire copy.
  • Outlets (with feed status) and owners (with where the ownership is documented).

Never article text: no snippets, no feed descriptions, no AI summaries. Hidden stories and stories marked as not about BC are left out, as on the site.

Publishers that opt out of AI. When an outlet's robots.txt blocks AI crawlers (Anthropic's, or every bot), the API withholds its headlines: headline is null and headline_withheld is "publisher_ai_optout". Its versions are still listed, with outlet, link and publish time. A story's title is never taken from a withheld headline; in the rare case that every headline is withheld, title is null with title_withheld set. /api/v1/outlets marks these outlets ai_optout: true.

Terms

  • Our derived data (stories, coverage, blind spots, ownership) is licensed under CC BY 4.0. Credit it as “Data: The Spread News (thespread.ca)”, with a link where you can.
  • Headlines and the articles they link to remain their publishers'. Link to them; don't republish them.
  • For publishers that opt out of AI, we give links as the site does but withhold the headline text. Don't use the API to train AI models on withheld content, or to reconstruct withheld headlines from their URLs.
  • One key per project, at most 3 per inbox a day. Keep it private; don't ship it in a web page or app.
  • We may revoke a key that's abused or shared, and the API may change while it's v1.

Get a key

We store your email and name with a hash of the key, nothing else; the key itself is only in the email. See privacy.

Requests and limits

  • Send the key in the X-API-Key header. Base URL https://thespread.ca/api/v1.
  • 60 requests a minute and 10,000 a day per key, in fixed windows: a minute starts with its first request, and a day is a UTC calendar day. Over either, you get a 429 with Retry-After: the seconds until that window resets.
  • Lists are cached for 60 seconds, so polling more often than once a minute gets the same answer.
  • Errors are JSON: {"error": {"code": "...", "message": "..."}}. 400 bad parameter, 401 missing or invalid key, 404 no such story, 429 over the limit, 503 try again shortly.
curl -H "X-API-Key: tsn_..." \
  "https://thespread.ca/api/v1/stories?region=north&limit=2"
{
  "data": [
    {
      "id": 6263,
      "url": "https://thespread.ca/story/6263",
      "api_url": "https://thespread.ca/api/v1/stories/6263",
      "title": "Quesnel RCMP seek witnesses in fatal Highway 97 hit-and-run",
      "title_withheld": null,
      "region": "north",
      "first_seen": "2026-10-22T20:32:39.000Z",
      "last_seen": "2026-10-23T01:10:00.000Z",
      "newsroom_count": 5,
      "outlet_count": 6,
      "owner_count": 5,
      "blindspot_type": null
    }
  ],
  "next_cursor": "MjAyNi0xMC0yMlQyMDozMjozOS4wMDBafDYyNjM",
  "attribution": "Data: The Spread News (thespread.ca)",
  "license": "CC BY 4.0 (derived data); headlines remain their publishers'"
}

Endpoints

GET /api/v1/stories

Stories, newest first by when we first saw them. Paged with a cursor. Each has one title: the one the site shows (our neutral title, or a publisher's headline until the story has one).

  • since, until: ISO 8601 dates or times, on first_seen (since inclusive, until exclusive).
  • region: island, lower_mainland, interior, north, province. A BC region includes province-wide stories, as on the site; province is only those.
  • owner: an owner slug from /api/v1/owners: stories with a version from that owner.
  • blindspot_type: small_outlets_only, chain_skipped, only_chains.
  • limit: 1 to 100 (default 50). cursor: the next_cursor of the previous page; null on the last page.

GET /api/v1/stories/{id}

One story with its versions: owner, outlet, headline, URL, publish time, is_wire, and the same article on the owner's other outlets (also_on). Blind spots include the proven absences. A story merged into another redirects (308) to it.

GET /api/v1/blindspots

Every blind spot from the last 72 hours, best first: the same set as /blindspots. Optional region (a BC region).

GET /api/v1/outlets

Every outlet we list, tracked or not, with its owner, region, feed status and whether it opts out of AI (ai_optout).

GET /api/v1/owners

Every owner with a listed outlet: legal name, the brand readers see, its group, where the ownership is documented, and its outlets. The same data as /ownership.

Questions

hello@thespread.ca. For press, see /press.