Perspectives API
Where named public voices stand, story by story. One row per position: who, the claim, the author's own words (≤25), the date, the platform, the link. No full text, ever. Read-only. Every example on this page is a real response captured 2026-09-24 17:47 UTC.
newsreel.co/perspectives/apiSpec/-/openapi.jsonBase URLhttps://newsreel-perspectives-api.onrender.comFormatJSON · add _shape=array for a plain list · drop .json for the browsable UIData45,420 positions · 517 voices · 100 story clusters · rebuilt dailyAuth & billing
Authorization: Bearer <key>Or in the URL&key=<key> on any request, for a chatbot or browser that cannot set headers. Stripped before the query runs; never loggedOpenAPIGET /-/openapi.json, no key. Import it into a ChatGPT custom GPT Action (auth Bearer) or any OpenAPI toolClaudeMCP server at /-/mcp with the same bearer headerKeyissued per partner, tied to a Stripe customer; a key with no billing account answers 402Price$0.03 per successful call, metered, invoiced monthly by Stripe. Errors and rate-limited calls are not billedLimits300 requests/min per key · 500 rows/response · 2 s/queryHealthGET /-/versions.json, no keycurl -H "Authorization: Bearer $KEY" \
"https://newsreel-perspectives-api.onrender.com/positions/stories_recent.json?_shape=array"
# the same call with the key in the URL
curl "https://newsreel-perspectives-api.onrender.com/positions/stories_recent.json?_shape=array&key=$KEY"
Content-Type: application/json; charset=utf-8
access-control-allow-origin: *
Cache-Control: private, no-store
link: <https://newsreel-perspectives-api.onrender.com/positions/topics.json?_shape=array&_size=1&_next=abortion-reproductive>; rel="next"
What comes back
Every endpoint returns JSON. With _shape=array the body is a list of row objects; without it Datasette wraps rows in {rows, columns, next, …}. Numbers are numbers, dates are YYYY-MM-DD strings, nulls are null. Confidence fields are calibrated probabilities from the labeller, present for posts labelled from 19 Sep 2026 and null before; nothing is inferred to fill the gap.
Endpoints
GET /positions/stories_recent.json
The 100 newest story clusters. Take an id from here into the spread queries.
| Parameter | Type | Meaning |
|---|---|---|
| No parameters | ||
curl -H "Authorization: Bearer $KEY" \
"https://newsreel-perspectives-api.onrender.com/positions/stories_recent.json?_shape=array"
[
{
"id": "state-and-local-politics-stories-span-dozens-of-unrelated-ev",
"headline": "State and local politics stories span dozens of unrelated events",
"date": "2026-09-24",
"position_count": 10,
"voice_count": 10
},
{
"id": "congress-does-little-as-mcconnell-absence-corruption-scandal",
"headline": "Congress does little as McConnell absence, corruption scandals dominate",
"date": "2026-09-24",
"position_count": 8,
"voice_count": 8
}
]GET /positions/spread_by_party.json
The product: what politicians in one party are saying about one story, grouped by the claim they make. Group rows by position_label.
| Parameter | Type | Meaning |
|---|---|---|
story_id | string | from stories_recent |
party_group | string | Democratic · Republican · Independent |
Party comes from Wikidata (P102), politicians only, never inferred from text.
curl -H "Authorization: Bearer $KEY" \
"https://newsreel-perspectives-api.onrender.com/positions/spread_by_party.json?story_id=iran-war-drags-on-costs-mount-as-congress-fights-back&party_group=Republican&_shape=array"
[
{
"position_label": "Deterrence and Escalation Support",
"voice": "Matt Gaetz",
"party": "Republican Party",
"quote": "Following our raging success in Iran, should the United States lead a military campaign in Yemen against the H…",
"date": "2026-09-20",
"platform": "x",
"source_url": "https://x.com/mattgaetz/status/2101665459917525149",
"stance": "lean",
"stance_confidence": null
},
{
"position_label": "Sanctions & Pressure Support",
"voice": "Katie Britt",
"party": "Republican Party",
"quote": "As my colleague and dear friend Senator Lindsey Graham said in his final days, the ability to pass this bill a…",
"date": "2026-09-17",
"platform": "x",
"source_url": "https://x.com/SenKatieBritt/status/2100383428579393659",
"stance": "strong",
"stance_confidence": null
}
]GET /positions/spread_by_tag.json
Same spread for an editorial tag from the glossary.
| Parameter | Type | Meaning |
|---|---|---|
story_id | string | from stories_recent |
tag | string | one of the 16 glossary tags, e.g. anti-war, conservative, media critic |
curl -H "Authorization: Bearer $KEY" \
"https://newsreel-perspectives-api.onrender.com/positions/spread_by_tag.json?story_id=iran-war-drags-on-costs-mount-as-congress-fights-back&tag=anti-war&_shape=array"
[
{
"position_label": "Anti-War Opposition",
"voice": "Jimmy Dore",
"category": "commentator",
"quote": "Iran Captures Unmanned U.S. Submarine!",
"date": "2026-09-17",
"platform": "youtube",
"source_url": "https://www.youtube.com/watch?v=1tzYmkfbKdw",
"stance": "lean",
"stance_confidence": null
},
{
"position_label": "Anti-War Opposition",
"voice": "Kyle Kulinski",
"category": "commentator",
"quote": "‘IT’S GENOCIDE’: Tucker Carlson COMPLETELY SNAPS On Warmonger Trump & Bibi | The Kyle Kulinski Show",
"date": "2026-09-17",
"platform": "youtube",
"source_url": "https://www.youtube.com/watch?v=Qbli1cQlPUQ",
"stance": "strong",
"stance_confidence": null
}
]GET /positions/spread.json
Everyone on a story, grouped by claim.
| Parameter | Type | Meaning |
|---|---|---|
story_id | string | from stories_recent |
curl -H "Authorization: Bearer $KEY" \
"https://newsreel-perspectives-api.onrender.com/positions/spread.json?story_id=iran-war-drags-on-costs-mount-as-congress-fights-back&_shape=array"
[
{
"position_label": "Anti-War Opposition",
"voice": "Quincy Institute",
"category": "think-tank",
"party_group": null,
"quote": "The Houthis could tip the Iran war in Tehran's favor (by Paul R. Pillar). The conflict between the United Stat…",
"date": "2026-09-18",
"platform": "blog",
"source_url": "https://responsiblestatecraft.org/iran-war-houthis/"
},
{
"position_label": "Anti-War Opposition",
"voice": "Tom Nichols",
"category": "commentator",
"party_group": null,
"quote": "But he did have a military disaster in Iran",
"date": "2026-09-18",
"platform": "bluesky",
"source_url": "https://bsky.app/profile/radiofreetom.bsky.social/post/3mvrb5xsbnc2f"
}
]GET /positions/search.json
Plain-text search over summary, quote and voice name. The text is matched as a phrase, so hyphens and quotes are safe.
| Parameter | Type | Meaning |
|---|---|---|
q | string | plain text |
since | date | YYYY-MM-DD, or empty string |
topic | string | taxonomy slug, or empty string |
Pass an empty string for a parameter you are not using.
curl -H "Authorization: Bearer $KEY" \
"https://newsreel-perspectives-api.onrender.com/positions/search.json?q=Epstein&since=&topic=&_shape=array"
[
{
"voice_name": "Brian Tyler Cohen",
"party_group": null,
"topic": "epstein-scandal",
"stance": "lean",
"summary": "Critiques Fox News for not covering Epstein files like other networks",
"quote": "CNN: Epstein files ABC: Epstein files NBC: Epstein files Fox News:",
"date": "2025-12-20",
"platform": "x",
"source_url": "https://x.com/briantylercohen/status/2002305662080913731",
"story_id": null,
"position_label": null
},
{
"voice_name": "Robert Garcia",
"party_group": "Democratic",
"topic": "epstein-scandal",
"stance": "strong",
"summary": "Demands Epstein files release, Bondi testimony",
"quote": "Release the Epstein files! And Bondi must testify! #congressman #dems #epstein #pambondi",
"date": "2026-04-13",
"platform": "tiktok",
"source_url": "https://www.tiktok.com/@robertgarcia/video/7628321564437548301",
"story_id": null,
"position_label": null
}
]GET /positions/voice.json
One voice: category, lens, party, tags.
| Parameter | Type | Meaning |
|---|---|---|
voice_id | string | e.g. ted-cruz |
curl -H "Authorization: Bearer $KEY" \
"https://newsreel-perspectives-api.onrender.com/positions/voice.json?voice_id=ted-cruz&_shape=array"
[
{
"id": "ted-cruz",
"name": "Ted Cruz",
"category": "politician",
"approach": "argues",
"lens": "Texas Senator and Verdict podcast host who frames issues through constitutional originalism and combative cons…",
"party": "Republican Party",
"party_group": "Republican",
"wikidata_qid": "Q2036942",
"platforms": "[\"x\", \"youtube\"]",
"profile_url": "https://newsreel.co/perspectives/voice/ted-cruz",
"first_seen": "2026-07-29",
"last_seen": "2026-09-24",
"position_count": 304,
"tags": "conservative"
}
]GET /positions/voice_positions.json
Where one voice has stood, newest first.
| Parameter | Type | Meaning |
|---|---|---|
voice_id | string | |
topic | string | slug, or empty string |
curl -H "Authorization: Bearer $KEY" \
"https://newsreel-perspectives-api.onrender.com/positions/voice_positions.json?voice_id=ted-cruz&topic=&_shape=array"
[
{
"topic": "civil-rights-race",
"stance": "strong",
"summary": "Criticizes Nikole Hannah-Jones and leftist failures",
"quote": "Cruz on Nikole Hannah-Jones’s astonishing and sad admissions highlighting the failures of the Left.",
"date": "2026-09-24",
"platform": "youtube",
"source_url": "https://www.youtube.com/shorts/gwIEnpSEtJc",
"story_id": null,
"position_label": null,
"stance_confidence": null
},
{
"topic": "foreign-policy-diplomacy",
"stance": "strong",
"summary": "Claims China funds both sides",
"quote": "The Chinese are funding both.",
"date": "2026-09-24",
"platform": "x",
"source_url": "https://x.com/tedcruz/status/2103108500175925714",
"story_id": null,
"position_label": null,
"stance_confidence": null
}
]GET /positions/voices_by_group.json
The voices in a party or with a tag. Pass the other as an empty string.
| Parameter | Type | Meaning |
|---|---|---|
party_group | string | |
tag | string |
curl -H "Authorization: Bearer $KEY" \
"https://newsreel-perspectives-api.onrender.com/positions/voices_by_group.json?party_group=&tag=media+critic&_shape=array"
[
{
"id": "glenn-greenwald",
"name": "Glenn Greenwald",
"category": "journalist",
"party_group": null,
"lens": "Journalist who broke the Edward Snowden NSA surveillance story. Once a leading voice on the progressive left, …",
"position_count": 237
},
{
"id": "tim-pool",
"name": "Tim Pool",
"category": "commentator",
"party_group": null,
"lens": "Independent journalist turned right-populist commentator. Timcast reaches millions daily. Anti-establishment, …",
"position_count": 209
}
]GET /positions/glossary.json
What every tag, category and stance label means, and the rule about what is never tagged.
| Parameter | Type | Meaning |
|---|---|---|
| No parameters | ||
curl -H "Authorization: Bearer $KEY" \
"https://newsreel-perspectives-api.onrender.com/positions/glossary.json?_shape=array"
[
{
"kind": "category",
"key": "activist",
"definition": "advocacy-focused public figure",
"tier": null
},
{
"kind": "category",
"key": "commentator",
"definition": "opinion-focused media personality",
"tier": null
}
]GET /positions/positions.json
Any table, filtered by any column with Datasette operators.
| Parameter | Type | Meaning |
|---|---|---|
<column> | exact match | |
<column>__gte / __lte / __contains | comparisons | |
_size | int | rows per page, max 500 |
_next | token | pagination cursor from the previous response |
Tables: positions · voices · voice_tags · stories · story_positions · topics · glossary · meta.
curl -H "Authorization: Bearer $KEY" \
"https://newsreel-perspectives-api.onrender.com/positions/positions.json?_shape=array&topic=iran-conflict&date__gte=2026-09-20&_size=2"
[
{
"id": "5c6ad76ad3d269ba",
"voice_id": "robert-reich",
"voice_name": "Robert Reich",
"topic": "iran-conflict",
"topics": "[\"iran-conflict\", \"healthcare\"]",
"stance": "strong",
"summary": "Opposes Iran war spending, supports ACA subsidy extension",
"quote": "Cost of extending ACA subsidies: $30 billion/year Cost of Trump's Iran War: $43 billion in roughly seven month…",
"date": "2026-09-24",
"platform": "bluesky",
"source_url": "https://bsky.app/profile/rbreich.bsky.social/post/3mwa2l5vjtz23",
"story_id": "iran-war-drags-on-costs-mount-as-congress-fights-back",
"position_label": "This war is a costly failure Trump owns",
"stance_p": null,
"stance_confidence": null,
"topic_p": null,
"topic_confidence": null,
"relevance_confidence": null,
"label_model": null,
"attribution": "Robert Reich, bluesky, 2026-09-24, via Newsreel Perspectives"
},
{
"id": "ca2c24ce10e12107",
"voice_id": "randi-weingarten",
"voice_name": "Randi Weingarten",
"topic": "iran-conflict",
"topics": "[\"iran-conflict\"]",
"stance": "strong",
"summary": "Opposes Trump's Iran rhetoric as incitement",
"quote": "Trump’s threat “annihilate” Iran is an incitement to mass slaughter. It invokes the worst crimes in history & …",
"date": "2026-09-24",
"platform": "bluesky",
"source_url": "https://bsky.app/profile/rweingarten.bsky.social/post/3mwafjnplhs2n",
"story_id": "iran-war-drags-on-costs-mount-as-congress-fights-back",
"position_label": "Trump's annihilation threat is a war crime",
"stance_p": null,
"stance_confidence": null,
"topic_p": null,
"topic_confidence": null,
"relevance_confidence": null,
"label_model": null,
"attribution": "Randi Weingarten, bluesky, 2026-09-24, via Newsreel Perspectives"
}
]GET /positions.json?sql=…
Read-only SQL over the whole file. Anything the tables can answer.
| Parameter | Type | Meaning |
|---|---|---|
sql | string | a SELECT; writes are refused |
_shape | array |
curl -H "Authorization: Bearer $KEY" \
"https://newsreel-perspectives-api.onrender.com/positions.json?sql=select%20party_group%2C%20count%28%2A%29%20as%20voices%20from%20voices%20where%20category%3D%27politician%27%20group%20by%201&_shape=array"
[
{
"party_group": "Democratic",
"voices": 29
},
{
"party_group": "Independent",
"voices": 2
}
]Errors
| Status | When | Body |
|---|---|---|
| 403 | no key, or a key we don't know | HTML "Forbidden" page (Datasette's default) |
| 402 | a known key with no billing account | {"error": {"code": "payment_required", "message": "This key is not attached to a billing account. Email jack@newsreel.co… |
| 429 | over 300/min on this key | {"error": {"code": "rate_limited", …}} with Retry-After: 60 |
| 400 | bad parameter or SQL | Datasette error JSON |
Position row
| Field | Type | Meaning |
|---|---|---|
id | string | sha1(voice_id|source_url)[:16], stable across rebuilds |
voice_id, voice_name | string | |
topic, topics | slug, JSON list | from the 40-topic taxonomy |
stance | strong · lean | neutral posts are not positions and are not in the file |
summary | string ≤120 | model-written from the linked post |
quote | string ≤25 words | the author's own words; ellipsis marks truncation |
date, platform, source_url | date only, no time of day; the link is the record | |
story_id, position_label | string | null | set when the post was published under a story cluster |
stance_p, stance_confidence, topic_p, topic_confidence, relevance_confidence | 0–1 | null | calibrated; null before 19 Sep 2026 |
label_model | string | null | e.g. jev-1.13.0 |
attribution | string | "Name, platform, date, via Newsreel Perspectives" |
Voice row
| Field | Meaning |
|---|---|
id, name | |
category | one of 11: commentator, politician, journalist, creator, think-tank, economist, activist, medical, legal, satirist, entrepreneur |
lens | one line on the angle they argue from |
party, party_group | Wikidata's label, and Democratic / Republican / Independent; politicians only |
wikidata_qid | the entity key |
platforms, first_seen, last_seen, position_count | |
| tags | in voice_tags; from a fixed 16-term glossary, or they do not exist. Never race, religion, gender or demographic identity |
Never served: post text, timestamps with time of day, handles, feeds, photos, follower counts, email. The file the API reads is built by naming its columns; a test that builds from a fixture containing all of those and asserts none survive runs in every deploy.
Retention
No query is logged, stored or cached. Requests are answered from a read-only SQLite file in memory; no database, model or third party is in the request path; the HTTP access log is off. The only retained numbers are per-key call counts for billing. Removal requests and corrections: jack@newsreel.co.