REST API
A plain JSON API over HTTPS at https://sputnikintelligence.com/api/v1. Everything an
MCP agent can do is here too, one endpoint per tool.
This page is the conventions. The endpoint reference is every operation, and openapi.json is the same thing machine-readable, if you would rather generate a client.
Five things that hold everywhere
Everything is addressed by slug
Not by a database id. A slug looks like k3mq-8w1p-ttz4, it is opaque, and it comes
back on every search and list result. Passing a number gets a 404 that tells you so, rather than
silently finding the wrong row.
Slugs are opaque on purpose: they carry no meaning, so they never go stale when a publication renames itself, and they do not leak how many rows we hold.
Every list has the same envelope
{ "items": [...], "total": 1204, "next_cursor": "50" }
Every one — including the short nested ones like an alert's destinations, which come back with a
real total and a null cursor rather than a different shape. You never have to know
which kind of list you are holding. See Paging.
Every detail response is wrapped in data
{ "data": { "slug": "k3mq-8w1p-ttz4", "title": "..." } }
Every error has a code
{ "message": "...", "code": "invalid_cursor", "errors": { "cursor": ["..."] } }
Branch on code, never on the message text. See Errors.
Timestamps are ISO 8601, in UTC
2026-08-19T06:00:00Z. A null timestamp means we do not know it, which for
published_at genuinely happens — some feeds do not date their items.
What is where
| You want | Endpoint |
|---|---|
| Find mentions of something | GET /posts/search?q= |
| A chronological feed | GET /posts |
| Read a podcast episode | GET /posts/{post}/transcript |
| Look up a person or a publication | GET /people?q=, GET /sources?q= |
| What the corpus is about | GET /topics, GET /sponsors |
| Group publications to search within | GET/POST /collections |
| Watch for something | POST /projects, then POST /alerts |
| What your alerts found | GET /alert-hits |
Search and browse are two endpoints
GET /posts/search takes a q and ranks by relevance.
GET /posts takes no query and gives you a chronological feed, newest first,
optionally narrowed to one collection, publication, person, topic or sponsor.
They are separate because a single endpoint whose valid parameters changed depending on whether
you passed q was hard to describe and easy to get wrong.
Booleans in query strings
true, false, 1 and 0 all work, everywhere.
You do not have to remember which endpoint wants which.
Generating a client
openapi.json is an OpenAPI 3.1 description of the whole surface, served publicly and unauthenticated. It is checked against the live routes on every build, in both directions — an endpoint that is not described, or a description with no endpoint, fails.