Documentation

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 wantEndpoint
Find mentions of somethingGET /posts/search?q=
A chronological feedGET /posts
Read a podcast episodeGET /posts/{post}/transcript
Look up a person or a publicationGET /people?q=, GET /sources?q=
What the corpus is aboutGET /topics, GET /sponsors
Group publications to search withinGET/POST /collections
Watch for somethingPOST /projects, then POST /alerts
What your alerts foundGET /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.