Pressroom API

A deterministic, fully-cited news-intelligence API. Query events, entities, quotes, certified facts, and analysis across a corpus of 200,000+ articles — every answer traceable to its source.

68 paths · 72 operations204,000+ articles177 sourcesNo LLM in the answer path — reproducible
BASE URL
https://api.pressroom.news
QUICK START
export PRESSROOM_KEY="YOUR_API_KEY"

curl -H "X-API-Key: $PRESSROOM_KEY" \
  "https://api.pressroom.news/v1/search?q=election&page_size=3"

Quick start

Every request is authenticated with your API key in the X-API-Key header. Base URL: https://api.pressroom.news

Set your key once and make your first call — the panels on the right show the base URL and the request.

🔑 Don't have a key yet? Keys are issued by request during the private beta — see Request access below.
BASE URL
https://api.pressroom.news
GET/v1/search
export PRESSROOM_KEY="YOUR_API_KEY"

curl -H "X-API-Key: $PRESSROOM_KEY" \
  "https://api.pressroom.news/v1/search?q=election&page_size=3"

Authentication

Pass your key on every call, either header form:

Unauthenticated requests to data endpoints return 401. Keep your key secret; it is tied to your quota and tier. Two endpoints are open (no key): /v1/health and /v1/version.

AUTHENTICATION HEADERS
X-API-Key: YOUR_API_KEY
# — or —
Authorization: Bearer YOUR_API_KEY

Tiers & rate limits

Each key belongs to a tier with a monthly call quota and a per-minute burst limit. Exceeding either returns 429 with a Retry-After hint.

TierMonthly quotaBurst
free1,00030/min
academic50,00060/min
starter100,000120/min
growth1,000,000300/min
pro5,000,000600/min
enterpriseunlimited1200/min

What you get — features & roadmap

Deterministic, provenance-cited news intelligence over a 200K+ article corpus (India / Bangladesh / UK / Nepal focus, 28 sources, English + Hindi + Bengali + Nepali originals). Every number links back to the articles and text spans it came from. No generative model sits between the corpus and your response.

FeatureWhat it doesStatus
Entity graphCanonical people / organisations / locations — profiles, search, disambiguation, coverage timeseries, co-mention relationships, identity-change historyLive
Quotes as objects385K+ attributed quote spans, searchable, with speaker resolution (people, and institutional speakers — "the RBI said"), full provenance chainLive
Canonical eventsPrecision-gated event registry with per-event narrative timelines and coverage breakdownsLive
Certified depth cardsTyped, certified fact cards (casualty counts, deadlines, legal status…) that pass a published safety gate before they existLive
Intelligence analyses11 POST analyses: narrative momentum, story lifecycle, signal-vs-noise, blind spots, epistemic confidence, story attribution, quote provenance, crisis trajectory…Live
Cited briefs & QADeterministic analyst briefs and retrieval QA — every sentence cited, no LLM hallucination surfaceLive
Geo layerHeatmaps, radius queries, sentiment geography over geocoded locationsLive — coverage expanding
Real-time streamSSE mention stream from a cursorLive
WorkspaceSaved searches (watchlists) and per-key article tagsLive
Embeddable widgetsDrop-in intelligence widgets (quotes, entity cards, narratives, coverage maps, analyses) rendered on your site via domain-locked publishable keys — each fetch meters as one API call on your planLive
Entity enrichment at depthWikidata-grounded org typing, industry, country and multilingual aliases across the long tailRolling out
Self-serve keys & billingSign up, get a key, pay online — today keys are issued by request (below)Planned
Continuous corpus syncCloud replica updated continuously from the extraction pipelinePlanned

What we deliberately do not offer: article body text. Pressroom is not a content-redistribution service — endpoints return metadata, entities, quotes, and analyses, each linking to the publisher's canonical URL. Read the journalism where it was published.

Response format

Every response is JSON. List endpoints wrap results in data with a meta block for pagination; single-object endpoints return data directly.

Paginate with page and page_size query parameters.

RESPONSE ENVELOPE
{
  "data": [ /* … results … */ ],
  "meta": { "total": 9690, "page": 1, "page_size": 3 }
}

Errors

The API uses conventional HTTP status codes and returns every error in one uniform envelope: a machine-readable code, a human-readable message, and a structured detail object naming the offending field or id. Nothing about your request internals is ever echoed back — no SQL, no stack traces, by design.

Every response — success or error — carries an X-Request-Id header and an X-Response-Time-ms header. Include the request_id when contacting support; it pins your exact request in our logs.

CodeHTTPMeaning
bad_request400Invalid parameter — detail names the field (e.g. page_size out of range).
unauthorized401Missing, invalid, or revoked API key.
forbidden403Key class or tier not allowed here — a publishable pk_ key on a non-widget route, an unauthorised embedding domain, or a tier-gated endpoint.
not_found404Unknown id or resource — detail carries the id you sent.
rate_limited429Over a limit; comes with a Retry-After header. detail.scope is per_minute (burst) or monthly_quota (plan quota; resets at month rollover, UTC).
internal_error500Something failed on our side. The message is deliberately generic — nothing about your request is echoed back.
widget_unavailable501The widget id exists but has no sellable data substrate yet — detail.reason explains.

Handling 429s. Honor the Retry-After header. When detail.scope is per_minute you hit the burst limit — back off for the given seconds and retry. When it is monthly_quota your plan's included calls are exhausted for the period; retrying sooner than the month rollover (or a tier upgrade) will not help.

Metering headers. Responses also carry X-Metering-State: normally FINALIZED. The rare RESERVED_RECONCILIATION_REQUIRED means the call was counted but its final status/latency record is pending reconciliation — an operational signal on our side, nothing you need to act on.

ERROR ENVELOPE · 404
{
  "error": {
    "code": "not_found",
    "message": "event not found.",
    "detail": { "id": 999999 },
    "request_id": "3f61c2a97d0b4e18"
  }
}
ERROR ENVELOPE · 429
{
  "error": {
    "code": "rate_limited",
    "message": "Rate limit exceeded.",
    "detail": { "retry_after": 42, "scope": "per_minute" },
    "request_id": "9b04d7c1e52a8f36"
  }
}
HEADERS ON EVERY RESPONSE
X-Request-Id: 3f61c2a97d0b4e18
X-Response-Time-ms: 104
X-Metering-State: FINALIZED
Retry-After: 42        # 429 responses only

Search & discovery

Canonical event registry GET /v1/events

Paginated registry of de-duplicated real-world events, each with domain, type, and coverage counts.

GET/v1/events
curl -H "X-API-Key: $PRESSROOM_KEY" \
  "https://api.pressroom.news/v1/events?page_size=3"
RESPONSE · 200
{
  "data": [
    {
      "id": 47133,
      "label": "World Cup",
      "wikidata_id": null,
      "event_year": null,
      "event_domain": "sport",
      "event_type": "sports",
      "article_count": 14783,
      "has_timeline": false,
      "beat_count": 0
    },
    {
      "id": 47132,
      "label": "FIFA World Cup 2026",
      "wikidata_id": "Q5020214",
      "event_year": "2026",
      "event_domain": "sport",
      "event_type": "sports",
      "article_count": 2488,
      "has_timeline": false,
      "beat_count": 0
    },
    {
      "id": 47187,
      "label": "World Cups",
      "wikidata_id": null,
      "event_year": null,
      "event_domain": "sport",
      "event_type": "sports",
      "article_count": 1110,
      "has_timeline": false,
      "beat_count": 0
    }
  ],
  "meta": {
    "total": 9690,
  … (4 more lines)

Event profile GET /v1/events/47133

A single canonical event with its metadata and linkage.

GET/v1/events/47133
curl -H "X-API-Key: $PRESSROOM_KEY" \
  "https://api.pressroom.news/v1/events/47133"
RESPONSE · 200
{
  "data": {
    "id": 47133,
    "kind": "event",
    "name": "World Cup",
    "event_domain": "sport",
    "event_type": "sports",
    "event_year": null,
    "event_date": {
      "start": null,
      "end": null,
      "precision": null
    },
    "identity": {
      "resolved": false,
      "wikidata_id": null
    },
    "description": null,
    "hierarchy": {
      "role": "franchise",
      "parent": null,
      "children": [
        {
          "id": 52491,
          "name": "World Cup 2035",
          "event_year": "2035",
          "event_domain": "sport"
        },
        {
          "id": 49379,
          "name": "2034 World Cup",
          "event_year": "2034",
          "event_domain": "sport"
        },
        {
          "id": 47211,
          "name": "2030 World Cup",
          "event_year": "2030",
  … (307 more lines)

Curated topics GET /v1/topics

The curated topic catalog.

GET/v1/topics
curl -H "X-API-Key: $PRESSROOM_KEY" \
  "https://api.pressroom.news/v1/topics"
RESPONSE · 200
{
  "data": [
    {
      "slug": "artificial-intelligence",
      "label": "Artificial Intelligence",
      "source": "curated",
      "article_count": 668,
      "keywords": [
        "artificial intelligence",
        "openai",
        "chatgpt",
        "machine learning",
        "large language model",
        "generative ai",
        "neural network"
      ],
      "aliases": [
        "ai",
        "genai",
        "generative ai",
        "machine learning"
      ],
      "sections": [],
      "updated_at": "2026-07-26T09:42:49Z"
    },
    {
      "slug": "climate-change",
      "label": "Climate & Environment",
      "source": "curated",
      "article_count": 2239,
      "keywords": [
        "climate",
        "emission",
        "global warming",
        "renewable",
        "carbon",
        "net zero",
        "monsoon",
  … (249 more lines)

Topic profile GET /v1/topics/artificial-intelligence

A single topic with its profile.

GET/v1/topics/artificial-intelligence
curl -H "X-API-Key: $PRESSROOM_KEY" \
  "https://api.pressroom.news/v1/topics/artificial-intelligence"
RESPONSE · 200
{
  "data": {
    "slug": "artificial-intelligence",
    "label": "Artificial Intelligence",
    "source": "curated",
    "keywords": [
      "artificial intelligence",
      "openai",
      "chatgpt",
      "machine learning",
      "large language model",
      "generative ai",
      "neural network"
    ],
    "aliases": [
      "ai",
      "genai",
      "generative ai",
      "machine learning"
    ],
    "sections": [],
    "stats": {
      "article_count": 668,
      "first_seen": "2025-07-06T12:11:14Z",
      "last_seen": "2026-07-26T03:53:15Z"
    },
    "top_sources": [
      {
        "source": "Mint",
        "articles": 324
      },
      {
        "source": "Reuters",
        "articles": 83
      },
      {
        "source": "Hindustan Times",
        "articles": 46
  … (32 more lines)

Entities — people, organisations, locations

Entity profile GET /v1/people/174

Canonical profile for a person, organisation, or location (the {kind} is people / organisations / locations).

GET/v1/people/174
curl -H "X-API-Key: $PRESSROOM_KEY" \
  "https://api.pressroom.news/v1/people/174"
RESPONSE · 200
{
  "data": {
    "id": 174,
    "kind": "people",
    "name": "Narendra Modi",
    "identity": {
      "resolved": true,
      "wikidata_id": "Q1058",
      "lei_code": null,
      "status": "resolved"
    },
    "attributes": {
      "current_title": null,
      "current_employer": null,
      "nationality": null,
      "date_of_birth": null,
      "wikipedia_url": "https://en.wikipedia.org/wiki/Narendra_Modi",
      "twitter_handle": "narendramodi",
      "twitter_url": "https://twitter.com/narendramodi",
      "linkedin_url": "narendramodi",
      "official_bio_url": "https://www.narendramodi.in",
      "image_url": "https://upload.wikimedia.org/wikipedia/commons/thumb/5/5f/The_official_portrait_of_Shri_Narendra_Modi%2C_the_Prime_Minister_of_the_Republic_of_India.jpg/330px-The_official_portrait_of_Shri_Narendra_Modi%2C_the_Prime_Minister_of_the_Republic_of_India.jpg",
      "bio_text": "Narendra Damodardas Modi is an Indian politician who has served as the prime minister of India since 26 May 2014. Modi was the chief minister of Gujarat from 2001 to 2014 and is the Member of Parliament (MP) for Varanasi. He is a member of the Bharatiya Janata Party (BJP) and of the Rashtriya Swayamsevak Sangh (RSS), a right-wing Hindutva paramilitary volunteer organisation. He is India's third-longest-serving prime minister, and the longest-serving prime minister outside the Indian National Congress.",
      "bio_source": "wikipedia"
    },
    "aliases": [
      "Modi",
      "Modi ji",
      "Narender Modi",
      "Narendra Damodardas Modi",
      "Narendra Modi",
      "Narendra) Modi",
      "Narendra] Modi",
      "Shri Modi",
      "Shri Narendra Modi",
      "Shri Narendra Modi ji"
    ],
    "stats": {
  … (9 more lines)

Entity relationships GET /v1/people/174/relationships

Top co-mentioned people, organisations, and locations for an entity.

GET/v1/people/174/relationships
curl -H "X-API-Key: $PRESSROOM_KEY" \
  "https://api.pressroom.news/v1/people/174/relationships"
RESPONSE · 200
{
  "data": {
    "entity_id": 174,
    "kind": "people",
    "source_article_count": 5501,
    "people": [
      {
        "id": 11084,
        "name": "Modi",
        "wikidata_id": null,
        "co_count": 702,
        "first_seen": "2025-11-24T18:30:00Z",
        "last_seen": "2026-07-26T10:40:47Z",
        "salience": 0.1276
      },
      {
        "id": 1,
        "name": "Donald Trump",
        "wikidata_id": "Q22686",
        "co_count": 553,
        "first_seen": "2025-09-10T18:15:00Z",
        "last_seen": "2026-07-25T12:00:00Z",
        "salience": 0.1005
      },
      {
        "id": 622,
        "name": "Amit Shah",
        "wikidata_id": "Q4746875",
        "co_count": 449,
        "first_seen": "2023-06-02T18:30:00Z",
        "last_seen": "2026-07-26T10:40:47Z",
        "salience": 0.0816
      },
      {
        "id": 1268,
        "name": "Rahul Gandhi",
        "wikidata_id": "Q10218",
        "co_count": 416,
  … (520 more lines)

Quotes by a person GET /v1/people/174/quotes

Quotes attributed to a person, with provenance.

GET/v1/people/174/quotes
curl -H "X-API-Key: $PRESSROOM_KEY" \
  "https://api.pressroom.news/v1/people/174/quotes?page_size=2"
RESPONSE · 200
{
  "data": [
    {
      "quote_id": 377063,
      "text": "Smt. Sonia Gandhi’s mother, Mrs. Paola Maino passed away at her home in Italy on Saturday the 27th August 2022. The funeral took place yesterday,",
      "verb": "tweeted",
      "type": "direct",
      "confidence": 0.85,
      "provenance": {
        "article_id": 174102,
        "headline": "Congress chief Sonia Gandhi's mother passes away",
        "source_id": "the-hindu",
        "source_url": "https://thehindu.com/news/national/congress-chief-sonia-gandhis-mother-passes-away/article65833715.ece",
        "extraction_method": "trafilatura",
        "extracted_at": "2026-07-15T20:02:30Z",
        "date_published_utc": null
      }
    },
    {
      "quote_id": 377062,
      "text": "May her soul rest in peace. In this hour of grief, my thoughts are with the entire family.",
      "verb": "tweeted",
      "type": "direct",
      "confidence": 0.85,
      "provenance": {
        "article_id": 174102,
        "headline": "Congress chief Sonia Gandhi's mother passes away",
        "source_id": "the-hindu",
        "source_url": "https://thehindu.com/news/national/congress-chief-sonia-gandhis-mother-passes-away/article65833715.ece",
        "extraction_method": "trafilatura",
        "extracted_at": "2026-07-15T20:02:30Z",
        "date_published_utc": null
      }
    }
  ],
  "meta": {
    "total": 1758,
    "page": 1,
  … (3 more lines)

Certified depth cards

Depth cards GET /v1/depth-cards

Certified, provenance-cited story-state facts (e.g. casualty / arrest counts) — every card is gated and traceable to its source span.

GET/v1/depth-cards
curl -H "X-API-Key: $PRESSROOM_KEY" \
  "https://api.pressroom.news/v1/depth-cards?page_size=3"
RESPONSE · 200
{
  "data": [
    {
      "card_id": "cc_arrest_count_ca89a2c654546c73",
      "contract_version": "depth_card_v1",
      "disposition": "PUBLISHED",
      "object_class": "ARREST_COUNT",
      "base_class": "ARREST_COUNT",
      "claim": "people arrested = 11",
      "claimed_value": 11.0,
      "unit": "people",
      "story_unit_id": null,
      "minted_at": "2026-07-25T14:38:07.732862Z",
      "provenance": {
        "article_ids": [
          26091
        ],
        "primary_article_id": 26091,
        "source_name": "myrepublica",
        "publication_date": null,
        "license_path": "P2_PERSON_HEAD",
        "span": "“Chinese security personnel recently arrested 11 Nepali traders.",
        "offsets": {
          "start": 949,
          "end": 951
        },
        "method": "typed_count_factory -> publish_safety_gate -> held_queue(rubric_v3, qwen3:14b local) -> reader_canary",
        "gate": {
          "status": "AUTO_PUBLISH",
          "families": [],
          "reason": null
        },
        "frozen_gate_sha256": "ede56aeb1bc2709caf77596e6489cd41a73fe6140e521ba5afddb7bbf2acbf24",
        "adjudicator": "none",
        "adjudicator_rationale": "publish_safety_gate AUTO_PUBLISH (deterministic; no model call)",
        "verdict_cache_key": null,
        "cert_ref": null,
        "content_hash": "ad2527eb0a086145acda10df8add427dc7a5358c3091fd6bede08e968fc6a923"
  … (85 more lines)

Single depth card GET /v1/depth-cards/cc_arrest_count_ca89a2c654546c73

One certified card by id, with full provenance.

GET/v1/depth-cards/cc_arrest_count_ca89a2c654546c73
curl -H "X-API-Key: $PRESSROOM_KEY" \
  "https://api.pressroom.news/v1/depth-cards/cc_arrest_count_ca89a2c654546c73"
RESPONSE · 200
{
  "data": {
    "card_id": "cc_arrest_count_ca89a2c654546c73",
    "contract_version": "depth_card_v1",
    "disposition": "PUBLISHED",
    "object_class": "ARREST_COUNT",
    "base_class": "ARREST_COUNT",
    "claim": "people arrested = 11",
    "claimed_value": 11.0,
    "unit": "people",
    "story_unit_id": null,
    "minted_at": "2026-07-25T14:38:07.732862Z",
    "provenance": {
      "article_ids": [
        26091
      ],
      "primary_article_id": 26091,
      "source_name": "myrepublica",
      "publication_date": null,
      "license_path": "P2_PERSON_HEAD",
      "span": "“Chinese security personnel recently arrested 11 Nepali traders.",
      "offsets": {
        "start": 949,
        "end": 951
      },
      "method": "typed_count_factory -> publish_safety_gate -> held_queue(rubric_v3, qwen3:14b local) -> reader_canary",
      "gate": {
        "status": "AUTO_PUBLISH",
        "families": [],
        "reason": null
      },
      "frozen_gate_sha256": "ede56aeb1bc2709caf77596e6489cd41a73fe6140e521ba5afddb7bbf2acbf24",
      "adjudicator": "none",
      "adjudicator_rationale": "publish_safety_gate AUTO_PUBLISH (deterministic; no model call)",
      "verdict_cache_key": null,
      "cert_ref": null,
      "content_hash": "ad2527eb0a086145acda10df8add427dc7a5358c3091fd6bede08e968fc6a923"
    }
  … (2 more lines)

Analysis & briefs (POST)

Narrative momentum POST /v1/analyse/narrative-momentum

Coverage momentum for a query — volume, diversity, geographic and sentiment trend.

POST/v1/analyse/narrative-momentum
curl -X POST -H "X-API-Key: $PRESSROOM_KEY" \
  -H "Content-Type: application/json" \
  -d '{"q": "election"}' \
  "https://api.pressroom.news/v1/analyse/narrative-momentum"
RESPONSE · 200
{
  "data": {
    "subject": "q:election",
    "window": {
      "from": "2026-04-27",
      "to": "2026-07-26",
      "midpoint": "2026-06-11"
    },
    "total_articles": 1093,
    "recent_half": {
      "volume": 472,
      "sources": 75,
      "countries": 5,
      "sentiment": -0.0067
    },
    "prior_half": {
      "volume": 621,
      "sources": 24,
      "countries": 5,
      "sentiment": 0.0707
    },
    "components": {
      "volume_rate_change": -0.2399,
      "source_diversity_delta": 0.2881,
      "geographic_spread_delta": 0.0,
      "sentiment_delta": -0.0774
    },
    "momentum_score": -0.0701,
    "method": "deterministic; weights v=0.5 div=0.2 geo=0.2 sent=0.1"
  }
}

Cited retrieval QA POST /v1/chat

Deterministic question-answering over the corpus with citations — no LLM, fully reproducible.

POST/v1/chat
curl -X POST -H "X-API-Key: $PRESSROOM_KEY" \
  -H "Content-Type: application/json" \
  -d '{"question": "What has been reported about elections?"}' \
  "https://api.pressroom.news/v1/chat"
RESPONSE · 200
{
  "data": {
    "question": "What has been reported about elections?",
    "answer": "8 relevant article(s) from 5 source(s) address this. Leading coverage: \"As heat wave sweeps across several states in the US, White House has reportedly deleted thousands of web pages about...\" (the-times-of-india, 2026-07-05). See cited passages below.",
    "matched_terms": [
      "has",
      "reported",
      "elections"
    ],
    "passages": [
      {
        "headline": "As heat wave sweeps across several states in the US, White House has reportedly deleted thousands of web pages about...",
        "dek": null,
        "provenance": {
          "article_id": 186888,
          "source_id": "the-times-of-india",
          "source_url": "https://timesofindia.indiatimes.com/technology/tech-news/as-heat-wave-sweeps-across-several-states-in-the-us-white-house-has-reportedly-deleted-thousands-of-web-pages-about-/articleshow/132196710.cms",
          "date_published_utc": "2026-07-05T13:43:00Z"
        }
      },
      {
        "headline": "Paul Hogan has reportedly called Pauline Hanson a ‘pelican’. Please explain?",
        "dek": null,
        "provenance": {
          "article_id": 132390,
          "source_id": "the-guardian",
          "source_url": "https://theguardian.com/australia-news/2026/jun/28/paul-hogan-pauline-hanson-pelican-insult-australian-slang-please-explain",
          "date_published_utc": "2026-06-28T07:41:24Z"
        }
      },
      {
        "headline": "Republicans reportedly weigh stopgap funding plan to avert Government shutdown before midterm elections",
        "dek": "Senator Rick Scott's proposal comes ahead of a planned meeting between President Donald Trump and Senate Republicans, where lawmakers are expected to discuss priorities for the remainder of the year.",
        "provenance": {
          "article_id": 108287,
          "source_id": "mint",
          "source_url": "https://livemint.com/news/us-news/republicans-reportedly-weigh-stopgap-funding-plan-to-avert-government-shutdown-before-midterm-elections-11782317710245.html",
          "date_published_utc": "2026-06-24T16:35:08Z"
  … (91 more lines)

Analyst brief POST /v1/briefs/generate

A deterministic, cited analyst brief for a query or entity.

POST/v1/briefs/generate
curl -X POST -H "X-API-Key: $PRESSROOM_KEY" \
  -H "Content-Type: application/json" \
  -d '{"q": "election"}' \
  "https://api.pressroom.news/v1/briefs/generate"
RESPONSE · 200
{
  "data": {
    "subject": "q:election",
    "window": {
      "from": "2026-04-27",
      "to": "2026-07-26"
    },
    "total_articles": 1093,
    "summary": "1093 articles from 8 sources cover q:election (2026-04-27–2026-07-26). Most-associated people: Donald Trump, Andy Burnham, Keir Starmer; organisations: BJP, Reuters, Labour; locations: United States, India, UK. 5 attributed quote(s) surfaced. Every claim is cited below.",
    "top_entities": {
      "people": [
        {
          "name": "Donald Trump",
          "co_count": 183
        },
        {
          "name": "Andy Burnham",
          "co_count": 76
        },
        {
          "name": "Keir Starmer",
          "co_count": 62
        },
        {
          "name": "Nigel Farage",
          "co_count": 53
        },
        {
          "name": "Joe Biden",
          "co_count": 42
        }
      ],
      "organisations": [
        {
          "name": "BJP",
          "co_count": 211
        },
        {
  … (301 more lines)

Embeddable widgets

Put live Pressroom intelligence on your own pages — attributed quotes, entity cards, active narratives, coverage maps, and analyses — with two lines of HTML. Widgets are a feature of your API plan, not a separate product: every widget data fetch meters as one API request against your monthly quota.

Publishable keys. Embeds use a pk_ publishable key minted from your account. It is safe to expose in page source: it only works on domains you allow-list, only reaches widget-data endpoints, and spends only your own quota. Your secret pr_ key never appears in a browser. Rotate or revoke a pk_ key at any time.

Honest states. A widget over quota or on an unauthorised domain renders a labelled error state — it never silently shows stale or fabricated data. Every widget carries a "Powered by Pressroom" attribution.

Catalogue. GET /v1/widgets lists the available widget ids: narrative_pulse, active_narrative_list, quote_provenance, daily_executive_brief, source_diversity, entity_intelligence_card, regional_news_map, narrative_conflict, coverage_trajectory, narrative_momentum. Widgets without a production-grade data substrate are not offered.

🔑 Publishable keys are issued with your API key during the private beta — include the domains you'll embed on in your access request.
EMBED — TWO LINES
<script async src="https://api.pressroom.news/widget.js"></script>
<div class="pressroom-widget"
     data-widget="quote_provenance"
     data-subject="Reserve Bank of India"
     data-key="pk_YOUR_PUBLISHABLE_KEY"></div>
GET/v1/widgets/{id}/data
curl -H "X-API-Key: $PRESSROOM_KEY" \
  "https://api.pressroom.news/v1/widgets/entity_intelligence_card/data?subject=Adani"
RESPONSE (TRIMMED)
{
  "data": { "profile": { "name": "Gautam Adani", "…": "…" } },
  "meta": {
    "widget": "entity_intelligence_card",
    "attribution_required": true,
    "window_days": 30
  }
}

Widget catalogue GET /v1/widgets

Lists the widgets available to embed, with each id's display name, whether it needs a subject, and its data URL. Accepts secret or publishable keys.

Widget idNamesubject
narrative_pulseNarrative Pulseoptional
active_narrative_listActive Narrativesoptional
quote_provenanceQuote Provenancerequired
daily_executive_briefDaily Executive Briefrequired
source_diversitySource Diversityoptional
entity_intelligence_cardEntity Intelligence Cardrequired
regional_news_mapCoverage Mapoptional
narrative_conflictNarrative Conflictrequired
coverage_trajectoryCoverage Trajectoryrequired
narrative_momentumNarrative Momentumrequired

Widget ids from our internal gallery that lack a production-grade public data substrate are not listed; requesting one returns 501 widget_unavailable with the reason — never fabricated data.

GET/v1/widgets
curl -H "X-API-Key: $PRESSROOM_KEY" \
  "https://api.pressroom.news/v1/widgets"
RESPONSE · 200 (TRIMMED)
{
  "data": [
    {
      "id": "narrative_pulse",
      "name": "Narrative Pulse",
      "needs_subject": false,
      "data_url": "/v1/widgets/narrative_pulse/data"
    },
    {
      "id": "quote_provenance",
      "name": "Quote Provenance",
      "needs_subject": true,
      "data_url": "/v1/widgets/quote_provenance/data"
    },
    { "…": "… 8 more widgets …" }
  ],
  "meta": {
    "total": 10,
    "embed": {
      "loader": "/widget.js",
      "iframe": "/widgets/embed"
    },
    "billing": "each data fetch meters as one API request on the key's account"
  }
}

Widget data (metered) GET /v1/widgets/{id}/data

The data payload behind a widget — the same payload the iframe renders, usable directly from your own code.

Parameters. subject — the entity or query the widget is about; required for subject-scoped widgets (see the catalogue). days — the coverage window in days; default 30, max 365.

Auth. Pass an X-API-Key header (publishable pk_ or secret key), or — for pk_ keys, which are public by design — a ?key= query parameter (that is how the embed iframe authenticates its plain GET navigation).

Metering. Every fetch meters as one API request on the owning account. Payloads are cached server-side for up to 5 minutes to protect the database from hot embeds; cached fetches still meter — the cache protects the corpus, not the bill.

Attribution. When meta.attribution_required is true, the embed must display the "Powered by Pressroom" badge.

GET/v1/widgets/{id}/data
curl -H "X-API-Key: $PRESSROOM_KEY" \
  "https://api.pressroom.news/v1/widgets/quote_provenance/data?subject=India"
RESPONSE · 200 (TRIMMED · REAL CORPUS DATA)
{
  "data": {
    "quotes": [
      {
        "quote_id": 399748,
        "text": "India has exported its food culture to the world, but not its alcohol. We are still drinking a colonial legacy. There will be an Indian alcohol story, and we are working towards it,",
        "verb": "said",
        "type": "direct",
        "confidence": 0.82,
        "speaker": {
          "kind": "person",
          "person_id": 293146,
          "name": "Sameer Mahandru",
          "speaker_text": "Mahandru",
          "resolved": true
        },
        "provenance": {
          "article_id": 206027,
          "headline": "UK FTA to intensify competition in alcobev market: IndoBevs",
          "source_id": "financial-express",
          "source_url": "https://financialexpress.com/policy/economy/uk-fta-to-intensify-competition-in-alcobev-market-indobevs/4303156",
          "extraction_method": "ld_json",
          "date_published_utc": "2026-07-26T18:13:30Z"
        }
      },
      { "…": "9 more" }
    ],
    "total": 11785
  },
  "meta": {
    "widget": "quote_provenance",
    "name": "Quote Provenance",
    "subject": "India",
    "window_days": 30,
    "attribution_required": true
  }
}

Embed attributes

Attributes on the <div class="pressroom-widget"> loader element.

AttributeMeaning
data-widgetWidget id from the catalogue. Required.
data-subjectEntity or query the widget is about (required for subject-scoped widgets).
data-keyYour publishable pk_ key. Required.
data-daysCoverage window in days (default 30).
data-themedark | light.
data-widthIframe width (default 100%).
data-heightPins a fixed height; otherwise the iframe auto-grows.
data-titleAccessible iframe title.

Embed error states

Each renders as a labelled state inside the iframe — never silent emptiness, never fake data.

HTTPState shown
401The widget's API key is invalid or revoked.
403Key not authorised for this domain — add the site to the key's allowed domains.
429The account's API quota is exhausted for the current period.
501Widget not available via the public API (reason shown).

Live examples

These are real embeds — the exact snippets shown, rendering live corpus data on this page right now. They run on a demo publishable key that is domain-locked to pressroom.news: copy the snippet, and it will render an honest "domain not authorised" state on your site until you swap in your own pk_ key.

Each render below made one metered API call (served from the 5-minute cache when hot).

EVERY EXAMPLE STARTS WITH
<script async src="https://api.pressroom.news/widget.js"></script>

Entity intelligence card

A person's resolved identity, links and coverage stats.

SNIPPET
<div class="pressroom-widget"
     data-widget="entity_intelligence_card"
     data-subject="Narendra Modi"
     data-key="pk_YOUR_KEY"></div>

Quote provenance

Attributed quotes about a subject, each traceable to its article.

SNIPPET
<div class="pressroom-widget"
     data-widget="quote_provenance"
     data-subject="Reserve Bank of India"
     data-key="pk_YOUR_KEY"></div>

Narrative pulse

The most active corroborated story units right now (no subject needed).

SNIPPET
<div class="pressroom-widget"
     data-widget="narrative_pulse"
     data-key="pk_YOUR_KEY"></div>

Coverage map

Where the news is happening — top mapped coverage cells, last 30 days.

SNIPPET
<div class="pressroom-widget"
     data-widget="regional_news_map"
     data-key="pk_YOUR_KEY"></div>

Source diversity

The registry's spread across countries and languages.

SNIPPET
<div class="pressroom-widget"
     data-widget="source_diversity"
     data-key="pk_YOUR_KEY"></div>

Narrative momentum

Is coverage of a subject accelerating or fading? Volume, source spread and tone, half-window over half-window.

SNIPPET
<div class="pressroom-widget"
     data-widget="narrative_momentum"
     data-subject="Adani"
     data-key="pk_YOUR_KEY"></div>

Account & meta

Account usage GET /v1/account/usage

Your key's metering and quota state.

GET/v1/account/usage
curl -H "X-API-Key: $PRESSROOM_KEY" \
  "https://api.pressroom.news/v1/account/usage"
RESPONSE · 200
{
  "data": {
    "tier": "enterprise",
    "monthly_quota": -1,
    "monthly_used": 6,
    "usage_period": "2026-07",
    "lifetime_calls": 7,
    "lifetime_cost_units": 7,
    "recent": [
      {
        "ts": "2026-07-26 13:51:42.693016+00",
        "method": "GET",
        "endpoint": "/v1/account/usage",
        "status": null,
        "latency_ms": null
      },
      {
        "ts": "2026-07-26 13:45:34.45839+00",
        "method": "GET",
        "endpoint": "/v1/depth-cards",
        "status": 200,
        "latency_ms": 100
      },
      {
        "ts": "2026-07-26 13:45:33.81118+00",
        "method": "GET",
        "endpoint": "/v1/events",
        "status": 200,
        "latency_ms": 153
      },
      {
        "ts": "2026-07-26 13:29:26.953925+00",
        "method": "GET",
        "endpoint": "/v1/depth-cards",
        "status": 200,
        "latency_ms": 112
      },
      {
  … (23 more lines)

Version & tiers GET /v1/version

API version and the tier catalog.

GET/v1/version
curl -H "X-API-Key: $PRESSROOM_KEY" \
  "https://api.pressroom.news/v1/version"
RESPONSE · 200
{
  "name": "Pressroom Analytics API",
  "version": "1.0.0-dev",
  "tiers": {
    "free": {
      "monthly_quota": 1000,
      "rate_per_minute": 30
    },
    "academic": {
      "monthly_quota": 50000,
      "rate_per_minute": 60
    },
    "starter": {
      "monthly_quota": 100000,
      "rate_per_minute": 120
    },
    "growth": {
      "monthly_quota": 1000000,
      "rate_per_minute": 300
    },
    "pro": {
      "monthly_quota": 5000000,
      "rate_per_minute": 600
    },
    "enterprise": {
      "monthly_quota": -1,
      "rate_per_minute": 1200
    }
  }
}

Corpus health GET /v1/health

Outcome-based corpus health (unauthenticated).

GET/v1/health
curl -H "X-API-Key: $PRESSROOM_KEY" \
  "https://api.pressroom.news/v1/health"
RESPONSE · 200
{
  "state": "DEGRADED",
  "reasons": [
    "stale_leases",
    "backlog_with_zero_recent_commits"
  ],
  "process_reachable": true,
  "durable_work_healthy": false,
  "corpus_reachable": true,
  "articles": 204333,
  "corpus_db": "/opt/pressroom/data/pressroom.db",
  "metering_reconciliation_required": 0,
  "oldest_metering_reservation": null
}

Source registry GET /v1/sources

Every publication with its audience taxonomy.

GET/v1/sources
curl -H "X-API-Key: $PRESSROOM_KEY" \
  "https://api.pressroom.news/v1/sources"
RESPONSE · 200
{
  "data": [
    {
      "source_id": "aajkaal",
      "name": "Aajkaal",
      "country_of_origin": "India",
      "country_of_audience": null,
      "language": "bn",
      "nav_group": null,
      "category": "[\"regional\"]",
      "article_count": 265
    },
    {
      "source_id": "aapti-institute",
      "name": "Aapti Institute",
      "country_of_origin": "India",
      "country_of_audience": "India",
      "language": "en",
      "nav_group": null,
      "category": "[\"data-governance\", \"technology\", \"policy\"]",
      "article_count": 138
    },
    {
      "source_id": "ajit",
      "name": "Ajit",
      "country_of_origin": "India",
      "country_of_audience": null,
      "language": "pa",
      "nav_group": null,
      "category": "[\"regional\"]",
      "article_count": 49
    },
    {
      "source_id": "algerie-eco",
      "name": "Algerie-eco",
      "country_of_origin": "Algeria",
      "country_of_audience": null,
      "language": null,
  … (1739 more lines)

Changelog

Versioning & stability. The /v1 surface is stable. Additive changes — new fields on existing responses, new endpoints, new enum values — are not considered breaking and can land at any time; write clients that tolerate unknown fields. Breaking changes (removing or renaming a field, changing a field's type or meaning, removing an endpoint) get a new version path (/v2) with a deprecation window on the old one — /v1 is never broken in place. Every response carries meta.generated_at, so any payload can be located in time against this changelog.

DateChange
2026-07-27Canonical id/name fields (additive). Every primary object now exposes a canonical id and name alongside its historical key: quotes add id (= quote_id), story units add id (= unit_id), depth cards add id (= card_id), events and graph entity references add name (= label), event-related threads add id/name (= thread_id/label), sources add id (= source_id), topics add id/name (= slug/label; slug remains the URL path key), and the stance-dossier subject adds id (= person_id). Nothing is removed today: the legacy keys keep working as deprecated aliases for one API version and will be dropped in the next version path. Migrate reads to id/name now.
2026-07-27Typed OpenAPI schema across all operations. Every endpoint now declares a typed response model with per-field descriptions; the endpoint reference below is generated from that schema and cannot drift from the code.
2026-07-27Embeddable widgets launched. Drop-in intelligence widgets rendered via domain-locked publishable pk_ keys; every data fetch meters as one API call.
2026-07-27Errors documented. The uniform error envelope, every error code, and the metering / request-id headers are documented above.
2026-07-27v1.0.0 live. The Pressroom Analytics API is live at api.pressroom.news.

Full endpoint reference

All public operations, generated from the API's typed OpenAPI schema. Every path takes the same X-API-Key auth (widget data additionally accepts publishable pk_ keys).

Authentication. Every operation below takes the same auth header — X-API-Key: <key> (or Authorization: Bearer <key>) — omitted from the parameter tables. Widget-data routes additionally accept publishable pk_ keys. /v1/health and /v1/version are open. This reference is generated from the API's typed OpenAPI schema (scripts/deploy/gen_api_docs.py) — it cannot drift from the code.

Search, stream & workspace

Real-time mention stream (SSE, held open, since a cursor) GET /v1/stream

Server-Sent Events (text/event-stream), not JSON: emits `article` events for every article after the cursor, stays open polling for new arrivals with keepalive comments, then closes with a final `cursor` event carrying the resume URL.

Returns an SSE stream.

Parameters

ParameterTypeRequiredDefaultDescription
since_id queryintegeroptional0Resume cursor: only articles with id > since_id are streamed (0 = from the latest batch).
limit queryintegeroptional50Max articles per poll batch (default 50, max 500).

Response attributes

No JSON schema — this endpoint streams text/event-stream (see the description).

GET/v1/stream
curl -H "X-API-Key: $PRESSROOM_KEY" \
  "https://api.pressroom.news/v1/stream"
import requests

r = requests.get(
    "https://api.pressroom.news/v1/stream",
    headers={"X-API-Key": PRESSROOM_KEY},
)
print(r.json())
const res = await fetch("https://api.pressroom.news/v1/stream", {
  headers: { "X-API-Key": PRESSROOM_KEY }
});
const data = await res.json();

Attach a custom tag to an article (per-key, non-corpus) POST /v1/tag

Tags an article for your key only — tags live in the API meta store and never touch the corpus.

Returns the attached (article_id, tag) pair; 404 when the article does not exist.

Parameters

ParameterTypeRequiredDefaultDescription
article_id bodyintegerrequiredCorpus article id to tag.
tag bodystringrequiredTag value (1-60 chars; stored per-key, never in the corpus).

Response attributes

AttributeTypeDescription
dataTagResultThe endpoint's payload.
data.article_idinteger | nullThe tagged article id.
data.tagstring | nullThe tag value.
data.created_atstring | nullWhen the tag was attached (list responses).
metaMetaBlock | nullResponse metadata: compute time, pagination, endpoint extras.
meta.generated_atstring | nullUTC timestamp when the server computed this response — use it to honestly label cached/stale data ('as of …').
meta.totalinteger | nullTotal matching items across all pages.
meta.pageinteger | nullCurrent page number (1-based).
meta.page_sizeinteger | nullItems per page.
meta.windowWindow | nullThe resolved date window the data covers.
meta.window.fromstring | nullWindow start date (ISO YYYY-MM-DD, inclusive).
meta.window.tostring | nullWindow end date (ISO YYYY-MM-DD, inclusive).
meta.next_cursorinteger | nullCursor pagination (present only when the request passed starting_after): the last id in this page — pass it as the next request's starting_after. null means the list is exhausted (has_more == next_cursor is not null).
POST/v1/tag
curl -X POST -H "X-API-Key: $PRESSROOM_KEY" \
  -H "Content-Type: application/json" \
  -d '{"article_id": 186888, "tag": "follow-up"}' \
  "https://api.pressroom.news/v1/tag"
import requests

r = requests.post(
    "https://api.pressroom.news/v1/tag",
    headers={"X-API-Key": PRESSROOM_KEY},
    json={"article_id": 186888, "tag": "follow-up"},
)
print(r.json())
const res = await fetch("https://api.pressroom.news/v1/tag", {
  headers: { "X-API-Key": PRESSROOM_KEY, "Content-Type": "application/json" },
  method: "POST",
  body: JSON.stringify({"article_id": 186888, "tag": "follow-up"})
});
const data = await res.json();

List your tagged articles GET /v1/tags

Your key's article tags, newest first, optionally filtered to one tag value.

Returns the tag rows.

Parameters

ParameterTypeRequiredDefaultDescription
tag querystring | nulloptionalOnly rows with this exact tag value.

Response attributes

AttributeTypeDescription
dataarray of TagResultThe endpoint's payload.
data[].article_idinteger | nullThe tagged article id.
data[].tagstring | nullThe tag value.
data[].created_atstring | nullWhen the tag was attached (list responses).
metaMetaBlock | nullResponse metadata: compute time, pagination, endpoint extras.
meta.generated_atstring | nullUTC timestamp when the server computed this response — use it to honestly label cached/stale data ('as of …').
meta.totalinteger | nullTotal matching items across all pages.
meta.pageinteger | nullCurrent page number (1-based).
meta.page_sizeinteger | nullItems per page.
meta.windowWindow | nullThe resolved date window the data covers.
meta.window.fromstring | nullWindow start date (ISO YYYY-MM-DD, inclusive).
meta.window.tostring | nullWindow end date (ISO YYYY-MM-DD, inclusive).
meta.next_cursorinteger | nullCursor pagination (present only when the request passed starting_after): the last id in this page — pass it as the next request's starting_after. null means the list is exhausted (has_more == next_cursor is not null).
GET/v1/tags
curl -H "X-API-Key: $PRESSROOM_KEY" \
  "https://api.pressroom.news/v1/tags"
import requests

r = requests.get(
    "https://api.pressroom.news/v1/tags",
    headers={"X-API-Key": PRESSROOM_KEY},
)
print(r.json())
const res = await fetch("https://api.pressroom.news/v1/tags", {
  headers: { "X-API-Key": PRESSROOM_KEY }
});
const data = await res.json();

List your saved searches GET /v1/watchlists

All saved searches owned by your key, newest first.

Returns the watchlist rows with their stored queries.

Parameters

No parameters (beyond the auth header).

Response attributes

AttributeTypeDescription
dataarray of WatchlistThe endpoint's payload.
data[].idinteger | nullWatchlist id.
data[].namestring | nullYour name for the saved search.
data[].queryobject | nullThe saved search query object.
data[].created_atstring | nullWhen the watchlist was created.
metaMetaBlock | nullResponse metadata: compute time, pagination, endpoint extras.
meta.generated_atstring | nullUTC timestamp when the server computed this response — use it to honestly label cached/stale data ('as of …').
meta.totalinteger | nullTotal matching items across all pages.
meta.pageinteger | nullCurrent page number (1-based).
meta.page_sizeinteger | nullItems per page.
meta.windowWindow | nullThe resolved date window the data covers.
meta.window.fromstring | nullWindow start date (ISO YYYY-MM-DD, inclusive).
meta.window.tostring | nullWindow end date (ISO YYYY-MM-DD, inclusive).
meta.next_cursorinteger | nullCursor pagination (present only when the request passed starting_after): the last id in this page — pass it as the next request's starting_after. null means the list is exhausted (has_more == next_cursor is not null).
GET/v1/watchlists
curl -H "X-API-Key: $PRESSROOM_KEY" \
  "https://api.pressroom.news/v1/watchlists"
import requests

r = requests.get(
    "https://api.pressroom.news/v1/watchlists",
    headers={"X-API-Key": PRESSROOM_KEY},
)
print(r.json())
const res = await fetch("https://api.pressroom.news/v1/watchlists", {
  headers: { "X-API-Key": PRESSROOM_KEY }
});
const data = await res.json();

Create a saved search POST /v1/watchlists

Stores a named search query against your key (in the API meta store, not the corpus).

Returns the created watchlist with its id.

Parameters

ParameterTypeRequiredDefaultDescription
name bodystringrequiredYour name for the saved search (1-120 chars).
query bodyobjectrequiredThe search query object to save (same fields as /v1/search).

Response attributes

AttributeTypeDescription
dataWatchlistThe endpoint's payload.
data.idinteger | nullWatchlist id.
data.namestring | nullYour name for the saved search.
data.queryobject | nullThe saved search query object.
data.created_atstring | nullWhen the watchlist was created.
metaMetaBlock | nullResponse metadata: compute time, pagination, endpoint extras.
meta.generated_atstring | nullUTC timestamp when the server computed this response — use it to honestly label cached/stale data ('as of …').
meta.totalinteger | nullTotal matching items across all pages.
meta.pageinteger | nullCurrent page number (1-based).
meta.page_sizeinteger | nullItems per page.
meta.windowWindow | nullThe resolved date window the data covers.
meta.window.fromstring | nullWindow start date (ISO YYYY-MM-DD, inclusive).
meta.window.tostring | nullWindow end date (ISO YYYY-MM-DD, inclusive).
meta.next_cursorinteger | nullCursor pagination (present only when the request passed starting_after): the last id in this page — pass it as the next request's starting_after. null means the list is exhausted (has_more == next_cursor is not null).
POST/v1/watchlists
curl -X POST -H "X-API-Key: $PRESSROOM_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name": "my-watchlist", "query": "election"}' \
  "https://api.pressroom.news/v1/watchlists"
import requests

r = requests.post(
    "https://api.pressroom.news/v1/watchlists",
    headers={"X-API-Key": PRESSROOM_KEY},
    json={"name": "my-watchlist", "query": "election"},
)
print(r.json())
const res = await fetch("https://api.pressroom.news/v1/watchlists", {
  headers: { "X-API-Key": PRESSROOM_KEY, "Content-Type": "application/json" },
  method: "POST",
  body: JSON.stringify({"name": "my-watchlist", "query": "election"})
});
const data = await res.json();

Delete a saved search DELETE /v1/watchlists/{wid}

Deletes one of your saved searches.

Returns the deleted watchlist id; 404 when it does not exist or is not yours.

Parameters

ParameterTypeRequiredDefaultDescription
wid pathintegerrequiredWatchlist id to delete.

Response attributes

AttributeTypeDescription
dataWatchlistDeletedThe endpoint's payload.
data.deletedinteger | nullId of the deleted watchlist.
metaMetaBlock | nullResponse metadata: compute time, pagination, endpoint extras.
meta.generated_atstring | nullUTC timestamp when the server computed this response — use it to honestly label cached/stale data ('as of …').
meta.totalinteger | nullTotal matching items across all pages.
meta.pageinteger | nullCurrent page number (1-based).
meta.page_sizeinteger | nullItems per page.
meta.windowWindow | nullThe resolved date window the data covers.
meta.window.fromstring | nullWindow start date (ISO YYYY-MM-DD, inclusive).
meta.window.tostring | nullWindow end date (ISO YYYY-MM-DD, inclusive).
meta.next_cursorinteger | nullCursor pagination (present only when the request passed starting_after): the last id in this page — pass it as the next request's starting_after. null means the list is exhausted (has_more == next_cursor is not null).
DELETE/v1/watchlists/{wid}
curl -X DELETE -H "X-API-Key: $PRESSROOM_KEY" \
  "https://api.pressroom.news/v1/watchlists/1"
import requests

r = requests.delete(
    "https://api.pressroom.news/v1/watchlists/1",
    headers={"X-API-Key": PRESSROOM_KEY},
)
print(r.json())
const res = await fetch("https://api.pressroom.news/v1/watchlists/1", {
  headers: { "X-API-Key": PRESSROOM_KEY },
  method: "DELETE"
});
const data = await res.json();

Events

Paginated canonical event registry GET /v1/events

The canonical event registry (franchises, editions, and flat events) with optional domain/type/year/QID filters.

Returns event rows with coverage counts and timeline availability flags.

Parameters

ParameterTypeRequiredDefaultDescription
domain querystring | nulloptionalfilter event_domain: sport|policy|other|non-sport
type querystring | nulloptionalfilter event_type
year querystring | nulloptionalfilter event_year
has_qid querystring | nulloptionalWikidata-linked filter
page queryintegeroptional1Page number (1-based).
page_size queryintegeroptional50Items per page (default 50, max 200).

Response attributes

AttributeTypeDescription
dataarray of EventListItemThe endpoint's payload.
data[].idinteger | nullCanonical event id.
data[].namestring | nullEvent display name (canonical).
data[].labelstring | nullDeprecated alias of `name`; will be removed in the next API version.
data[].wikidata_idstring | nullWikidata QID when linked.
data[].event_yearstring | nullEdition year, when the event is an edition.
data[].event_domainstring | nullCoarse domain: sport | policy | other.
data[].event_typestring | nullGranular type: conflict | election | disaster | sports | summit | conference | festival | observance | ceremony | other.
data[].parent_idinteger | nullParent (franchise) event id, when an edition.
data[].children_countinteger | nullNumber of child events.
data[].article_countinteger | nullDistinct articles mentioning the event.
data[].has_timelineboolean | nullTrue when a pre-generated narrative timeline artifact exists.
data[].beat_countinteger | nullBeats in the timeline artifact (0 when none).
metaMetaBlock | nullResponse metadata: compute time, pagination, endpoint extras.
meta.generated_atstring | nullUTC timestamp when the server computed this response — use it to honestly label cached/stale data ('as of …').
meta.totalinteger | nullTotal matching items across all pages.
meta.pageinteger | nullCurrent page number (1-based).
meta.page_sizeinteger | nullItems per page.
meta.windowWindow | nullThe resolved date window the data covers.
meta.window.fromstring | nullWindow start date (ISO YYYY-MM-DD, inclusive).
meta.window.tostring | nullWindow end date (ISO YYYY-MM-DD, inclusive).
meta.next_cursorinteger | nullCursor pagination (present only when the request passed starting_after): the last id in this page — pass it as the next request's starting_after. null means the list is exhausted (has_more == next_cursor is not null).
GET/v1/events
curl -H "X-API-Key: $PRESSROOM_KEY" \
  "https://api.pressroom.news/v1/events"
import requests

r = requests.get(
    "https://api.pressroom.news/v1/events",
    headers={"X-API-Key": PRESSROOM_KEY},
)
print(r.json())
const res = await fetch("https://api.pressroom.news/v1/events", {
  headers: { "X-API-Key": PRESSROOM_KEY }
});
const data = await res.json();

Canonical event profile GET /v1/events/{eid}

The canonical event profile: identity, dates, franchise/edition hierarchy, coverage stats, timeline availability, and cross-referenced Emergence threads.

Returns the EventProfile object.

Parameters

ParameterTypeRequiredDefaultDescription
eid pathintegerrequiredCanonical event id.

Response attributes

AttributeTypeDescription
dataEventProfileThe endpoint's payload.
data.objectstring | nullObject type discriminator: always "event".
data.idinteger | nullCanonical event id.
data.kindstring | nullAlways 'event'.
data.namestring | nullEvent display label.
data.event_domainstring | nullCoarse domain: sport | policy | other.
data.event_typestring | nullGranular event type.
data.event_yearstring | nullEdition year, when applicable.
data.event_dateobject | nullKnown event dates {start, end, precision}.
data.identityobject | nullIdentity block {resolved, wikidata_id}.
data.descriptionstring | nullEvent description, when available.
data.hierarchyEventHierarchy | nullFranchise/edition hierarchy.
data.hierarchy.rolestring | nullfranchise (has editions) | child (an edition/sub-event) | flat.
data.hierarchy.parentobject | nullThe parent event {id, name, event_year}, when any.
data.hierarchy.childrenarray of object | nullDirect child events {id, name, event_year, event_domain}.
data.hierarchy.children_countinteger | nullNumber of direct children.
data.statsobject | nullCoverage stats {article_count, mention_count, first_seen, last_seen}.
data.timelineobject | nullTimeline availability {has_timeline, beat_count}.
data.related_threadsarray of RelatedThread | nullEmergence threads covering this happening (cross-reference only).
data.related_threads[].idinteger | nullEmergence thread id (canonical).
data.related_threads[].thread_idinteger | nullDeprecated alias of `id`; will be removed in the next API version.
data.related_threads[].namestring | nullThread label surface (canonical).
data.related_threads[].labelstring | nullDeprecated alias of `name`; will be removed in the next API version.
data.related_threads[].detectorstring | nullDetector that raised the thread.
data.related_threads[].kindstring | nullThread kind (event | topic).
data.related_threads[].statestring | nullThread lifecycle state (watch | dormant | …).
data.related_threads[].link_basisstring | nullWhy the thread was linked to the event.
data.related_threads[].notestring | nullCross-reference caveat.
data.notestring | nullScope note (clustering is owned by Emergence).
metaMetaBlock | nullResponse metadata: compute time, pagination, endpoint extras.
meta.generated_atstring | nullUTC timestamp when the server computed this response — use it to honestly label cached/stale data ('as of …').
meta.totalinteger | nullTotal matching items across all pages.
meta.pageinteger | nullCurrent page number (1-based).
meta.page_sizeinteger | nullItems per page.
meta.windowWindow | nullThe resolved date window the data covers.
meta.window.fromstring | nullWindow start date (ISO YYYY-MM-DD, inclusive).
meta.window.tostring | nullWindow end date (ISO YYYY-MM-DD, inclusive).
meta.next_cursorinteger | nullCursor pagination (present only when the request passed starting_after): the last id in this page — pass it as the next request's starting_after. null means the list is exhausted (has_more == next_cursor is not null).
GET/v1/events/{eid}
curl -H "X-API-Key: $PRESSROOM_KEY" \
  "https://api.pressroom.news/v1/events/174"
import requests

r = requests.get(
    "https://api.pressroom.news/v1/events/174",
    headers={"X-API-Key": PRESSROOM_KEY},
)
print(r.json())
const res = await fetch("https://api.pressroom.news/v1/events/174", {
  headers: { "X-API-Key": PRESSROOM_KEY }
});
const data = await res.json();

Articles mentioning an event GET /v1/events/{eid}/articles

Paginated articles mentioning the event, newest first, each with mention count and provenance.

Returns the article list with pagination meta.

Parameters

ParameterTypeRequiredDefaultDescription
eid pathintegerrequiredCanonical event id.
page queryintegeroptional1Page number (1-based).
page_size queryintegeroptional50Items per page (default 50, max 200).

Response attributes

AttributeTypeDescription
dataarray of ArticleItemThe endpoint's payload.
data[].article_idinteger | nullCorpus article id.
data[].headlinestring | nullArticle headline.
data[].dekstring | nullArticle dek / standfirst, when extracted.
data[].languagestring | nullDetected article language (ISO code).
data[].mention_countinteger | nullHow many times the queried entity is mentioned in this article.
data[].provenanceProvenance | nullProvenance of the article (source, URL, extraction record).
data[].provenance.article_idinteger | nullCorpus article id the claim came from.
data[].provenance.headlinestring | nullHeadline of the source article.
data[].provenance.source_idstring | nullRegistry id of the publishing source.
data[].provenance.source_namestring | nullDisplay name of the publishing source.
data[].provenance.source_urlstring | nullCanonical URL of the source article.
data[].provenance.extraction_methodstring | nullExtractor that captured the article (trafilatura, playwright, …).
data[].provenance.extracted_atstring | nullUTC timestamp of extraction.
data[].provenance.date_published_utcstring | nullPublisher-stated publication time (UTC).
data[].provenance.word_countinteger | nullExtracted body word count.
metaMetaBlock | nullResponse metadata: compute time, pagination, endpoint extras.
meta.generated_atstring | nullUTC timestamp when the server computed this response — use it to honestly label cached/stale data ('as of …').
meta.totalinteger | nullTotal matching items across all pages.
meta.pageinteger | nullCurrent page number (1-based).
meta.page_sizeinteger | nullItems per page.
meta.windowWindow | nullThe resolved date window the data covers.
meta.window.fromstring | nullWindow start date (ISO YYYY-MM-DD, inclusive).
meta.window.tostring | nullWindow end date (ISO YYYY-MM-DD, inclusive).
meta.next_cursorinteger | nullCursor pagination (present only when the request passed starting_after): the last id in this page — pass it as the next request's starting_after. null means the list is exhausted (has_more == next_cursor is not null).
GET/v1/events/{eid}/articles
curl -H "X-API-Key: $PRESSROOM_KEY" \
  "https://api.pressroom.news/v1/events/174/articles"
import requests

r = requests.get(
    "https://api.pressroom.news/v1/events/174/articles",
    headers={"X-API-Key": PRESSROOM_KEY},
)
print(r.json())
const res = await fetch("https://api.pressroom.news/v1/events/174/articles", {
  headers: { "X-API-Key": PRESSROOM_KEY }
});
const data = await res.json();

Event coverage timeseries + by-source GET /v1/events/{eid}/coverage

Mention timeseries (articles + mentions per bucket) and the top publishing sources for the event.

Returns the EventCoverage object.

Parameters

ParameterTypeRequiredDefaultDescription
eid pathintegerrequiredCanonical event id.
bucket querystringoptional"month"Bucket granularity: day | week | month (default month).

Response attributes

AttributeTypeDescription
dataEventCoverageThe endpoint's payload.
data.idinteger | nullCanonical event id.
data.bucketstring | nullBucket granularity: day | week | month.
data.methodstring | nullHow the series was computed.
data.timeseriesarray of SeriesBucket | nullArticles + mentions per time bucket.
data.timeseries[].bucketstring | nullBucket key (YYYY-MM-DD day, YYYY-Www week, or YYYY-MM month).
data.timeseries[].articlesinteger | nullDistinct articles in the bucket.
data.timeseries[].mentionsinteger | number | nullTotal mentions in the bucket (event coverage only).
data.by_sourcearray of SourceBreakdown | nullTop publishing sources.
data.by_source[].sourcestring | nullSource display name.
data.by_source[].articlesinteger | nullDistinct articles from this source in the window.
metaMetaBlock | nullResponse metadata: compute time, pagination, endpoint extras.
meta.generated_atstring | nullUTC timestamp when the server computed this response — use it to honestly label cached/stale data ('as of …').
meta.totalinteger | nullTotal matching items across all pages.
meta.pageinteger | nullCurrent page number (1-based).
meta.page_sizeinteger | nullItems per page.
meta.windowWindow | nullThe resolved date window the data covers.
meta.window.fromstring | nullWindow start date (ISO YYYY-MM-DD, inclusive).
meta.window.tostring | nullWindow end date (ISO YYYY-MM-DD, inclusive).
meta.next_cursorinteger | nullCursor pagination (present only when the request passed starting_after): the last id in this page — pass it as the next request's starting_after. null means the list is exhausted (has_more == next_cursor is not null).
GET/v1/events/{eid}/coverage
curl -H "X-API-Key: $PRESSROOM_KEY" \
  "https://api.pressroom.news/v1/events/174/coverage"
import requests

r = requests.get(
    "https://api.pressroom.news/v1/events/174/coverage",
    headers={"X-API-Key": PRESSROOM_KEY},
)
print(r.json())
const res = await fetch("https://api.pressroom.news/v1/events/174/coverage", {
  headers: { "X-API-Key": PRESSROOM_KEY }
});
const data = await res.json();

Emergence threads covering this event (read-only cross-reference) GET /v1/events/{eid}/threads

Emergence threads (ADR-057) clustering the same happening, matched on label/anchor overlap — a read-only cross-reference, never a merge.

Returns up to 5 linked threads.

Parameters

ParameterTypeRequiredDefaultDescription
eid pathintegerrequiredCanonical event id.

Response attributes

AttributeTypeDescription
dataarray of RelatedThreadThe endpoint's payload.
data[].idinteger | nullEmergence thread id (canonical).
data[].thread_idinteger | nullDeprecated alias of `id`; will be removed in the next API version.
data[].namestring | nullThread label surface (canonical).
data[].labelstring | nullDeprecated alias of `name`; will be removed in the next API version.
data[].detectorstring | nullDetector that raised the thread.
data[].kindstring | nullThread kind (event | topic).
data[].statestring | nullThread lifecycle state (watch | dormant | …).
data[].link_basisstring | nullWhy the thread was linked to the event.
data[].notestring | nullCross-reference caveat.
metaMetaBlock | nullResponse metadata: compute time, pagination, endpoint extras.
meta.generated_atstring | nullUTC timestamp when the server computed this response — use it to honestly label cached/stale data ('as of …').
meta.totalinteger | nullTotal matching items across all pages.
meta.pageinteger | nullCurrent page number (1-based).
meta.page_sizeinteger | nullItems per page.
meta.windowWindow | nullThe resolved date window the data covers.
meta.window.fromstring | nullWindow start date (ISO YYYY-MM-DD, inclusive).
meta.window.tostring | nullWindow end date (ISO YYYY-MM-DD, inclusive).
meta.next_cursorinteger | nullCursor pagination (present only when the request passed starting_after): the last id in this page — pass it as the next request's starting_after. null means the list is exhausted (has_more == next_cursor is not null).
GET/v1/events/{eid}/threads
curl -H "X-API-Key: $PRESSROOM_KEY" \
  "https://api.pressroom.news/v1/events/174/threads"
import requests

r = requests.get(
    "https://api.pressroom.news/v1/events/174/threads",
    headers={"X-API-Key": PRESSROOM_KEY},
)
print(r.json())
const res = await fetch("https://api.pressroom.news/v1/events/174/threads", {
  headers: { "X-API-Key": PRESSROOM_KEY }
});
const data = await res.json();

Deterministic per-event narrative timeline (beats + provenance) GET /v1/events/{eid}/timeline

Full event-arc timeline: gap-clustered beats (representative + secondary headlines with article_id citations, verbatim actor-action lines with sentence provenance, quotes), an UNDATED shelf and a What's-next tail — served from pre-generated artifacts. When no artifact exists, a degraded DB-derived monthly digest is returned with status='fallback'.

Returns the EventTimeline artifact.

Parameters

ParameterTypeRequiredDefaultDescription
eid pathintegerrequiredCanonical event id.

Response attributes

AttributeTypeDescription
dataEventTimelineThe endpoint's payload.
data.schemastring | nullArtifact schema version.
data.statusstring | null'fallback' marks the degraded DB-derived timeline; absent/other for the full pre-generated artifact.
data.methodstring | nullHow the timeline was generated.
data.event_idinteger | nullCanonical event id.
data.namestring | nullEvent display name (canonical).
data.labelstring | nullDeprecated alias of `name`; will be removed in the next API version.
data.beatsarray of object | nullChronological beats: representative + secondary headlines with article_id citations; the full artifact adds verbatim actor-action lines and quotes with provenance.
data.statsobject | nullTimeline aggregates.
data.notestring | nullHonesty note for fallback timelines.
metaMetaBlock | nullResponse metadata: compute time, pagination, endpoint extras.
meta.generated_atstring | nullUTC timestamp when the server computed this response — use it to honestly label cached/stale data ('as of …').
meta.totalinteger | nullTotal matching items across all pages.
meta.pageinteger | nullCurrent page number (1-based).
meta.page_sizeinteger | nullItems per page.
meta.windowWindow | nullThe resolved date window the data covers.
meta.window.fromstring | nullWindow start date (ISO YYYY-MM-DD, inclusive).
meta.window.tostring | nullWindow end date (ISO YYYY-MM-DD, inclusive).
meta.next_cursorinteger | nullCursor pagination (present only when the request passed starting_after): the last id in this page — pass it as the next request's starting_after. null means the list is exhausted (has_more == next_cursor is not null).
GET/v1/events/{eid}/timeline
curl -H "X-API-Key: $PRESSROOM_KEY" \
  "https://api.pressroom.news/v1/events/174/timeline"
import requests

r = requests.get(
    "https://api.pressroom.news/v1/events/174/timeline",
    headers={"X-API-Key": PRESSROOM_KEY},
)
print(r.json())
const res = await fetch("https://api.pressroom.news/v1/events/174/timeline", {
  headers: { "X-API-Key": PRESSROOM_KEY }
});
const data = await res.json();

Topics

List curated topics GET /v1/topics

The curated topic overlay: every topic with its keywords, aliases, mapped sections, and article count.

Returns the full topic list.

Parameters

No parameters (beyond the auth header).

Response attributes

AttributeTypeDescription
dataarray of TopicThe endpoint's payload.
data[].objectstring | nullObject type discriminator: always "topic".
data[].idstring | nullCanonical topic id (same value as `slug`, which remains the URL path key).
data[].namestring | nullTopic display name (canonical).
data[].slugstring | nullStable topic slug — the URL path key (same value as `id`).
data[].labelstring | nullDeprecated alias of `name`; will be removed in the next API version.
data[].sourcestring | nullWhere the topic came from (curated).
data[].article_countinteger | nullArticles matched to the topic.
data[].keywordsarray of any | nullMatching keywords.
data[].aliasesarray of any | nullAlias surfaces.
data[].sectionsarray of any | nullPublisher sections mapped to the topic.
data[].updated_atstring | nullWhen the topic membership was last materialised.
data[].statsobject | nullCoverage stats (topic profile only).
metaMetaBlock | nullResponse metadata: compute time, pagination, endpoint extras.
meta.generated_atstring | nullUTC timestamp when the server computed this response — use it to honestly label cached/stale data ('as of …').
meta.totalinteger | nullTotal matching items across all pages.
meta.pageinteger | nullCurrent page number (1-based).
meta.page_sizeinteger | nullItems per page.
meta.windowWindow | nullThe resolved date window the data covers.
meta.window.fromstring | nullWindow start date (ISO YYYY-MM-DD, inclusive).
meta.window.tostring | nullWindow end date (ISO YYYY-MM-DD, inclusive).
meta.next_cursorinteger | nullCursor pagination (present only when the request passed starting_after): the last id in this page — pass it as the next request's starting_after. null means the list is exhausted (has_more == next_cursor is not null).
GET/v1/topics
curl -H "X-API-Key: $PRESSROOM_KEY" \
  "https://api.pressroom.news/v1/topics"
import requests

r = requests.get(
    "https://api.pressroom.news/v1/topics",
    headers={"X-API-Key": PRESSROOM_KEY},
)
print(r.json())
const res = await fetch("https://api.pressroom.news/v1/topics", {
  headers: { "X-API-Key": PRESSROOM_KEY }
});
const data = await res.json();

Curated topic profile GET /v1/topics/{slug}

One curated topic with its keywords, aliases, sections, and coverage stats.

Returns the Topic object.

Parameters

ParameterTypeRequiredDefaultDescription
slug pathstringrequiredTopic slug (lowercase letters, digits, hyphens).

Response attributes

AttributeTypeDescription
dataTopicThe endpoint's payload.
data.objectstring | nullObject type discriminator: always "topic".
data.idstring | nullCanonical topic id (same value as `slug`, which remains the URL path key).
data.namestring | nullTopic display name (canonical).
data.slugstring | nullStable topic slug — the URL path key (same value as `id`).
data.labelstring | nullDeprecated alias of `name`; will be removed in the next API version.
data.sourcestring | nullWhere the topic came from (curated).
data.article_countinteger | nullArticles matched to the topic.
data.keywordsarray of any | nullMatching keywords.
data.aliasesarray of any | nullAlias surfaces.
data.sectionsarray of any | nullPublisher sections mapped to the topic.
data.updated_atstring | nullWhen the topic membership was last materialised.
data.statsobject | nullCoverage stats (topic profile only).
metaMetaBlock | nullResponse metadata: compute time, pagination, endpoint extras.
meta.generated_atstring | nullUTC timestamp when the server computed this response — use it to honestly label cached/stale data ('as of …').
meta.totalinteger | nullTotal matching items across all pages.
meta.pageinteger | nullCurrent page number (1-based).
meta.page_sizeinteger | nullItems per page.
meta.windowWindow | nullThe resolved date window the data covers.
meta.window.fromstring | nullWindow start date (ISO YYYY-MM-DD, inclusive).
meta.window.tostring | nullWindow end date (ISO YYYY-MM-DD, inclusive).
meta.next_cursorinteger | nullCursor pagination (present only when the request passed starting_after): the last id in this page — pass it as the next request's starting_after. null means the list is exhausted (has_more == next_cursor is not null).
GET/v1/topics/{slug}
curl -H "X-API-Key: $PRESSROOM_KEY" \
  "https://api.pressroom.news/v1/topics/artificial-intelligence"
import requests

r = requests.get(
    "https://api.pressroom.news/v1/topics/artificial-intelligence",
    headers={"X-API-Key": PRESSROOM_KEY},
)
print(r.json())
const res = await fetch("https://api.pressroom.news/v1/topics/artificial-intelligence", {
  headers: { "X-API-Key": PRESSROOM_KEY }
});
const data = await res.json();

Articles under a topic GET /v1/topics/{slug}/articles

Paginated articles matched to the topic, newest first, each with provenance.

Returns the article list with pagination meta.

Parameters

ParameterTypeRequiredDefaultDescription
slug pathstringrequiredTopic slug (lowercase letters, digits, hyphens).
page queryintegeroptional1Page number (1-based).
page_size queryintegeroptional50Items per page (default 50, max 200).

Response attributes

AttributeTypeDescription
dataarray of ArticleItemThe endpoint's payload.
data[].article_idinteger | nullCorpus article id.
data[].headlinestring | nullArticle headline.
data[].dekstring | nullArticle dek / standfirst, when extracted.
data[].languagestring | nullDetected article language (ISO code).
data[].mention_countinteger | nullHow many times the queried entity is mentioned in this article.
data[].provenanceProvenance | nullProvenance of the article (source, URL, extraction record).
data[].provenance.article_idinteger | nullCorpus article id the claim came from.
data[].provenance.headlinestring | nullHeadline of the source article.
data[].provenance.source_idstring | nullRegistry id of the publishing source.
data[].provenance.source_namestring | nullDisplay name of the publishing source.
data[].provenance.source_urlstring | nullCanonical URL of the source article.
data[].provenance.extraction_methodstring | nullExtractor that captured the article (trafilatura, playwright, …).
data[].provenance.extracted_atstring | nullUTC timestamp of extraction.
data[].provenance.date_published_utcstring | nullPublisher-stated publication time (UTC).
data[].provenance.word_countinteger | nullExtracted body word count.
metaMetaBlock | nullResponse metadata: compute time, pagination, endpoint extras.
meta.generated_atstring | nullUTC timestamp when the server computed this response — use it to honestly label cached/stale data ('as of …').
meta.totalinteger | nullTotal matching items across all pages.
meta.pageinteger | nullCurrent page number (1-based).
meta.page_sizeinteger | nullItems per page.
meta.windowWindow | nullThe resolved date window the data covers.
meta.window.fromstring | nullWindow start date (ISO YYYY-MM-DD, inclusive).
meta.window.tostring | nullWindow end date (ISO YYYY-MM-DD, inclusive).
meta.next_cursorinteger | nullCursor pagination (present only when the request passed starting_after): the last id in this page — pass it as the next request's starting_after. null means the list is exhausted (has_more == next_cursor is not null).
GET/v1/topics/{slug}/articles
curl -H "X-API-Key: $PRESSROOM_KEY" \
  "https://api.pressroom.news/v1/topics/artificial-intelligence/articles"
import requests

r = requests.get(
    "https://api.pressroom.news/v1/topics/artificial-intelligence/articles",
    headers={"X-API-Key": PRESSROOM_KEY},
)
print(r.json())
const res = await fetch("https://api.pressroom.news/v1/topics/artificial-intelligence/articles", {
  headers: { "X-API-Key": PRESSROOM_KEY }
});
const data = await res.json();

Topic coverage timeseries + breakdowns GET /v1/topics/{slug}/coverage

Article counts for the topic per time bucket, with source / geography / language breakdowns over the window.

Returns the coverage series and breakdowns.

Parameters

ParameterTypeRequiredDefaultDescription
slug pathstringrequiredTopic slug (lowercase letters, digits, hyphens).
date_from querystring | nulloptionalWindow start, ISO YYYY-MM-DD (default: 90 days before date_to; max window 1825 days).
date_to querystring | nulloptionalWindow end, ISO YYYY-MM-DD (default: today, UTC).
bucket querystringoptional"day"Bucket granularity: day | week | month (default day).

Response attributes

AttributeTypeDescription
dataTopicCoverageThe endpoint's payload.
data.slugstring | nullThe topic slug.
data.bucketstring | nullBucket granularity: day | week | month.
data.windowobject | nullResolved date window {from, to}.
data.total_articlesinteger | nullDistinct articles in the window.
data.seriesarray of object | nullArticle counts per time bucket.
data.by_sourcearray of object | nullBreakdown by publishing source.
data.by_geographyarray of object | nullBreakdown by source geography.
data.by_languagearray of object | nullBreakdown by article language.
metaMetaBlock | nullResponse metadata: compute time, pagination, endpoint extras.
meta.generated_atstring | nullUTC timestamp when the server computed this response — use it to honestly label cached/stale data ('as of …').
meta.totalinteger | nullTotal matching items across all pages.
meta.pageinteger | nullCurrent page number (1-based).
meta.page_sizeinteger | nullItems per page.
meta.windowWindow | nullThe resolved date window the data covers.
meta.window.fromstring | nullWindow start date (ISO YYYY-MM-DD, inclusive).
meta.window.tostring | nullWindow end date (ISO YYYY-MM-DD, inclusive).
meta.next_cursorinteger | nullCursor pagination (present only when the request passed starting_after): the last id in this page — pass it as the next request's starting_after. null means the list is exhausted (has_more == next_cursor is not null).
GET/v1/topics/{slug}/coverage
curl -H "X-API-Key: $PRESSROOM_KEY" \
  "https://api.pressroom.news/v1/topics/artificial-intelligence/coverage"
import requests

r = requests.get(
    "https://api.pressroom.news/v1/topics/artificial-intelligence/coverage",
    headers={"X-API-Key": PRESSROOM_KEY},
)
print(r.json())
const res = await fetch("https://api.pressroom.news/v1/topics/artificial-intelligence/coverage", {
  headers: { "X-API-Key": PRESSROOM_KEY }
});
const data = await res.json();

Top co-occurring people / orgs / locations GET /v1/topics/{slug}/entities

The entities most covered under the topic, per kind, ranked by distinct articles.

Returns top people, organisations, and locations.

Parameters

ParameterTypeRequiredDefaultDescription
slug pathstringrequiredTopic slug (lowercase letters, digits, hyphens).
limit queryintegeroptional15Max entities per kind (default 15, max 50).

Response attributes

AttributeTypeDescription
dataTopicEntitiesThe endpoint's payload.
data.slugstring | nullThe topic slug.
data.peoplearray of any | nullTop people under the topic.
data.organisationsarray of any | nullTop organisations under the topic.
data.locationsarray of any | nullTop locations under the topic.
metaMetaBlock | nullResponse metadata: compute time, pagination, endpoint extras.
meta.generated_atstring | nullUTC timestamp when the server computed this response — use it to honestly label cached/stale data ('as of …').
meta.totalinteger | nullTotal matching items across all pages.
meta.pageinteger | nullCurrent page number (1-based).
meta.page_sizeinteger | nullItems per page.
meta.windowWindow | nullThe resolved date window the data covers.
meta.window.fromstring | nullWindow start date (ISO YYYY-MM-DD, inclusive).
meta.window.tostring | nullWindow end date (ISO YYYY-MM-DD, inclusive).
meta.next_cursorinteger | nullCursor pagination (present only when the request passed starting_after): the last id in this page — pass it as the next request's starting_after. null means the list is exhausted (has_more == next_cursor is not null).
GET/v1/topics/{slug}/entities
curl -H "X-API-Key: $PRESSROOM_KEY" \
  "https://api.pressroom.news/v1/topics/artificial-intelligence/entities"
import requests

r = requests.get(
    "https://api.pressroom.news/v1/topics/artificial-intelligence/entities",
    headers={"X-API-Key": PRESSROOM_KEY},
)
print(r.json())
const res = await fetch("https://api.pressroom.news/v1/topics/artificial-intelligence/entities", {
  headers: { "X-API-Key": PRESSROOM_KEY }
});
const data = await res.json();

Entities (people · organisations · locations)

People affiliated with an organisation (employer link) GET /v1/organisations/{eid}/people

People linked to the organisation through the normalised employer relation, highest link-confidence first.

Returns the affiliated people with link method and confidence.

Parameters

ParameterTypeRequiredDefaultDescription
eid pathintegerrequiredCanonical organisation id.
page queryintegeroptional1Page number (1-based).
page_size queryintegeroptional50Items per page (default 50, max 200).

Response attributes

AttributeTypeDescription
dataarray of OrgPersonThe endpoint's payload.
data[].idinteger | nullCanonical person id.
data[].namestring | nullCanonical person name.
data[].titlestring | nullCurrent title, when known.
data[].wikidata_idstring | nullWikidata QID when resolved.
data[].employer_textstring | nullThe employer surface the link was made from.
data[].link_methodstring | nullHow the employer was matched (exact, acronym, …).
data[].confidenceinteger | number | nullEmployer-link confidence in [0, 1].
metaMetaBlock | nullResponse metadata: compute time, pagination, endpoint extras.
meta.generated_atstring | nullUTC timestamp when the server computed this response — use it to honestly label cached/stale data ('as of …').
meta.totalinteger | nullTotal matching items across all pages.
meta.pageinteger | nullCurrent page number (1-based).
meta.page_sizeinteger | nullItems per page.
meta.windowWindow | nullThe resolved date window the data covers.
meta.window.fromstring | nullWindow start date (ISO YYYY-MM-DD, inclusive).
meta.window.tostring | nullWindow end date (ISO YYYY-MM-DD, inclusive).
meta.next_cursorinteger | nullCursor pagination (present only when the request passed starting_after): the last id in this page — pass it as the next request's starting_after. null means the list is exhausted (has_more == next_cursor is not null).
GET/v1/organisations/{eid}/people
curl -H "X-API-Key: $PRESSROOM_KEY" \
  "https://api.pressroom.news/v1/organisations/174/people"
import requests

r = requests.get(
    "https://api.pressroom.news/v1/organisations/174/people",
    headers={"X-API-Key": PRESSROOM_KEY},
)
print(r.json())
const res = await fetch("https://api.pressroom.news/v1/organisations/174/people", {
  headers: { "X-API-Key": PRESSROOM_KEY }
});
const data = await res.json();

Quotes attributed to a person GET /v1/people/{eid}/quotes

Quotes attributed to the person (canonical entity linkage plus legacy attribution rows), newest first, each with provenance.

Returns the quote list with pagination meta.

Parameters

ParameterTypeRequiredDefaultDescription
eid pathintegerrequiredCanonical person id.
page queryintegeroptional1Page number (1-based).
page_size queryintegeroptional50Items per page (default 50, max 200).

Response attributes

AttributeTypeDescription
dataarray of QuoteThe endpoint's payload.
data[].objectstring | nullObject type discriminator: always "quote".
data[].idinteger | nullCorpus quote id (canonical).
data[].quote_idinteger | nullDeprecated alias of `id`; will be removed in the next API version.
data[].textstring | nullVerbatim quote text.
data[].verbstring | nullAttribution verb (said, told, announced, …).
data[].typestring | nullQuote type: direct | indirect.
data[].confidenceinteger | number | nullExtraction/attribution confidence in [0, 1].
data[].speakerSpeaker | nullResolved speaker (absent on person-scoped quote lists, where the person is the request subject).
data[].speaker.kindstring | nullSpeaker entity kind: person | org | location; null when unresolved.
data[].speaker.entity_idinteger | nullCanonical entity id of the speaker (per kind), when resolved.
data[].speaker.person_idinteger | nullLegacy person id linkage (kept for backward compatibility).
data[].speaker.namestring | nullCanonical name of the resolved speaker entity.
data[].speaker.speaker_textstring | nullThe verbatim speaker surface as written in the article.
data[].speaker.resolvedboolean | nullTrue when the speaker is linked to ANY canonical entity.
data[].provenanceProvenance | nullSource article the quote was extracted from.
data[].provenance.article_idinteger | nullCorpus article id the claim came from.
data[].provenance.headlinestring | nullHeadline of the source article.
data[].provenance.source_idstring | nullRegistry id of the publishing source.
data[].provenance.source_namestring | nullDisplay name of the publishing source.
data[].provenance.source_urlstring | nullCanonical URL of the source article.
data[].provenance.extraction_methodstring | nullExtractor that captured the article (trafilatura, playwright, …).
data[].provenance.extracted_atstring | nullUTC timestamp of extraction.
data[].provenance.date_published_utcstring | nullPublisher-stated publication time (UTC).
data[].provenance.word_countinteger | nullExtracted body word count.
metaMetaBlock | nullResponse metadata: compute time, pagination, endpoint extras.
meta.generated_atstring | nullUTC timestamp when the server computed this response — use it to honestly label cached/stale data ('as of …').
meta.totalinteger | nullTotal matching items across all pages.
meta.pageinteger | nullCurrent page number (1-based).
meta.page_sizeinteger | nullItems per page.
meta.windowWindow | nullThe resolved date window the data covers.
meta.window.fromstring | nullWindow start date (ISO YYYY-MM-DD, inclusive).
meta.window.tostring | nullWindow end date (ISO YYYY-MM-DD, inclusive).
meta.next_cursorinteger | nullCursor pagination (present only when the request passed starting_after): the last id in this page — pass it as the next request's starting_after. null means the list is exhausted (has_more == next_cursor is not null).
GET/v1/people/{eid}/quotes
curl -H "X-API-Key: $PRESSROOM_KEY" \
  "https://api.pressroom.news/v1/people/174/quotes"
import requests

r = requests.get(
    "https://api.pressroom.news/v1/people/174/quotes",
    headers={"X-API-Key": PRESSROOM_KEY},
)
print(r.json())
const res = await fetch("https://api.pressroom.news/v1/people/174/quotes", {
  headers: { "X-API-Key": PRESSROOM_KEY }
});
const data = await res.json();

Resolve an ambiguous surface to candidates GET /v1/{kind}/disambiguate

Real candidate ranking for an ambiguous surface form: matched against canonical names, alias tables, and whole-token containment; ranked by resolution then coverage, each with a one-line distinguishing context.

Returns up to 10 candidates, best first.

Parameters

ParameterTypeRequiredDefaultDescription
kind pathstringrequiredEntity kind: people | organisations | locations.
surface querystringrequiredThe ambiguous surface form to resolve (e.g. 'Modi', 'Congress').

Response attributes

AttributeTypeDescription
dataarray of DisambiguationCandidateThe endpoint's payload.
data[].idinteger | nullCanonical entity id of the candidate.
data[].namestring | nullCanonical name of the candidate.
data[].wikidata_idstring | nullWikidata QID when resolved.
data[].resolvedboolean | nullTrue when identity-resolved.
data[].statusstring | nullIdentity-resolution status.
data[].article_countinteger | nullDistinct articles mentioning the candidate.
data[].contextstring | nullOne-line human hint distinguishing same-named candidates (title + employer, org type + country, admin region).
data[].matchstring | nullHow the surface matched: exact_name | alias | name_token.
metaMetaBlock | nullResponse metadata: compute time, pagination, endpoint extras.
meta.generated_atstring | nullUTC timestamp when the server computed this response — use it to honestly label cached/stale data ('as of …').
meta.totalinteger | nullTotal matching items across all pages.
meta.pageinteger | nullCurrent page number (1-based).
meta.page_sizeinteger | nullItems per page.
meta.windowWindow | nullThe resolved date window the data covers.
meta.window.fromstring | nullWindow start date (ISO YYYY-MM-DD, inclusive).
meta.window.tostring | nullWindow end date (ISO YYYY-MM-DD, inclusive).
meta.next_cursorinteger | nullCursor pagination (present only when the request passed starting_after): the last id in this page — pass it as the next request's starting_after. null means the list is exhausted (has_more == next_cursor is not null).
GET/v1/{kind}/disambiguate
curl -H "X-API-Key: $PRESSROOM_KEY" \
  "https://api.pressroom.news/v1/people/disambiguate?surface=example"
import requests

r = requests.get(
    "https://api.pressroom.news/v1/people/disambiguate",
    headers={"X-API-Key": PRESSROOM_KEY},
    params={"surface": "example"},
)
print(r.json())
const res = await fetch("https://api.pressroom.news/v1/people/disambiguate?surface=example", {
  headers: { "X-API-Key": PRESSROOM_KEY }
});
const data = await res.json();

Canonical entity profile GET /v1/{kind}/{eid}

The canonical profile: identity resolution (Wikidata QID / LEI / geo status), per-kind attributes, aliases, and corpus coverage stats including aboutness centrality.

Returns the EntityProfile object.

Parameters

ParameterTypeRequiredDefaultDescription
kind pathstringrequiredEntity kind: people | organisations | locations.
eid pathintegerrequiredCanonical entity id within the kind.

Response attributes

AttributeTypeDescription
dataEntityProfileThe endpoint's payload.
data.objectstring (enum) | nullObject type discriminator: person | organisation | location (the singular of the requested kind).
data.idinteger | nullCanonical entity id (scoped to its kind).
data.kindstring | nullEntity kind: people | organisations | locations.
data.namestring | nullCanonical display name.
data.identityEntityIdentity | nullIdentity-resolution block.
data.identity.resolvedboolean | nullTrue when bound to an external identity (Wikidata QID, LEI, or a resolved geo record).
data.identity.wikidata_idstring | nullWikidata QID (null when unresolved or identity-suppressed).
data.identity.lei_codestring | nullLegal Entity Identifier (organisations only).
data.identity.statusstring | nullResolution status from the identity pipeline (resolved | not_found | pending | …).
data.attributesobject | nullPer-kind attributes: people carry title/employer/bio/social links; organisations carry type/sector/country/website; locations carry type/coordinates/admin region/population.
data.aliasesarray of any | nullKnown aliases and alternate surfaces.
data.statsEntityStats | nullCorpus coverage statistics.
data.stats.article_countinteger | nullDistinct articles mentioning the entity.
data.stats.mention_countinteger | number | nullTotal mentions across all articles.
data.stats.aboutness_totalinteger | number | nullSum of per-article aboutness weight (a headline subject weighs ~5x a body name-drop) — the centrality signal.
data.stats.subject_article_countinteger | nullArticles that are ABOUT the entity (aboutness weight >= 2.0), vs merely mentioning it.
data.stats.first_seenstring | nullEarliest publication date mentioning the entity.
data.stats.last_seenstring | nullLatest publication date mentioning the entity.
metaMetaBlock | nullResponse metadata: compute time, pagination, endpoint extras.
meta.generated_atstring | nullUTC timestamp when the server computed this response — use it to honestly label cached/stale data ('as of …').
meta.totalinteger | nullTotal matching items across all pages.
meta.pageinteger | nullCurrent page number (1-based).
meta.page_sizeinteger | nullItems per page.
meta.windowWindow | nullThe resolved date window the data covers.
meta.window.fromstring | nullWindow start date (ISO YYYY-MM-DD, inclusive).
meta.window.tostring | nullWindow end date (ISO YYYY-MM-DD, inclusive).
meta.next_cursorinteger | nullCursor pagination (present only when the request passed starting_after): the last id in this page — pass it as the next request's starting_after. null means the list is exhausted (has_more == next_cursor is not null).
GET/v1/{kind}/{eid}
curl -H "X-API-Key: $PRESSROOM_KEY" \
  "https://api.pressroom.news/v1/people/174"
import requests

r = requests.get(
    "https://api.pressroom.news/v1/people/174",
    headers={"X-API-Key": PRESSROOM_KEY},
)
print(r.json())
const res = await fetch("https://api.pressroom.news/v1/people/174", {
  headers: { "X-API-Key": PRESSROOM_KEY }
});
const data = await res.json();

Articles mentioning the entity GET /v1/{kind}/{eid}/articles

Paginated articles mentioning the entity, newest first, each with mention count and provenance (locations sharing a QID are collapsed).

Returns the article list with pagination meta.

Parameters

ParameterTypeRequiredDefaultDescription
kind pathstringrequiredEntity kind: people | organisations | locations.
eid pathintegerrequiredCanonical entity id within the kind.
starting_after queryinteger | nulloptionalOptional cursor pagination: only articles with id strictly below this article id, ordered id-descending (the default newest-first date order is not id-monotone, so cursor requests switch to id order to stay sound). page/page_size keep working unchanged without it. When passed, meta.next_cursor carries the last id of the page (null = exhausted; has_more == next_cursor is not null). Start a walk with any id above the newest article.
page queryintegeroptional1Page number (1-based).
page_size queryintegeroptional50Items per page (default 50, max 200).

Response attributes

AttributeTypeDescription
dataarray of ArticleItemThe endpoint's payload.
data[].article_idinteger | nullCorpus article id.
data[].headlinestring | nullArticle headline.
data[].dekstring | nullArticle dek / standfirst, when extracted.
data[].languagestring | nullDetected article language (ISO code).
data[].mention_countinteger | nullHow many times the queried entity is mentioned in this article.
data[].provenanceProvenance | nullProvenance of the article (source, URL, extraction record).
data[].provenance.article_idinteger | nullCorpus article id the claim came from.
data[].provenance.headlinestring | nullHeadline of the source article.
data[].provenance.source_idstring | nullRegistry id of the publishing source.
data[].provenance.source_namestring | nullDisplay name of the publishing source.
data[].provenance.source_urlstring | nullCanonical URL of the source article.
data[].provenance.extraction_methodstring | nullExtractor that captured the article (trafilatura, playwright, …).
data[].provenance.extracted_atstring | nullUTC timestamp of extraction.
data[].provenance.date_published_utcstring | nullPublisher-stated publication time (UTC).
data[].provenance.word_countinteger | nullExtracted body word count.
metaMetaBlock | nullResponse metadata: compute time, pagination, endpoint extras.
meta.generated_atstring | nullUTC timestamp when the server computed this response — use it to honestly label cached/stale data ('as of …').
meta.totalinteger | nullTotal matching items across all pages.
meta.pageinteger | nullCurrent page number (1-based).
meta.page_sizeinteger | nullItems per page.
meta.windowWindow | nullThe resolved date window the data covers.
meta.window.fromstring | nullWindow start date (ISO YYYY-MM-DD, inclusive).
meta.window.tostring | nullWindow end date (ISO YYYY-MM-DD, inclusive).
meta.next_cursorinteger | nullCursor pagination (present only when the request passed starting_after): the last id in this page — pass it as the next request's starting_after. null means the list is exhausted (has_more == next_cursor is not null).
GET/v1/{kind}/{eid}/articles
curl -H "X-API-Key: $PRESSROOM_KEY" \
  "https://api.pressroom.news/v1/people/174/articles"
import requests

r = requests.get(
    "https://api.pressroom.news/v1/people/174/articles",
    headers={"X-API-Key": PRESSROOM_KEY},
)
print(r.json())
const res = await fetch("https://api.pressroom.news/v1/people/174/articles", {
  headers: { "X-API-Key": PRESSROOM_KEY }
});
const data = await res.json();

Mention timeseries + breakdowns GET /v1/{kind}/{eid}/coverage

Distinct-article counts for the entity per time bucket with source / geography / language breakdowns (locations sharing a QID are collapsed).

Returns the coverage series and breakdowns for the window.

Parameters

ParameterTypeRequiredDefaultDescription
kind pathstringrequiredEntity kind: people | organisations | locations.
eid pathintegerrequiredCanonical entity id within the kind.
date_from querystring | nulloptionalWindow start, ISO YYYY-MM-DD (default: 90 days before date_to; max window 1825 days).
date_to querystring | nulloptionalWindow end, ISO YYYY-MM-DD (default: today, UTC).
bucket querystringoptional"day"Bucket granularity: day | week | month (default day).

Response attributes

AttributeTypeDescription
dataEntityCoverageThe endpoint's payload.
data.entity_idinteger | nullThe queried entity id.
data.kindstring | nullEntity kind.
data.bucketstring | nullBucket granularity: day | week | month.
data.windowWindow | nullResolved date window.
data.window.fromstring | nullWindow start date (ISO YYYY-MM-DD, inclusive).
data.window.tostring | nullWindow end date (ISO YYYY-MM-DD, inclusive).
data.total_articlesinteger | nullDistinct articles in the window.
data.seriesarray of SeriesBucket | nullArticle counts per time bucket.
data.series[].bucketstring | nullBucket key (YYYY-MM-DD day, YYYY-Www week, or YYYY-MM month).
data.series[].articlesinteger | nullDistinct articles in the bucket.
data.series[].mentionsinteger | number | nullTotal mentions in the bucket (event coverage only).
data.by_sourcearray of SourceBreakdown | nullBreakdown by publishing source.
data.by_source[].sourcestring | nullSource display name.
data.by_source[].articlesinteger | nullDistinct articles from this source in the window.
data.by_geographyarray of GeographyBreakdown | nullBreakdown by source geography.
data.by_geography[].geographystring | nullSource primary geography.
data.by_geography[].articlesinteger | nullDistinct articles from sources in this geography.
data.by_languagearray of LanguageBreakdown | nullBreakdown by article language.
data.by_language[].languagestring | nullArticle language (ISO code).
data.by_language[].articlesinteger | nullDistinct articles in this language.
metaMetaBlock | nullResponse metadata: compute time, pagination, endpoint extras.
meta.generated_atstring | nullUTC timestamp when the server computed this response — use it to honestly label cached/stale data ('as of …').
meta.totalinteger | nullTotal matching items across all pages.
meta.pageinteger | nullCurrent page number (1-based).
meta.page_sizeinteger | nullItems per page.
meta.windowWindow | nullThe resolved date window the data covers.
meta.window.fromstring | nullWindow start date (ISO YYYY-MM-DD, inclusive).
meta.window.tostring | nullWindow end date (ISO YYYY-MM-DD, inclusive).
meta.next_cursorinteger | nullCursor pagination (present only when the request passed starting_after): the last id in this page — pass it as the next request's starting_after. null means the list is exhausted (has_more == next_cursor is not null).
GET/v1/{kind}/{eid}/coverage
curl -H "X-API-Key: $PRESSROOM_KEY" \
  "https://api.pressroom.news/v1/people/174/coverage"
import requests

r = requests.get(
    "https://api.pressroom.news/v1/people/174/coverage",
    headers={"X-API-Key": PRESSROOM_KEY},
)
print(r.json())
const res = await fetch("https://api.pressroom.news/v1/people/174/coverage", {
  headers: { "X-API-Key": PRESSROOM_KEY }
});
const data = await res.json();

Identity-change history (from the change journals) GET /v1/{kind}/{eid}/history

How the entity's identity changed over time, merged chronologically from the change journals (audit_decisions + org cascade / location recovery logs).

Returns the change events with before/after state.

Parameters

ParameterTypeRequiredDefaultDescription
kind pathstringrequiredEntity kind: people | organisations | locations.
eid pathintegerrequiredCanonical entity id within the kind.

Response attributes

AttributeTypeDescription
dataEntityHistoryThe endpoint's payload.
data.entity_idinteger | nullThe queried entity id.
data.kindstring | nullEntity kind.
data.countinteger | nullNumber of history events.
data.eventsarray of HistoryEvent | nullChronological change events.
data.events[].tsstring | nullWhen the change was recorded (UTC).
data.events[].sourcestring | nullJournal of record: audit_decisions | org_cascade_log | location_recovery_log.
data.events[].actionstring | nullWhat happened (action taken / stage:outcome / status transition).
data.events[].beforeanyState before the change (journal-specific JSON).
data.events[].afteranyState after the change (journal-specific JSON).
data.notestring | nullScope of the journals backing this history.
metaMetaBlock | nullResponse metadata: compute time, pagination, endpoint extras.
meta.generated_atstring | nullUTC timestamp when the server computed this response — use it to honestly label cached/stale data ('as of …').
meta.totalinteger | nullTotal matching items across all pages.
meta.pageinteger | nullCurrent page number (1-based).
meta.page_sizeinteger | nullItems per page.
meta.windowWindow | nullThe resolved date window the data covers.
meta.window.fromstring | nullWindow start date (ISO YYYY-MM-DD, inclusive).
meta.window.tostring | nullWindow end date (ISO YYYY-MM-DD, inclusive).
meta.next_cursorinteger | nullCursor pagination (present only when the request passed starting_after): the last id in this page — pass it as the next request's starting_after. null means the list is exhausted (has_more == next_cursor is not null).
GET/v1/{kind}/{eid}/history
curl -H "X-API-Key: $PRESSROOM_KEY" \
  "https://api.pressroom.news/v1/people/174/history"
import requests

r = requests.get(
    "https://api.pressroom.news/v1/people/174/history",
    headers={"X-API-Key": PRESSROOM_KEY},
)
print(r.json())
const res = await fetch("https://api.pressroom.news/v1/people/174/history", {
  headers: { "X-API-Key": PRESSROOM_KEY }
});
const data = await res.json();

Deterministic sentiment over time GET /v1/{kind}/{eid}/sentiment-trend

Lexicon-based (deterministic, no LLM) sentiment of the entity's coverage per bucket, plus window aggregates.

Returns the sentiment series and overall counts.

Parameters

ParameterTypeRequiredDefaultDescription
kind pathstringrequiredEntity kind: people | organisations | locations.
eid pathintegerrequiredCanonical entity id within the kind.
date_from querystring | nulloptionalWindow start, ISO YYYY-MM-DD (default: 90 days before date_to; max window 1825 days).
date_to querystring | nulloptionalWindow end, ISO YYYY-MM-DD (default: today, UTC).
bucket querystringoptional"month"Bucket granularity: day | week | month (default month).

Response attributes

AttributeTypeDescription
dataSentimentTrendThe endpoint's payload.
data.entity_idinteger | nullThe queried entity id.
data.kindstring | nullEntity kind.
data.bucketstring | nullBucket granularity: day | week | month.
data.windowWindow | nullResolved date window.
data.window.fromstring | nullWindow start date (ISO YYYY-MM-DD, inclusive).
data.window.tostring | nullWindow end date (ISO YYYY-MM-DD, inclusive).
data.overallobject | nullWindow aggregate: avg_compound, scored_articles, positive/negative/neutral counts.
data.seriesarray of object | nullPer-bucket sentiment aggregates.
metaMetaBlock | nullResponse metadata: compute time, pagination, endpoint extras.
meta.generated_atstring | nullUTC timestamp when the server computed this response — use it to honestly label cached/stale data ('as of …').
meta.totalinteger | nullTotal matching items across all pages.
meta.pageinteger | nullCurrent page number (1-based).
meta.page_sizeinteger | nullItems per page.
meta.windowWindow | nullThe resolved date window the data covers.
meta.window.fromstring | nullWindow start date (ISO YYYY-MM-DD, inclusive).
meta.window.tostring | nullWindow end date (ISO YYYY-MM-DD, inclusive).
meta.next_cursorinteger | nullCursor pagination (present only when the request passed starting_after): the last id in this page — pass it as the next request's starting_after. null means the list is exhausted (has_more == next_cursor is not null).
GET/v1/{kind}/{eid}/sentiment-trend
curl -H "X-API-Key: $PRESSROOM_KEY" \
  "https://api.pressroom.news/v1/people/174/sentiment-trend"
import requests

r = requests.get(
    "https://api.pressroom.news/v1/people/174/sentiment-trend",
    headers={"X-API-Key": PRESSROOM_KEY},
)
print(r.json())
const res = await fetch("https://api.pressroom.news/v1/people/174/sentiment-trend", {
  headers: { "X-API-Key": PRESSROOM_KEY }
});
const data = await res.json();

Relationships

Top co-mentioned people / organisations / locations GET /v1/{kind}/{eid}/relationships

The entity's co-occurrence neighbourhood: for each requested target kind, the entities most often sharing articles with it, with co-counts, salience, and first/last co-coverage dates, plus asserted typed edges from the Wikidata-grounded graph.

Returns a Relationships object keyed by target kind.

Parameters

ParameterTypeRequiredDefaultDescription
kind pathstringrequiredEntity kind: people | organisations | locations.
eid pathintegerrequiredCanonical entity id within the kind.
target querystringoptional"people,organisations,locations"Comma-separated target kinds to include (any of people, organisations, locations).
limit queryintegeroptional20Max related entities per target kind (default 20, max 100).

Response attributes

AttributeTypeDescription
dataRelationshipsThe endpoint's payload.
data.entity_idinteger | nullThe source entity id.
data.kindstring | nullSource entity kind.
data.source_article_countinteger | nullArticles mentioning the source entity (the salience denominator).
data.peoplearray of RelatedEntity | nullTop co-mentioned people.
data.people[].idinteger | nullCanonical entity id.
data.people[].namestring | nullCanonical name.
data.people[].wikidata_idstring | nullWikidata QID when resolved.
data.people[].co_countinteger | nullDistinct articles shared with the source entity.
data.people[].first_seenstring | nullEarliest shared article date.
data.people[].last_seenstring | nullLatest shared article date.
data.people[].salienceinteger | number | nullco_count / source entity's article count — how central this relationship is to the source's coverage.
data.people[].possible_duplicateboolean | nullSet when this row looks like an unresolved stub of another entity in the list (ranked last).
data.organisationsarray of RelatedEntity | nullTop co-mentioned organisations.
data.organisations[].idinteger | nullCanonical entity id.
data.organisations[].namestring | nullCanonical name.
data.organisations[].wikidata_idstring | nullWikidata QID when resolved.
data.organisations[].co_countinteger | nullDistinct articles shared with the source entity.
data.organisations[].first_seenstring | nullEarliest shared article date.
data.organisations[].last_seenstring | nullLatest shared article date.
data.organisations[].salienceinteger | number | nullco_count / source entity's article count — how central this relationship is to the source's coverage.
data.organisations[].possible_duplicateboolean | nullSet when this row looks like an unresolved stub of another entity in the list (ranked last).
data.locationsarray of RelatedEntity | nullTop co-mentioned locations (QID-deduped).
data.locations[].idinteger | nullCanonical entity id.
data.locations[].namestring | nullCanonical name.
data.locations[].wikidata_idstring | nullWikidata QID when resolved.
data.locations[].co_countinteger | nullDistinct articles shared with the source entity.
data.locations[].first_seenstring | nullEarliest shared article date.
data.locations[].last_seenstring | nullLatest shared article date.
data.locations[].salienceinteger | number | nullco_count / source entity's article count — how central this relationship is to the source's coverage.
data.locations[].possible_duplicateboolean | nullSet when this row looks like an unresolved stub of another entity in the list (ranked last).
data.typedarray of object | nullAsserted TYPED relationships from entity_edges (Wikidata-grounded), both directions: {direction, predicate, predicate_qid, confidence, method, target|source}.
metaMetaBlock | nullResponse metadata: compute time, pagination, endpoint extras.
meta.generated_atstring | nullUTC timestamp when the server computed this response — use it to honestly label cached/stale data ('as of …').
meta.totalinteger | nullTotal matching items across all pages.
meta.pageinteger | nullCurrent page number (1-based).
meta.page_sizeinteger | nullItems per page.
meta.windowWindow | nullThe resolved date window the data covers.
meta.window.fromstring | nullWindow start date (ISO YYYY-MM-DD, inclusive).
meta.window.tostring | nullWindow end date (ISO YYYY-MM-DD, inclusive).
meta.next_cursorinteger | nullCursor pagination (present only when the request passed starting_after): the last id in this page — pass it as the next request's starting_after. null means the list is exhausted (has_more == next_cursor is not null).
GET/v1/{kind}/{eid}/relationships
curl -H "X-API-Key: $PRESSROOM_KEY" \
  "https://api.pressroom.news/v1/people/174/relationships"
import requests

r = requests.get(
    "https://api.pressroom.news/v1/people/174/relationships",
    headers={"X-API-Key": PRESSROOM_KEY},
)
print(r.json())
const res = await fetch("https://api.pressroom.news/v1/people/174/relationships", {
  headers: { "X-API-Key": PRESSROOM_KEY }
});
const data = await res.json();

Relationship graph

All LITIGATION edges for an entity, dated, with assertion_status GET /v1/graph/entities/{entity_id}/litigation

The entity's LITIGATION edges with dates and assertion_status (asserted vs alleged — the allegation firewall).

Returns the entity plus the paginated edge list.

Parameters

ParameterTypeRequiredDefaultDescription
entity_id pathintegerrequiredCanonical entity id.
page queryintegeroptional1Page number (1-based).
page_size queryintegeroptional50Items per page (default 50, max 200).

Response attributes

AttributeTypeDescription
dataGraphNeighborsThe endpoint's payload.
data.entityGraphEntity | nullThe queried entity.
data.entity.idinteger | nullCanonical entity id.
data.entity.kindstring | nullEntity kind.
data.entity.namestring | nullCanonical name (canonical).
data.entity.labelstring | nullDeprecated alias of `name`; will be removed in the next API version.
data.edgesarray of GraphEdge | nullEdges touching the entity (in + out).
data.edges[].edge_typestring | nullTyped relation (EMPLOYMENT, OWNERSHIP, LITIGATION, KINSHIP, …).
data.edges[].src_entity_idinteger | nullSource entity id.
data.edges[].dst_entity_idinteger | nullDestination entity id.
data.edges[].confidenceinteger | number | nullGate-passed extraction confidence.
data.edges[].assertion_statusstring | nullAssertion status (asserted vs alleged — the allegation firewall).
data.edges[].is_currentanyWhether the relation is flagged current.
data.edges[].valid_fromstring | nullTemporal validity start, when stated.
data.edges[].valid_tostring | nullTemporal validity end, when stated.
data.edges[].evidenceanyEvidence spans (article + sentence + char-span).
metaMetaBlock | nullResponse metadata: compute time, pagination, endpoint extras.
meta.generated_atstring | nullUTC timestamp when the server computed this response — use it to honestly label cached/stale data ('as of …').
meta.totalinteger | nullTotal matching items across all pages.
meta.pageinteger | nullCurrent page number (1-based).
meta.page_sizeinteger | nullItems per page.
meta.windowWindow | nullThe resolved date window the data covers.
meta.window.fromstring | nullWindow start date (ISO YYYY-MM-DD, inclusive).
meta.window.tostring | nullWindow end date (ISO YYYY-MM-DD, inclusive).
meta.next_cursorinteger | nullCursor pagination (present only when the request passed starting_after): the last id in this page — pass it as the next request's starting_after. null means the list is exhausted (has_more == next_cursor is not null).
GET/v1/graph/entities/{entity_id}/litigation
curl -H "X-API-Key: $PRESSROOM_KEY" \
  "https://api.pressroom.news/v1/graph/entities/174/litigation"
import requests

r = requests.get(
    "https://api.pressroom.news/v1/graph/entities/174/litigation",
    headers={"X-API-Key": PRESSROOM_KEY},
)
print(r.json())
const res = await fetch("https://api.pressroom.news/v1/graph/entities/174/litigation", {
  headers: { "X-API-Key": PRESSROOM_KEY }
});
const data = await res.json();

All typed edges touching an entity (in + out), provenance-cited GET /v1/graph/entities/{entity_id}/neighbors

Every gate-passed typed edge touching the entity, both directions, each with evidence spans and confidence.

Returns the entity plus its paginated edge list.

Parameters

ParameterTypeRequiredDefaultDescription
entity_id pathintegerrequiredCanonical entity id.
current_only querybooleanoptionalfalseonly edges flagged is_current
page queryintegeroptional1Page number (1-based).
page_size queryintegeroptional50Items per page (default 50, max 200).

Response attributes

AttributeTypeDescription
dataGraphNeighborsThe endpoint's payload.
data.entityGraphEntity | nullThe queried entity.
data.entity.idinteger | nullCanonical entity id.
data.entity.kindstring | nullEntity kind.
data.entity.namestring | nullCanonical name (canonical).
data.entity.labelstring | nullDeprecated alias of `name`; will be removed in the next API version.
data.edgesarray of GraphEdge | nullEdges touching the entity (in + out).
data.edges[].edge_typestring | nullTyped relation (EMPLOYMENT, OWNERSHIP, LITIGATION, KINSHIP, …).
data.edges[].src_entity_idinteger | nullSource entity id.
data.edges[].dst_entity_idinteger | nullDestination entity id.
data.edges[].confidenceinteger | number | nullGate-passed extraction confidence.
data.edges[].assertion_statusstring | nullAssertion status (asserted vs alleged — the allegation firewall).
data.edges[].is_currentanyWhether the relation is flagged current.
data.edges[].valid_fromstring | nullTemporal validity start, when stated.
data.edges[].valid_tostring | nullTemporal validity end, when stated.
data.edges[].evidenceanyEvidence spans (article + sentence + char-span).
metaMetaBlock | nullResponse metadata: compute time, pagination, endpoint extras.
meta.generated_atstring | nullUTC timestamp when the server computed this response — use it to honestly label cached/stale data ('as of …').
meta.totalinteger | nullTotal matching items across all pages.
meta.pageinteger | nullCurrent page number (1-based).
meta.page_sizeinteger | nullItems per page.
meta.windowWindow | nullThe resolved date window the data covers.
meta.window.fromstring | nullWindow start date (ISO YYYY-MM-DD, inclusive).
meta.window.tostring | nullWindow end date (ISO YYYY-MM-DD, inclusive).
meta.next_cursorinteger | nullCursor pagination (present only when the request passed starting_after): the last id in this page — pass it as the next request's starting_after. null means the list is exhausted (has_more == next_cursor is not null).
GET/v1/graph/entities/{entity_id}/neighbors
curl -H "X-API-Key: $PRESSROOM_KEY" \
  "https://api.pressroom.news/v1/graph/entities/174/neighbors"
import requests

r = requests.get(
    "https://api.pressroom.news/v1/graph/entities/174/neighbors",
    headers={"X-API-Key": PRESSROOM_KEY},
)
print(r.json())
const res = await fetch("https://api.pressroom.news/v1/graph/entities/174/neighbors", {
  headers: { "X-API-Key": PRESSROOM_KEY }
});
const data = await res.json();

Recursive OWNERSHIP closure up (owners) + down (holdings) GET /v1/graph/entities/{entity_id}/ownership

Walks OWNERSHIP edges recursively in both directions up to max_depth.

Returns the ownership tree: upward owners and downward holdings.

Parameters

ParameterTypeRequiredDefaultDescription
entity_id pathintegerrequiredCanonical entity id.
max_depth queryintegeroptional5Maximum recursion depth (default 5, max 8).

Response attributes

AttributeTypeDescription
dataOwnershipTreeThe endpoint's payload.
data.entityGraphEntity | nullThe queried entity.
data.entity.idinteger | nullCanonical entity id.
data.entity.kindstring | nullEntity kind.
data.entity.namestring | nullCanonical name (canonical).
data.entity.labelstring | nullDeprecated alias of `name`; will be removed in the next API version.
data.ownersarray of any | nullUpward ownership chain (who owns it).
data.holdingsarray of any | nullDownward closure (what it owns).
metaMetaBlock | nullResponse metadata: compute time, pagination, endpoint extras.
meta.generated_atstring | nullUTC timestamp when the server computed this response — use it to honestly label cached/stale data ('as of …').
meta.totalinteger | nullTotal matching items across all pages.
meta.pageinteger | nullCurrent page number (1-based).
meta.page_sizeinteger | nullItems per page.
meta.windowWindow | nullThe resolved date window the data covers.
meta.window.fromstring | nullWindow start date (ISO YYYY-MM-DD, inclusive).
meta.window.tostring | nullWindow end date (ISO YYYY-MM-DD, inclusive).
meta.next_cursorinteger | nullCursor pagination (present only when the request passed starting_after): the last id in this page — pass it as the next request's starting_after. null means the list is exhausted (has_more == next_cursor is not null).
GET/v1/graph/entities/{entity_id}/ownership
curl -H "X-API-Key: $PRESSROOM_KEY" \
  "https://api.pressroom.news/v1/graph/entities/174/ownership"
import requests

r = requests.get(
    "https://api.pressroom.news/v1/graph/entities/174/ownership",
    headers={"X-API-Key": PRESSROOM_KEY},
)
print(r.json())
const res = await fetch("https://api.pressroom.news/v1/graph/entities/174/ownership", {
  headers: { "X-API-Key": PRESSROOM_KEY }
});
const data = await res.json();

An entity's typed edges in chronological order (relationship evolution) GET /v1/graph/entities/{entity_id}/timeline

The entity's typed edges ordered by temporal validity (valid_from / as_of ascending) — how its relationships evolved.

Returns the entity plus the ordered edge list.

Parameters

ParameterTypeRequiredDefaultDescription
entity_id pathintegerrequiredCanonical entity id.
page queryintegeroptional1Page number (1-based).
page_size queryintegeroptional50Items per page (default 50, max 200).

Response attributes

AttributeTypeDescription
dataGraphNeighborsThe endpoint's payload.
data.entityGraphEntity | nullThe queried entity.
data.entity.idinteger | nullCanonical entity id.
data.entity.kindstring | nullEntity kind.
data.entity.namestring | nullCanonical name (canonical).
data.entity.labelstring | nullDeprecated alias of `name`; will be removed in the next API version.
data.edgesarray of GraphEdge | nullEdges touching the entity (in + out).
data.edges[].edge_typestring | nullTyped relation (EMPLOYMENT, OWNERSHIP, LITIGATION, KINSHIP, …).
data.edges[].src_entity_idinteger | nullSource entity id.
data.edges[].dst_entity_idinteger | nullDestination entity id.
data.edges[].confidenceinteger | number | nullGate-passed extraction confidence.
data.edges[].assertion_statusstring | nullAssertion status (asserted vs alleged — the allegation firewall).
data.edges[].is_currentanyWhether the relation is flagged current.
data.edges[].valid_fromstring | nullTemporal validity start, when stated.
data.edges[].valid_tostring | nullTemporal validity end, when stated.
data.edges[].evidenceanyEvidence spans (article + sentence + char-span).
metaMetaBlock | nullResponse metadata: compute time, pagination, endpoint extras.
meta.generated_atstring | nullUTC timestamp when the server computed this response — use it to honestly label cached/stale data ('as of …').
meta.totalinteger | nullTotal matching items across all pages.
meta.pageinteger | nullCurrent page number (1-based).
meta.page_sizeinteger | nullItems per page.
meta.windowWindow | nullThe resolved date window the data covers.
meta.window.fromstring | nullWindow start date (ISO YYYY-MM-DD, inclusive).
meta.window.tostring | nullWindow end date (ISO YYYY-MM-DD, inclusive).
meta.next_cursorinteger | nullCursor pagination (present only when the request passed starting_after): the last id in this page — pass it as the next request's starting_after. null means the list is exhausted (has_more == next_cursor is not null).
GET/v1/graph/entities/{entity_id}/timeline
curl -H "X-API-Key: $PRESSROOM_KEY" \
  "https://api.pressroom.news/v1/graph/entities/174/timeline"
import requests

r = requests.get(
    "https://api.pressroom.news/v1/graph/entities/174/timeline",
    headers={"X-API-Key": PRESSROOM_KEY},
)
print(r.json())
const res = await fetch("https://api.pressroom.news/v1/graph/entities/174/timeline", {
  headers: { "X-API-Key": PRESSROOM_KEY }
});
const data = await res.json();

Shortest typed-relation path between two entities (bounded BFS) GET /v1/graph/path

Bounded breadth-first search over the typed graph.

Returns whether a path exists within max_depth, the hop count, and the path's edges in order.

Parameters

ParameterTypeRequiredDefaultDescription
src queryintegerrequiredcanonical entity id
dst queryintegerrequiredcanonical entity id
max_depth queryintegeroptional4Maximum BFS depth in edges (default 4, max 6).

Response attributes

AttributeTypeDescription
dataGraphPathThe endpoint's payload.
data.srcGraphEntity | nullPath source entity.
data.src.idinteger | nullCanonical entity id.
data.src.kindstring | nullEntity kind.
data.src.namestring | nullCanonical name (canonical).
data.src.labelstring | nullDeprecated alias of `name`; will be removed in the next API version.
data.dstGraphEntity | nullPath destination entity.
data.dst.idinteger | nullCanonical entity id.
data.dst.kindstring | nullEntity kind.
data.dst.namestring | nullCanonical name (canonical).
data.dst.labelstring | nullDeprecated alias of `name`; will be removed in the next API version.
data.foundboolean | nullTrue when a path exists within max_depth.
data.hopsinteger | nullPath length in edges (null when not found).
data.edgesarray of GraphEdge | nullThe path's edges in order (empty when not found).
data.edges[].edge_typestring | nullTyped relation (EMPLOYMENT, OWNERSHIP, LITIGATION, KINSHIP, …).
data.edges[].src_entity_idinteger | nullSource entity id.
data.edges[].dst_entity_idinteger | nullDestination entity id.
data.edges[].confidenceinteger | number | nullGate-passed extraction confidence.
data.edges[].assertion_statusstring | nullAssertion status (asserted vs alleged — the allegation firewall).
data.edges[].is_currentanyWhether the relation is flagged current.
data.edges[].valid_fromstring | nullTemporal validity start, when stated.
data.edges[].valid_tostring | nullTemporal validity end, when stated.
data.edges[].evidenceanyEvidence spans (article + sentence + char-span).
metaMetaBlock | nullResponse metadata: compute time, pagination, endpoint extras.
meta.generated_atstring | nullUTC timestamp when the server computed this response — use it to honestly label cached/stale data ('as of …').
meta.totalinteger | nullTotal matching items across all pages.
meta.pageinteger | nullCurrent page number (1-based).
meta.page_sizeinteger | nullItems per page.
meta.windowWindow | nullThe resolved date window the data covers.
meta.window.fromstring | nullWindow start date (ISO YYYY-MM-DD, inclusive).
meta.window.tostring | nullWindow end date (ISO YYYY-MM-DD, inclusive).
meta.next_cursorinteger | nullCursor pagination (present only when the request passed starting_after): the last id in this page — pass it as the next request's starting_after. null means the list is exhausted (has_more == next_cursor is not null).
GET/v1/graph/path
curl -H "X-API-Key: $PRESSROOM_KEY" \
  "https://api.pressroom.news/v1/graph/path?src=1&dst=1"
import requests

r = requests.get(
    "https://api.pressroom.news/v1/graph/path",
    headers={"X-API-Key": PRESSROOM_KEY},
    params={"src": 1, "dst": 1},
)
print(r.json())
const res = await fetch("https://api.pressroom.news/v1/graph/path?src=1&dst=1", {
  headers: { "X-API-Key": PRESSROOM_KEY }
});
const data = await res.json();

Geo, quotes & timelines

Geo-tagged locations within a radius GET /v1/locations/nearby

Geo-resolved locations within radius_km of a point, nearest first, with per-location coverage counts.

Returns the location list plus the query center.

Parameters

ParameterTypeRequiredDefaultDescription
lat querynumberrequiredLatitude of the search center (-90 to 90).
lon querynumberrequiredLongitude of the search center (-180 to 180).
radius_km querynumberoptional100Search radius in km (default 100, max 5000).
limit queryintegeroptional50Max locations returned (default 50, max 200).

Response attributes

AttributeTypeDescription
dataNearbyThe endpoint's payload.
data.centerobject | nullThe query point {lat, lon}.
data.radius_kminteger | number | nullSearch radius in kilometres.
data.countinteger | nullLocations returned.
data.locationsarray of NearbyLocation | nullMatching locations, nearest first.
data.locations[].idinteger | nullCanonical location id.
data.locations[].namestring | nullCanonical location name.
data.locations[].latinteger | number | nullLatitude.
data.locations[].loninteger | number | nullLongitude.
data.locations[].distance_kminteger | number | nullDistance from the query point (km).
data.locations[].article_countinteger | nullDistinct articles mentioning the location.
data.metaobject | nullSuppression/aggregation notes.
metaMetaBlock | nullResponse metadata: compute time, pagination, endpoint extras.
meta.generated_atstring | nullUTC timestamp when the server computed this response — use it to honestly label cached/stale data ('as of …').
meta.totalinteger | nullTotal matching items across all pages.
meta.pageinteger | nullCurrent page number (1-based).
meta.page_sizeinteger | nullItems per page.
meta.windowWindow | nullThe resolved date window the data covers.
meta.window.fromstring | nullWindow start date (ISO YYYY-MM-DD, inclusive).
meta.window.tostring | nullWindow end date (ISO YYYY-MM-DD, inclusive).
meta.next_cursorinteger | nullCursor pagination (present only when the request passed starting_after): the last id in this page — pass it as the next request's starting_after. null means the list is exhausted (has_more == next_cursor is not null).
GET/v1/locations/nearby
curl -H "X-API-Key: $PRESSROOM_KEY" \
  "https://api.pressroom.news/v1/locations/nearby?lat=1.0&lon=1.0"
import requests

r = requests.get(
    "https://api.pressroom.news/v1/locations/nearby",
    headers={"X-API-Key": PRESSROOM_KEY},
    params={"lat": 1.0, "lon": 1.0},
)
print(r.json())
const res = await fetch("https://api.pressroom.news/v1/locations/nearby?lat=1.0&lon=1.0", {
  headers: { "X-API-Key": PRESSROOM_KEY }
});
const data = await res.json();

Mentions aggregated into lat/lon cells GET /v1/map/heatmap

Location mentions in the window aggregated into rounded lat/lon cells for map rendering, optionally filtered by a headline/dek query.

Returns the cell grid with per-cell article counts.

Parameters

ParameterTypeRequiredDefaultDescription
date_from querystring | nulloptionalWindow start, ISO YYYY-MM-DD (default: 90 days before date_to; max window 1825 days).
date_to querystring | nulloptionalWindow end, ISO YYYY-MM-DD (default: today, UTC).
q querystring | nulloptionalOptional full-text filter over headline and dek.
precision queryintegeroptional1Cell rounding precision in decimal degrees (0-3; default 1 ≈ 11 km).
limit queryintegeroptional1000Max cells returned (default 1000, max 5000).
include_regions querybooleanoptionalfalseinclude country/admin1-level centroids in cells (default: excluded)

Response attributes

AttributeTypeDescription
dataHeatmapThe endpoint's payload.
data.windowWindow | nullResolved date window.
data.window.fromstring | nullWindow start date (ISO YYYY-MM-DD, inclusive).
data.window.tostring | nullWindow end date (ISO YYYY-MM-DD, inclusive).
data.querystring | nullThe optional headline/dek filter applied.
data.precisioninteger | nullCell rounding precision (decimal degrees).
data.cellsarray of HeatmapCell | nullAggregated map cells.
data.cells[].latinteger | number | nullCell centroid latitude.
data.cells[].loninteger | number | nullCell centroid longitude.
data.cells[].articlesinteger | nullDistinct articles mentioning locations in the cell.
data.metaobject | nullAggregation notes: region-centroid inclusion and excluded counts.
metaMetaBlock | nullResponse metadata: compute time, pagination, endpoint extras.
meta.generated_atstring | nullUTC timestamp when the server computed this response — use it to honestly label cached/stale data ('as of …').
meta.totalinteger | nullTotal matching items across all pages.
meta.pageinteger | nullCurrent page number (1-based).
meta.page_sizeinteger | nullItems per page.
meta.windowWindow | nullThe resolved date window the data covers.
meta.window.fromstring | nullWindow start date (ISO YYYY-MM-DD, inclusive).
meta.window.tostring | nullWindow end date (ISO YYYY-MM-DD, inclusive).
meta.next_cursorinteger | nullCursor pagination (present only when the request passed starting_after): the last id in this page — pass it as the next request's starting_after. null means the list is exhausted (has_more == next_cursor is not null).
GET/v1/map/heatmap
curl -H "X-API-Key: $PRESSROOM_KEY" \
  "https://api.pressroom.news/v1/map/heatmap?q=election"
import requests

r = requests.get(
    "https://api.pressroom.news/v1/map/heatmap",
    headers={"X-API-Key": PRESSROOM_KEY},
    params={"q": "election"},
)
print(r.json())
const res = await fetch("https://api.pressroom.news/v1/map/heatmap?q=election", {
  headers: { "X-API-Key": PRESSROOM_KEY }
});
const data = await res.json();

A quote with full provenance + speaker GET /v1/quotes/{quote_id}

One quote as a first-class object: verbatim text, attribution verb and confidence, the resolved speaker entity, and the source article provenance.

Returns the Quote object.

Parameters

ParameterTypeRequiredDefaultDescription
quote_id pathintegerrequiredCorpus quote id.

Response attributes

AttributeTypeDescription
dataQuoteThe endpoint's payload.
data.objectstring | nullObject type discriminator: always "quote".
data.idinteger | nullCorpus quote id (canonical).
data.quote_idinteger | nullDeprecated alias of `id`; will be removed in the next API version.
data.textstring | nullVerbatim quote text.
data.verbstring | nullAttribution verb (said, told, announced, …).
data.typestring | nullQuote type: direct | indirect.
data.confidenceinteger | number | nullExtraction/attribution confidence in [0, 1].
data.speakerSpeaker | nullResolved speaker (absent on person-scoped quote lists, where the person is the request subject).
data.speaker.kindstring | nullSpeaker entity kind: person | org | location; null when unresolved.
data.speaker.entity_idinteger | nullCanonical entity id of the speaker (per kind), when resolved.
data.speaker.person_idinteger | nullLegacy person id linkage (kept for backward compatibility).
data.speaker.namestring | nullCanonical name of the resolved speaker entity.
data.speaker.speaker_textstring | nullThe verbatim speaker surface as written in the article.
data.speaker.resolvedboolean | nullTrue when the speaker is linked to ANY canonical entity.
data.provenanceProvenance | nullSource article the quote was extracted from.
data.provenance.article_idinteger | nullCorpus article id the claim came from.
data.provenance.headlinestring | nullHeadline of the source article.
data.provenance.source_idstring | nullRegistry id of the publishing source.
data.provenance.source_namestring | nullDisplay name of the publishing source.
data.provenance.source_urlstring | nullCanonical URL of the source article.
data.provenance.extraction_methodstring | nullExtractor that captured the article (trafilatura, playwright, …).
data.provenance.extracted_atstring | nullUTC timestamp of extraction.
data.provenance.date_published_utcstring | nullPublisher-stated publication time (UTC).
data.provenance.word_countinteger | nullExtracted body word count.
metaMetaBlock | nullResponse metadata: compute time, pagination, endpoint extras.
meta.generated_atstring | nullUTC timestamp when the server computed this response — use it to honestly label cached/stale data ('as of …').
meta.totalinteger | nullTotal matching items across all pages.
meta.pageinteger | nullCurrent page number (1-based).
meta.page_sizeinteger | nullItems per page.
meta.windowWindow | nullThe resolved date window the data covers.
meta.window.fromstring | nullWindow start date (ISO YYYY-MM-DD, inclusive).
meta.window.tostring | nullWindow end date (ISO YYYY-MM-DD, inclusive).
meta.next_cursorinteger | nullCursor pagination (present only when the request passed starting_after): the last id in this page — pass it as the next request's starting_after. null means the list is exhausted (has_more == next_cursor is not null).
GET/v1/quotes/{quote_id}
curl -H "X-API-Key: $PRESSROOM_KEY" \
  "https://api.pressroom.news/v1/quotes/359746"
import requests

r = requests.get(
    "https://api.pressroom.news/v1/quotes/359746",
    headers={"X-API-Key": PRESSROOM_KEY},
)
print(r.json())
const res = await fetch("https://api.pressroom.news/v1/quotes/359746", {
  headers: { "X-API-Key": PRESSROOM_KEY }
});
const data = await res.json();

Date-anchored coverage timeline GET /v1/{kind}/{eid}/timeline

Per-day coverage of the entity across the window: article counts plus each day's representative (longest) article with provenance.

Returns the EntityTimeline object, most recent day first.

Parameters

ParameterTypeRequiredDefaultDescription
kind pathstringrequiredEntity kind: people | organisations | locations.
eid pathintegerrequiredCanonical entity id within the kind.
date_from querystring | nulloptionalWindow start, ISO YYYY-MM-DD (default: 90 days before date_to; max window 1825 days).
date_to querystring | nulloptionalWindow end, ISO YYYY-MM-DD (default: today, UTC).
limit queryintegeroptional100Max days returned (default 100, max 365).

Response attributes

AttributeTypeDescription
dataEntityTimelineThe endpoint's payload.
data.entity_idinteger | nullThe queried entity id.
data.kindstring | nullEntity kind.
data.windowWindow | nullResolved date window.
data.window.fromstring | nullWindow start date (ISO YYYY-MM-DD, inclusive).
data.window.tostring | nullWindow end date (ISO YYYY-MM-DD, inclusive).
data.daysarray of TimelineDay | nullPer-day coverage, most recent first.
data.days[].datestring | nullThe day (ISO YYYY-MM-DD).
data.days[].article_countinteger | nullDistinct articles that day.
data.days[].representativeobject | nullThe day's longest article: {article_id, headline, provenance}.
data.notestring | nullMethodological caveats.
metaMetaBlock | nullResponse metadata: compute time, pagination, endpoint extras.
meta.generated_atstring | nullUTC timestamp when the server computed this response — use it to honestly label cached/stale data ('as of …').
meta.totalinteger | nullTotal matching items across all pages.
meta.pageinteger | nullCurrent page number (1-based).
meta.page_sizeinteger | nullItems per page.
meta.windowWindow | nullThe resolved date window the data covers.
meta.window.fromstring | nullWindow start date (ISO YYYY-MM-DD, inclusive).
meta.window.tostring | nullWindow end date (ISO YYYY-MM-DD, inclusive).
meta.next_cursorinteger | nullCursor pagination (present only when the request passed starting_after): the last id in this page — pass it as the next request's starting_after. null means the list is exhausted (has_more == next_cursor is not null).
GET/v1/{kind}/{eid}/timeline
curl -H "X-API-Key: $PRESSROOM_KEY" \
  "https://api.pressroom.news/v1/people/174/timeline"
import requests

r = requests.get(
    "https://api.pressroom.news/v1/people/174/timeline",
    headers={"X-API-Key": PRESSROOM_KEY},
)
print(r.json())
const res = await fetch("https://api.pressroom.news/v1/people/174/timeline", {
  headers: { "X-API-Key": PRESSROOM_KEY }
});
const data = await res.json();

Certified depth cards

Paginated certified depth cards (story-state, PUBLISHED only) GET /v1/depth-cards

Certified, PUBLISHED-disposition depth cards minted by the story-state publisher, filterable by story unit, object class, and publication date.

Returns depth cards with full mint provenance, newest first.

Parameters

ParameterTypeRequiredDefaultDescription
story_unit_id queryinteger | nulloptionalfilter to one spine story unit
object_class querystring | nulloptionalfilter object_class (e.g. DEATH_COUNT)
base_class querystring | nulloptionalfilter coarse base_class
date_from querystring | nulloptionalpublication_date >= (ISO date/datetime)
date_to querystring | nulloptionalpublication_date <= (ISO date/datetime)
page queryintegeroptional1Page number (1-based).
page_size queryintegeroptional50Items per page (default 50, max 200).

Response attributes

AttributeTypeDescription
dataarray of DepthCardThe endpoint's payload.
data[].objectstring | nullObject type discriminator: always "depth_card".
data[].idstring | nullStable card id (canonical).
data[].card_idstring | nullDeprecated alias of `id`; will be removed in the next API version.
data[].contract_versionstring | nullCard contract version (depth_card_v1).
data[].dispositionstring | nullAlways PUBLISHED (only published cards persist).
data[].object_classstring | nullTyped object class (e.g. DEATH_COUNT).
data[].base_classstring | nullCoarse base class.
data[].claimstring | nullThe card's claim text.
data[].claimed_valueanyThe typed claimed value (number, range, …).
data[].unitstring | nullUnit of the claimed value (e.g. people).
data[].story_unit_idinteger | nullSpine story unit the card belongs to, when linked.
data[].minted_atstring | nullWhen the guarded writer minted the card.
data[].provenanceDepthCardProvenance | nullFull mint provenance.
data[].provenance.article_idsanyAll article ids backing the card.
data[].provenance.primary_article_idinteger | nullThe primary evidence article.
data[].provenance.source_namestring | nullPublishing source of the primary article.
data[].provenance.publication_datestring | nullPublication time of the primary article.
data[].provenance.license_pathstring | nullLicense record for the quoted span.
data[].provenance.spanstring | nullThe verbatim evidence span.
data[].provenance.offsetsobject | nullCharacter offsets {start, end} of the span.
data[].provenance.methodstring | nullThe mint pipeline (factory -> gate -> canary).
data[].provenance.gateDepthCardGate | nullPublish-safety gate record. See the DepthCardGate object.
data[].provenance.frozen_gate_sha256string | nullSHA-256 of the frozen gate code that admitted the card.
data[].provenance.adjudicatorstring | nullWho/what adjudicated the card (e.g. held_queue rubric).
data[].provenance.adjudicator_rationalestring | nullAdjudicator rationale, when recorded.
data[].provenance.verdict_cache_keystring | nullCache key of the adjudication verdict.
data[].provenance.cert_refstring | nullCertification run reference.
data[].provenance.content_hashstring | nullContent hash of the card payload.
metaMetaBlock | nullResponse metadata: compute time, pagination, endpoint extras.
meta.generated_atstring | nullUTC timestamp when the server computed this response — use it to honestly label cached/stale data ('as of …').
meta.totalinteger | nullTotal matching items across all pages.
meta.pageinteger | nullCurrent page number (1-based).
meta.page_sizeinteger | nullItems per page.
meta.windowWindow | nullThe resolved date window the data covers.
meta.window.fromstring | nullWindow start date (ISO YYYY-MM-DD, inclusive).
meta.window.tostring | nullWindow end date (ISO YYYY-MM-DD, inclusive).
meta.next_cursorinteger | nullCursor pagination (present only when the request passed starting_after): the last id in this page — pass it as the next request's starting_after. null means the list is exhausted (has_more == next_cursor is not null).
GET/v1/depth-cards
curl -H "X-API-Key: $PRESSROOM_KEY" \
  "https://api.pressroom.news/v1/depth-cards"
import requests

r = requests.get(
    "https://api.pressroom.news/v1/depth-cards",
    headers={"X-API-Key": PRESSROOM_KEY},
)
print(r.json())
const res = await fetch("https://api.pressroom.news/v1/depth-cards", {
  headers: { "X-API-Key": PRESSROOM_KEY }
});
const data = await res.json();

Certified depth cards for one story unit GET /v1/depth-cards/story/{story_unit_id}

All persisted depth cards scoped to a story unit — matched by story_unit_id, falling back to article membership when the mint keyed cards by article (meta.method states which route resolved).

Returns the card list with pagination meta.

Parameters

ParameterTypeRequiredDefaultDescription
story_unit_id pathintegerrequiredSpine story unit id.
page queryintegeroptional1Page number (1-based).
page_size queryintegeroptional50Items per page (default 50, max 200).

Response attributes

AttributeTypeDescription
dataarray of DepthCardThe endpoint's payload.
data[].objectstring | nullObject type discriminator: always "depth_card".
data[].idstring | nullStable card id (canonical).
data[].card_idstring | nullDeprecated alias of `id`; will be removed in the next API version.
data[].contract_versionstring | nullCard contract version (depth_card_v1).
data[].dispositionstring | nullAlways PUBLISHED (only published cards persist).
data[].object_classstring | nullTyped object class (e.g. DEATH_COUNT).
data[].base_classstring | nullCoarse base class.
data[].claimstring | nullThe card's claim text.
data[].claimed_valueanyThe typed claimed value (number, range, …).
data[].unitstring | nullUnit of the claimed value (e.g. people).
data[].story_unit_idinteger | nullSpine story unit the card belongs to, when linked.
data[].minted_atstring | nullWhen the guarded writer minted the card.
data[].provenanceDepthCardProvenance | nullFull mint provenance.
data[].provenance.article_idsanyAll article ids backing the card.
data[].provenance.primary_article_idinteger | nullThe primary evidence article.
data[].provenance.source_namestring | nullPublishing source of the primary article.
data[].provenance.publication_datestring | nullPublication time of the primary article.
data[].provenance.license_pathstring | nullLicense record for the quoted span.
data[].provenance.spanstring | nullThe verbatim evidence span.
data[].provenance.offsetsobject | nullCharacter offsets {start, end} of the span.
data[].provenance.methodstring | nullThe mint pipeline (factory -> gate -> canary).
data[].provenance.gateDepthCardGate | nullPublish-safety gate record. See the DepthCardGate object.
data[].provenance.frozen_gate_sha256string | nullSHA-256 of the frozen gate code that admitted the card.
data[].provenance.adjudicatorstring | nullWho/what adjudicated the card (e.g. held_queue rubric).
data[].provenance.adjudicator_rationalestring | nullAdjudicator rationale, when recorded.
data[].provenance.verdict_cache_keystring | nullCache key of the adjudication verdict.
data[].provenance.cert_refstring | nullCertification run reference.
data[].provenance.content_hashstring | nullContent hash of the card payload.
metaMetaBlock | nullResponse metadata: compute time, pagination, endpoint extras.
meta.generated_atstring | nullUTC timestamp when the server computed this response — use it to honestly label cached/stale data ('as of …').
meta.totalinteger | nullTotal matching items across all pages.
meta.pageinteger | nullCurrent page number (1-based).
meta.page_sizeinteger | nullItems per page.
meta.windowWindow | nullThe resolved date window the data covers.
meta.window.fromstring | nullWindow start date (ISO YYYY-MM-DD, inclusive).
meta.window.tostring | nullWindow end date (ISO YYYY-MM-DD, inclusive).
meta.next_cursorinteger | nullCursor pagination (present only when the request passed starting_after): the last id in this page — pass it as the next request's starting_after. null means the list is exhausted (has_more == next_cursor is not null).
GET/v1/depth-cards/story/{story_unit_id}
curl -H "X-API-Key: $PRESSROOM_KEY" \
  "https://api.pressroom.news/v1/depth-cards/story/su_2026_0001"
import requests

r = requests.get(
    "https://api.pressroom.news/v1/depth-cards/story/su_2026_0001",
    headers={"X-API-Key": PRESSROOM_KEY},
)
print(r.json())
const res = await fetch("https://api.pressroom.news/v1/depth-cards/story/su_2026_0001", {
  headers: { "X-API-Key": PRESSROOM_KEY }
});
const data = await res.json();

Single certified depth card by id (provenance-cited) GET /v1/depth-cards/{card_id}

One certified depth card with its complete mint provenance (article lineage, evidence span, license, gate record, adjudicator, certification reference).

Returns the DepthCard object; 404 when the id is unknown or persistence is not yet activated.

Parameters

ParameterTypeRequiredDefaultDescription
card_id pathstringrequiredStable depth-card id.

Response attributes

AttributeTypeDescription
dataDepthCardThe endpoint's payload.
data.objectstring | nullObject type discriminator: always "depth_card".
data.idstring | nullStable card id (canonical).
data.card_idstring | nullDeprecated alias of `id`; will be removed in the next API version.
data.contract_versionstring | nullCard contract version (depth_card_v1).
data.dispositionstring | nullAlways PUBLISHED (only published cards persist).
data.object_classstring | nullTyped object class (e.g. DEATH_COUNT).
data.base_classstring | nullCoarse base class.
data.claimstring | nullThe card's claim text.
data.claimed_valueanyThe typed claimed value (number, range, …).
data.unitstring | nullUnit of the claimed value (e.g. people).
data.story_unit_idinteger | nullSpine story unit the card belongs to, when linked.
data.minted_atstring | nullWhen the guarded writer minted the card.
data.provenanceDepthCardProvenance | nullFull mint provenance.
data.provenance.article_idsanyAll article ids backing the card.
data.provenance.primary_article_idinteger | nullThe primary evidence article.
data.provenance.source_namestring | nullPublishing source of the primary article.
data.provenance.publication_datestring | nullPublication time of the primary article.
data.provenance.license_pathstring | nullLicense record for the quoted span.
data.provenance.spanstring | nullThe verbatim evidence span.
data.provenance.offsetsobject | nullCharacter offsets {start, end} of the span.
data.provenance.methodstring | nullThe mint pipeline (factory -> gate -> canary).
data.provenance.gateDepthCardGate | nullPublish-safety gate record. See the DepthCardGate object.
data.provenance.frozen_gate_sha256string | nullSHA-256 of the frozen gate code that admitted the card.
data.provenance.adjudicatorstring | nullWho/what adjudicated the card (e.g. held_queue rubric).
data.provenance.adjudicator_rationalestring | nullAdjudicator rationale, when recorded.
data.provenance.verdict_cache_keystring | nullCache key of the adjudication verdict.
data.provenance.cert_refstring | nullCertification run reference.
data.provenance.content_hashstring | nullContent hash of the card payload.
metaMetaBlock | nullResponse metadata: compute time, pagination, endpoint extras.
meta.generated_atstring | nullUTC timestamp when the server computed this response — use it to honestly label cached/stale data ('as of …').
meta.totalinteger | nullTotal matching items across all pages.
meta.pageinteger | nullCurrent page number (1-based).
meta.page_sizeinteger | nullItems per page.
meta.windowWindow | nullThe resolved date window the data covers.
meta.window.fromstring | nullWindow start date (ISO YYYY-MM-DD, inclusive).
meta.window.tostring | nullWindow end date (ISO YYYY-MM-DD, inclusive).
meta.next_cursorinteger | nullCursor pagination (present only when the request passed starting_after): the last id in this page — pass it as the next request's starting_after. null means the list is exhausted (has_more == next_cursor is not null).
GET/v1/depth-cards/{card_id}
curl -H "X-API-Key: $PRESSROOM_KEY" \
  "https://api.pressroom.news/v1/depth-cards/cc_arrest_count_ca89a2c654546c73"
import requests

r = requests.get(
    "https://api.pressroom.news/v1/depth-cards/cc_arrest_count_ca89a2c654546c73",
    headers={"X-API-Key": PRESSROOM_KEY},
)
print(r.json())
const res = await fetch("https://api.pressroom.news/v1/depth-cards/cc_arrest_count_ca89a2c654546c73", {
  headers: { "X-API-Key": PRESSROOM_KEY }
});
const data = await res.json();

Story units

Active story units (deterministic order, no ranking model) GET /v1/story/active

Story units still receiving coverage within the last `days`, most recently updated first — no ranking model.

Returns the unit summaries and their count.

Parameters

ParameterTypeRequiredDefaultDescription
days queryintegeroptional7Recency window in days (default 7, max 90).
limit queryintegeroptional50Max units returned (default 50, max 200).

Response attributes

AttributeTypeDescription
dataStoryActiveThe endpoint's payload.
data.unitsarray of StoryUnit | nullActive units, most recently updated first.
data.units[].objectstring | nullObject type discriminator: always "story_unit".
data.units[].idinteger | nullStory unit id (canonical).
data.units[].unit_idinteger | nullDeprecated alias of `id`; will be removed in the next API version.
data.units[].headlinestring | nullThe unit's representative headline (origin article).
data.units[].first_published_atstring | nullPublication time of the first member article.
data.units[].last_published_atstring | nullPublication time of the latest member article.
data.units[].article_countinteger | nullMember articles in the unit.
data.units[].source_countinteger | nullDistinct sources covering the unit.
data.countinteger | nullNumber of units returned.
metaMetaBlock | nullResponse metadata: compute time, pagination, endpoint extras.
meta.generated_atstring | nullUTC timestamp when the server computed this response — use it to honestly label cached/stale data ('as of …').
meta.totalinteger | nullTotal matching items across all pages.
meta.pageinteger | nullCurrent page number (1-based).
meta.page_sizeinteger | nullItems per page.
meta.windowWindow | nullThe resolved date window the data covers.
meta.window.fromstring | nullWindow start date (ISO YYYY-MM-DD, inclusive).
meta.window.tostring | nullWindow end date (ISO YYYY-MM-DD, inclusive).
meta.next_cursorinteger | nullCursor pagination (present only when the request passed starting_after): the last id in this page — pass it as the next request's starting_after. null means the list is exhausted (has_more == next_cursor is not null).
GET/v1/story/active
curl -H "X-API-Key: $PRESSROOM_KEY" \
  "https://api.pressroom.news/v1/story/active"
import requests

r = requests.get(
    "https://api.pressroom.news/v1/story/active",
    headers={"X-API-Key": PRESSROOM_KEY},
)
print(r.json())
const res = await fetch("https://api.pressroom.news/v1/story/active", {
  headers: { "X-API-Key": PRESSROOM_KEY }
});
const data = await res.json();

Second click: article → its grounded story page GET /v1/story/by-article/{article_id}

The second-click surface: resolves the article to its corroborated story unit and composes the grounded page (what happened, timeline with novelty, canonical statements, disagreement, key players — all cited). An article without a unit gets an honest article-only fallback (unit: null), never a 404.

Returns the StoryPage object.

Parameters

ParameterTypeRequiredDefaultDescription
article_id pathintegerrequiredCorpus article id (the tapped article).

Response attributes

AttributeTypeDescription
dataStoryPageThe endpoint's payload.
data.unitStoryUnit | nullThe story unit summary; null in the article-only fallback.
data.unit.objectstring | nullObject type discriminator: always "story_unit".
data.unit.idinteger | nullStory unit id (canonical).
data.unit.unit_idinteger | nullDeprecated alias of `id`; will be removed in the next API version.
data.unit.headlinestring | nullThe unit's representative headline (origin article).
data.unit.first_published_atstring | nullPublication time of the first member article.
data.unit.last_published_atstring | nullPublication time of the latest member article.
data.unit.article_countinteger | nullMember articles in the unit.
data.unit.source_countinteger | nullDistinct sources covering the unit.
data.articleobject | nullFallback only: the tapped article with source + translation-safety state.
data.what_happenedstring | nullTemplated, fully-cited narrative summary (no LLM).
data.timelinearray of object | nullMember articles in publication order with per-article novelty (novel/repeat quote counts, novel entities).
data.signalobject | nullNovel-vs-repeated statement counts and the signal ratio.
data.statementsarray of StoryStatement | nullCanonical statements (quotes grouped by verbatim identity).
data.statements[].textstring | nullVerbatim statement text.
data.statements[].speakerstring | nullResolved speaker name (or the raw speaker surface).
data.statements[].speaker_person_idinteger | nullCanonical person id when resolved.
data.statements[].first_said_ininteger | nullArticle id where the statement first appeared.
data.statements[].first_said_atstring | nullPublication time of the first appearance.
data.statements[].articlesarray of integer | nullAll member article ids carrying the statement.
data.statements[].repeat_countinteger | nullRepetitions after the first appearance.
data.statements[].stance_targetstring | nullThe issue the statement takes a position on.
data.statements[].stance_polaritystring | nullPosition polarity: support | oppose | ….
data.disagreementarray of object | nullStance targets where statements carry mixed polarity.
data.key_playersarray of object | nullAboutness-weighted entities across the unit, QID-linked.
data.citationsarray of object | nullArticle citations backing every claim on the page.
data.generation_modestring | nullAlways 'deterministic' (no LLM).
data.generated_atstring | nullServer compute-time stamp (ISSUE-278).
data.notestring | nullFallback honesty note, when unit is null.
metaMetaBlock | nullResponse metadata: compute time, pagination, endpoint extras.
meta.generated_atstring | nullUTC timestamp when the server computed this response — use it to honestly label cached/stale data ('as of …').
meta.totalinteger | nullTotal matching items across all pages.
meta.pageinteger | nullCurrent page number (1-based).
meta.page_sizeinteger | nullItems per page.
meta.windowWindow | nullThe resolved date window the data covers.
meta.window.fromstring | nullWindow start date (ISO YYYY-MM-DD, inclusive).
meta.window.tostring | nullWindow end date (ISO YYYY-MM-DD, inclusive).
meta.next_cursorinteger | nullCursor pagination (present only when the request passed starting_after): the last id in this page — pass it as the next request's starting_after. null means the list is exhausted (has_more == next_cursor is not null).
GET/v1/story/by-article/{article_id}
curl -H "X-API-Key: $PRESSROOM_KEY" \
  "https://api.pressroom.news/v1/story/by-article/186888"
import requests

r = requests.get(
    "https://api.pressroom.news/v1/story/by-article/186888",
    headers={"X-API-Key": PRESSROOM_KEY},
)
print(r.json())
const res = await fetch("https://api.pressroom.news/v1/story/by-article/186888", {
  headers: { "X-API-Key": PRESSROOM_KEY }
});
const data = await res.json();

Grounded story page by unit id GET /v1/story/units/{unit_id}

The same grounded story page, addressed by story unit id (merged units follow their merge target).

Returns the StoryPage object.

Parameters

ParameterTypeRequiredDefaultDescription
unit_id pathintegerrequiredStory unit id.

Response attributes

AttributeTypeDescription
dataStoryPageThe endpoint's payload.
data.unitStoryUnit | nullThe story unit summary; null in the article-only fallback.
data.unit.objectstring | nullObject type discriminator: always "story_unit".
data.unit.idinteger | nullStory unit id (canonical).
data.unit.unit_idinteger | nullDeprecated alias of `id`; will be removed in the next API version.
data.unit.headlinestring | nullThe unit's representative headline (origin article).
data.unit.first_published_atstring | nullPublication time of the first member article.
data.unit.last_published_atstring | nullPublication time of the latest member article.
data.unit.article_countinteger | nullMember articles in the unit.
data.unit.source_countinteger | nullDistinct sources covering the unit.
data.articleobject | nullFallback only: the tapped article with source + translation-safety state.
data.what_happenedstring | nullTemplated, fully-cited narrative summary (no LLM).
data.timelinearray of object | nullMember articles in publication order with per-article novelty (novel/repeat quote counts, novel entities).
data.signalobject | nullNovel-vs-repeated statement counts and the signal ratio.
data.statementsarray of StoryStatement | nullCanonical statements (quotes grouped by verbatim identity).
data.statements[].textstring | nullVerbatim statement text.
data.statements[].speakerstring | nullResolved speaker name (or the raw speaker surface).
data.statements[].speaker_person_idinteger | nullCanonical person id when resolved.
data.statements[].first_said_ininteger | nullArticle id where the statement first appeared.
data.statements[].first_said_atstring | nullPublication time of the first appearance.
data.statements[].articlesarray of integer | nullAll member article ids carrying the statement.
data.statements[].repeat_countinteger | nullRepetitions after the first appearance.
data.statements[].stance_targetstring | nullThe issue the statement takes a position on.
data.statements[].stance_polaritystring | nullPosition polarity: support | oppose | ….
data.disagreementarray of object | nullStance targets where statements carry mixed polarity.
data.key_playersarray of object | nullAboutness-weighted entities across the unit, QID-linked.
data.citationsarray of object | nullArticle citations backing every claim on the page.
data.generation_modestring | nullAlways 'deterministic' (no LLM).
data.generated_atstring | nullServer compute-time stamp (ISSUE-278).
data.notestring | nullFallback honesty note, when unit is null.
metaMetaBlock | nullResponse metadata: compute time, pagination, endpoint extras.
meta.generated_atstring | nullUTC timestamp when the server computed this response — use it to honestly label cached/stale data ('as of …').
meta.totalinteger | nullTotal matching items across all pages.
meta.pageinteger | nullCurrent page number (1-based).
meta.page_sizeinteger | nullItems per page.
meta.windowWindow | nullThe resolved date window the data covers.
meta.window.fromstring | nullWindow start date (ISO YYYY-MM-DD, inclusive).
meta.window.tostring | nullWindow end date (ISO YYYY-MM-DD, inclusive).
meta.next_cursorinteger | nullCursor pagination (present only when the request passed starting_after): the last id in this page — pass it as the next request's starting_after. null means the list is exhausted (has_more == next_cursor is not null).
GET/v1/story/units/{unit_id}
curl -H "X-API-Key: $PRESSROOM_KEY" \
  "https://api.pressroom.news/v1/story/units/su_2026_0001"
import requests

r = requests.get(
    "https://api.pressroom.news/v1/story/units/su_2026_0001",
    headers={"X-API-Key": PRESSROOM_KEY},
)
print(r.json())
const res = await fetch("https://api.pressroom.news/v1/story/units/su_2026_0001", {
  headers: { "X-API-Key": PRESSROOM_KEY }
});
const data = await res.json();

Stance

Deterministic stance dossier (person alias, D8) GET /v1/stance/dossier

The original person-scoped stance dossier: verbatim, attributed quotes grouped into topic sections with a stable content digest — deterministic, no LLM.

Returns the dossier with its digest in meta.

Parameters

ParameterTypeRequiredDefaultDescription
person querystringrequiredcanonical name or people.id
topic querystring | nulloptionalcomma-separated topic slugs
since querystring | nulloptionalISO date lower bound
until querystring | nulloptionalISO date upper bound
min_len queryintegeroptional40Minimum quote length in characters (default 40, range 10-400).

Response attributes

AttributeTypeDescription
dataStanceDossierThe endpoint's payload.
data.digeststring | nullContent digest of the dossier (stable for identical inputs).
data.subjectanyThe dossier subject (person / entity block).
data.personanyThe resolved person (v1 dossier).
data.sectionsanyTopic / act-family sections of cited positions.
data.positionsanyResolved-target positions with consistency, when present.
data.coverageanyCoverage stats backing the dossier.
metaMetaBlock | nullResponse metadata: compute time, pagination, endpoint extras.
meta.generated_atstring | nullUTC timestamp when the server computed this response — use it to honestly label cached/stale data ('as of …').
meta.totalinteger | nullTotal matching items across all pages.
meta.pageinteger | nullCurrent page number (1-based).
meta.page_sizeinteger | nullItems per page.
meta.windowWindow | nullThe resolved date window the data covers.
meta.window.fromstring | nullWindow start date (ISO YYYY-MM-DD, inclusive).
meta.window.tostring | nullWindow end date (ISO YYYY-MM-DD, inclusive).
meta.next_cursorinteger | nullCursor pagination (present only when the request passed starting_after): the last id in this page — pass it as the next request's starting_after. null means the list is exhausted (has_more == next_cursor is not null).
GET/v1/stance/dossier
curl -H "X-API-Key: $PRESSROOM_KEY" \
  "https://api.pressroom.news/v1/stance/dossier?person=example"
import requests

r = requests.get(
    "https://api.pressroom.news/v1/stance/dossier",
    headers={"X-API-Key": PRESSROOM_KEY},
    params={"person": "example"},
)
print(r.json())
const res = await fetch("https://api.pressroom.news/v1/stance/dossier?person=example", {
  headers: { "X-API-Key": PRESSROOM_KEY }
});
const data = await res.json();

Entity stance dossier v2 (voices + act-family sections + resolved-target positions with consistency; country = world-wide government-voice roll-up) GET /v1/{kind}/{eid}/stance

The entity's stated positions as a facet of its canonical profile: attributed voices, act-family sections, and resolved-target positions with consistency scoring; a COUNTRY location rolls up its world-wide government voices.

Returns the v2 dossier with its digest in meta.

Parameters

ParameterTypeRequiredDefaultDescription
kind pathstringrequiredEntity kind: people | organisations | locations.
eid pathintegerrequiredCanonical entity id within the kind.
topic querystring | nulloptionalcomma-separated topic slugs
since querystring | nulloptionalISO date lower bound
until querystring | nulloptionalISO date upper bound
min_len queryintegeroptional40Minimum quote length in characters (default 40, range 10-400).

Response attributes

AttributeTypeDescription
dataStanceDossierThe endpoint's payload.
data.digeststring | nullContent digest of the dossier (stable for identical inputs).
data.subjectanyThe dossier subject (person / entity block).
data.personanyThe resolved person (v1 dossier).
data.sectionsanyTopic / act-family sections of cited positions.
data.positionsanyResolved-target positions with consistency, when present.
data.coverageanyCoverage stats backing the dossier.
metaMetaBlock | nullResponse metadata: compute time, pagination, endpoint extras.
meta.generated_atstring | nullUTC timestamp when the server computed this response — use it to honestly label cached/stale data ('as of …').
meta.totalinteger | nullTotal matching items across all pages.
meta.pageinteger | nullCurrent page number (1-based).
meta.page_sizeinteger | nullItems per page.
meta.windowWindow | nullThe resolved date window the data covers.
meta.window.fromstring | nullWindow start date (ISO YYYY-MM-DD, inclusive).
meta.window.tostring | nullWindow end date (ISO YYYY-MM-DD, inclusive).
meta.next_cursorinteger | nullCursor pagination (present only when the request passed starting_after): the last id in this page — pass it as the next request's starting_after. null means the list is exhausted (has_more == next_cursor is not null).
GET/v1/{kind}/{eid}/stance
curl -H "X-API-Key: $PRESSROOM_KEY" \
  "https://api.pressroom.news/v1/people/174/stance"
import requests

r = requests.get(
    "https://api.pressroom.news/v1/people/174/stance",
    headers={"X-API-Key": PRESSROOM_KEY},
)
print(r.json())
const res = await fetch("https://api.pressroom.news/v1/people/174/stance", {
  headers: { "X-API-Key": PRESSROOM_KEY }
});
const data = await res.json();

Intelligence analyses

Entities once co-covered with X that are now absent POST /v1/analyse/blind-spot

Entities that used to share coverage with the subject but have gone quiet in the recent half of the window.

Returns the dropped co-entities with their prior and recent co-counts.

Parameters

ParameterTypeRequiredDefaultDescription
entity bodyEntityRefrequiredThe entity whose dropped co-coverage to find.
date_from bodystring | nulloptionalWindow start, ISO YYYY-MM-DD.
date_to bodystring | nulloptionalWindow end, ISO YYYY-MM-DD.

Response attributes

AttributeTypeDescription
dataAnalysisResultThe endpoint's payload.
data.subjectstring | nullThe resolved subject: 'q:<query>' or '<kind>:<id>'.
data.windowWindow | nullResolved date window.
data.window.fromstring | nullWindow start date (ISO YYYY-MM-DD, inclusive).
data.window.tostring | nullWindow end date (ISO YYYY-MM-DD, inclusive).
data.methodstring | nullExactly how the numbers were computed — every result is reproducible by an independent query.
data.interpretationstring | nullPlain-language reading of the finding, when provided.
data.reliabilitystring | nullDenominator-health grade of the underlying clusters, when relevant.
data.notestring | nullCaveats, when any.
metaMetaBlock | nullResponse metadata: compute time, pagination, endpoint extras.
meta.generated_atstring | nullUTC timestamp when the server computed this response — use it to honestly label cached/stale data ('as of …').
meta.totalinteger | nullTotal matching items across all pages.
meta.pageinteger | nullCurrent page number (1-based).
meta.page_sizeinteger | nullItems per page.
meta.windowWindow | nullThe resolved date window the data covers.
meta.window.fromstring | nullWindow start date (ISO YYYY-MM-DD, inclusive).
meta.window.tostring | nullWindow end date (ISO YYYY-MM-DD, inclusive).
meta.next_cursorinteger | nullCursor pagination (present only when the request passed starting_after): the last id in this page — pass it as the next request's starting_after. null means the list is exhausted (has_more == next_cursor is not null).
POST/v1/analyse/blind-spot
curl -X POST -H "X-API-Key: $PRESSROOM_KEY" \
  -H "Content-Type: application/json" \
  -d '{"entity": "example"}' \
  "https://api.pressroom.news/v1/analyse/blind-spot"
import requests

r = requests.post(
    "https://api.pressroom.news/v1/analyse/blind-spot",
    headers={"X-API-Key": PRESSROOM_KEY},
    json={"entity": "example"},
)
print(r.json())
const res = await fetch("https://api.pressroom.news/v1/analyse/blind-spot", {
  headers: { "X-API-Key": PRESSROOM_KEY, "Content-Type": "application/json" },
  method: "POST",
  body: JSON.stringify({"entity": "example"})
});
const data = await res.json();

Project peak / duration from the volume curve POST /v1/analyse/crisis-trajectory

Fits the subject's coverage volume curve to project the peak and expected duration.

Returns the curve, the projection, and the method.

Parameters

ParameterTypeRequiredDefaultDescription
q bodystring | nulloptionalKeyword subject (headline match). Provide q or entity.
entity bodyEntityRef | nulloptionalEntity subject. Provide q or entity.
date_from bodystring | nulloptionalWindow start, ISO YYYY-MM-DD (default: 90 days before date_to).
date_to bodystring | nulloptionalWindow end, ISO YYYY-MM-DD (default: today, UTC).

Response attributes

AttributeTypeDescription
dataAnalysisResultThe endpoint's payload.
data.subjectstring | nullThe resolved subject: 'q:<query>' or '<kind>:<id>'.
data.windowWindow | nullResolved date window.
data.window.fromstring | nullWindow start date (ISO YYYY-MM-DD, inclusive).
data.window.tostring | nullWindow end date (ISO YYYY-MM-DD, inclusive).
data.methodstring | nullExactly how the numbers were computed — every result is reproducible by an independent query.
data.interpretationstring | nullPlain-language reading of the finding, when provided.
data.reliabilitystring | nullDenominator-health grade of the underlying clusters, when relevant.
data.notestring | nullCaveats, when any.
metaMetaBlock | nullResponse metadata: compute time, pagination, endpoint extras.
meta.generated_atstring | nullUTC timestamp when the server computed this response — use it to honestly label cached/stale data ('as of …').
meta.totalinteger | nullTotal matching items across all pages.
meta.pageinteger | nullCurrent page number (1-based).
meta.page_sizeinteger | nullItems per page.
meta.windowWindow | nullThe resolved date window the data covers.
meta.window.fromstring | nullWindow start date (ISO YYYY-MM-DD, inclusive).
meta.window.tostring | nullWindow end date (ISO YYYY-MM-DD, inclusive).
meta.next_cursorinteger | nullCursor pagination (present only when the request passed starting_after): the last id in this page — pass it as the next request's starting_after. null means the list is exhausted (has_more == next_cursor is not null).
POST/v1/analyse/crisis-trajectory
curl -X POST -H "X-API-Key: $PRESSROOM_KEY" \
  -H "Content-Type: application/json" \
  -d '{"q": "election"}' \
  "https://api.pressroom.news/v1/analyse/crisis-trajectory"
import requests

r = requests.post(
    "https://api.pressroom.news/v1/analyse/crisis-trajectory",
    headers={"X-API-Key": PRESSROOM_KEY},
    json={"q": "election"},
)
print(r.json())
const res = await fetch("https://api.pressroom.news/v1/analyse/crisis-trajectory", {
  headers: { "X-API-Key": PRESSROOM_KEY, "Content-Type": "application/json" },
  method: "POST",
  body: JSON.stringify({"q": "election"})
});
const data = await res.json();

How two entities' relationship moved over a window POST /v1/analyse/entity-relationship-delta

Compares the two entities' shared-article counts across the window midpoint.

Returns co-occurrence totals per half, the delta, and its direction (strengthening / weakening / stable).

Parameters

ParameterTypeRequiredDefaultDescription
a bodyEntityRefrequiredFirst entity.
b bodyEntityRefrequiredSecond entity.
date_from bodystring | nulloptionalWindow start, ISO YYYY-MM-DD.
date_to bodystring | nulloptionalWindow end, ISO YYYY-MM-DD.

Response attributes

AttributeTypeDescription
dataRelationshipDeltaThe endpoint's payload.
data.subjectstring | nullThe resolved subject: 'q:<query>' or '<kind>:<id>'.
data.windowWindow | nullResolved date window.
data.window.fromstring | nullWindow start date (ISO YYYY-MM-DD, inclusive).
data.window.tostring | nullWindow end date (ISO YYYY-MM-DD, inclusive).
data.methodstring | nullExactly how the numbers were computed — every result is reproducible by an independent query.
data.interpretationstring | nullPlain-language reading of the finding, when provided.
data.reliabilitystring | nullDenominator-health grade of the underlying clusters, when relevant.
data.notestring | nullCaveats, when any.
data.aobject | nullEntity A reference.
data.bobject | nullEntity B reference.
data.co_totalinteger | nullShared articles across the whole window.
data.co_recent_halfinteger | nullShared articles in the recent half.
data.co_prior_halfinteger | nullShared articles in the prior half.
data.deltainteger | number | nullrecent - prior shared-article delta.
data.directionstring | nullstrengthening | weakening | stable.
metaMetaBlock | nullResponse metadata: compute time, pagination, endpoint extras.
meta.generated_atstring | nullUTC timestamp when the server computed this response — use it to honestly label cached/stale data ('as of …').
meta.totalinteger | nullTotal matching items across all pages.
meta.pageinteger | nullCurrent page number (1-based).
meta.page_sizeinteger | nullItems per page.
meta.windowWindow | nullThe resolved date window the data covers.
meta.window.fromstring | nullWindow start date (ISO YYYY-MM-DD, inclusive).
meta.window.tostring | nullWindow end date (ISO YYYY-MM-DD, inclusive).
meta.next_cursorinteger | nullCursor pagination (present only when the request passed starting_after): the last id in this page — pass it as the next request's starting_after. null means the list is exhausted (has_more == next_cursor is not null).
POST/v1/analyse/entity-relationship-delta
curl -X POST -H "X-API-Key: $PRESSROOM_KEY" \
  -H "Content-Type: application/json" \
  -d '{"a": "Adani", "b": "Reserve Bank of India"}' \
  "https://api.pressroom.news/v1/analyse/entity-relationship-delta"
import requests

r = requests.post(
    "https://api.pressroom.news/v1/analyse/entity-relationship-delta",
    headers={"X-API-Key": PRESSROOM_KEY},
    json={"a": "Adani", "b": "Reserve Bank of India"},
)
print(r.json())
const res = await fetch("https://api.pressroom.news/v1/analyse/entity-relationship-delta", {
  headers: { "X-API-Key": PRESSROOM_KEY, "Content-Type": "application/json" },
  method: "POST",
  body: JSON.stringify({"a": "Adani", "b": "Reserve Bank of India"})
});
const data = await res.json();

Corroboration vs contradiction across independent sources POST /v1/analyse/epistemic-confidence

How firmly the subject's coverage is corroborated: independent sources and countries, per-cluster corroboration, sentiment spread, contradiction.

Returns the component scores, the composite confidence, and an interpretation.

Parameters

ParameterTypeRequiredDefaultDescription
q bodystring | nulloptionalKeyword subject (headline match). Provide q or entity.
entity bodyEntityRef | nulloptionalEntity subject. Provide q or entity.
date_from bodystring | nulloptionalWindow start, ISO YYYY-MM-DD (default: 90 days before date_to).
date_to bodystring | nulloptionalWindow end, ISO YYYY-MM-DD (default: today, UTC).

Response attributes

AttributeTypeDescription
dataEpistemicConfidenceThe endpoint's payload.
data.subjectstring | nullThe resolved subject: 'q:<query>' or '<kind>:<id>'.
data.windowWindow | nullResolved date window.
data.window.fromstring | nullWindow start date (ISO YYYY-MM-DD, inclusive).
data.window.tostring | nullWindow end date (ISO YYYY-MM-DD, inclusive).
data.methodstring | nullExactly how the numbers were computed — every result is reproducible by an independent query.
data.interpretationstring | nullPlain-language reading of the finding, when provided.
data.reliabilitystring | nullDenominator-health grade of the underlying clusters, when relevant.
data.notestring | nullCaveats, when any.
data.articlesinteger | nullArticles in the result set.
data.independent_sourcesinteger | nullDistinct sources.
data.countriesinteger | nullDistinct source origin countries.
data.distinct_storiesinteger | nullDistinct near-dup story clusters.
data.corroborationinteger | number | nullCorroboration score component.
data.contradictioninteger | number | nullContradiction score component.
data.confidenceinteger | number | nullComposite epistemic-confidence score.
metaMetaBlock | nullResponse metadata: compute time, pagination, endpoint extras.
meta.generated_atstring | nullUTC timestamp when the server computed this response — use it to honestly label cached/stale data ('as of …').
meta.totalinteger | nullTotal matching items across all pages.
meta.pageinteger | nullCurrent page number (1-based).
meta.page_sizeinteger | nullItems per page.
meta.windowWindow | nullThe resolved date window the data covers.
meta.window.fromstring | nullWindow start date (ISO YYYY-MM-DD, inclusive).
meta.window.tostring | nullWindow end date (ISO YYYY-MM-DD, inclusive).
meta.next_cursorinteger | nullCursor pagination (present only when the request passed starting_after): the last id in this page — pass it as the next request's starting_after. null means the list is exhausted (has_more == next_cursor is not null).
POST/v1/analyse/epistemic-confidence
curl -X POST -H "X-API-Key: $PRESSROOM_KEY" \
  -H "Content-Type: application/json" \
  -d '{"q": "election"}' \
  "https://api.pressroom.news/v1/analyse/epistemic-confidence"
import requests

r = requests.post(
    "https://api.pressroom.news/v1/analyse/epistemic-confidence",
    headers={"X-API-Key": PRESSROOM_KEY},
    json={"q": "election"},
)
print(r.json())
const res = await fetch("https://api.pressroom.news/v1/analyse/epistemic-confidence", {
  headers: { "X-API-Key": PRESSROOM_KEY, "Content-Type": "application/json" },
  method: "POST",
  body: JSON.stringify({"q": "election"})
});
const data = await res.json();

Competing narratives across source clusters POST /v1/analyse/narrative-conflict

Contrasts how source clusters (by origin/audience taxonomy) frame the subject: volume and sentiment per cluster, divergence measures.

Returns the per-cluster framing comparison.

Parameters

ParameterTypeRequiredDefaultDescription
q bodystring | nulloptionalKeyword subject (headline match). Provide q or entity.
entity bodyEntityRef | nulloptionalEntity subject. Provide q or entity.
date_from bodystring | nulloptionalWindow start, ISO YYYY-MM-DD (default: 90 days before date_to).
date_to bodystring | nulloptionalWindow end, ISO YYYY-MM-DD (default: today, UTC).

Response attributes

AttributeTypeDescription
dataAnalysisResultThe endpoint's payload.
data.subjectstring | nullThe resolved subject: 'q:<query>' or '<kind>:<id>'.
data.windowWindow | nullResolved date window.
data.window.fromstring | nullWindow start date (ISO YYYY-MM-DD, inclusive).
data.window.tostring | nullWindow end date (ISO YYYY-MM-DD, inclusive).
data.methodstring | nullExactly how the numbers were computed — every result is reproducible by an independent query.
data.interpretationstring | nullPlain-language reading of the finding, when provided.
data.reliabilitystring | nullDenominator-health grade of the underlying clusters, when relevant.
data.notestring | nullCaveats, when any.
metaMetaBlock | nullResponse metadata: compute time, pagination, endpoint extras.
meta.generated_atstring | nullUTC timestamp when the server computed this response — use it to honestly label cached/stale data ('as of …').
meta.totalinteger | nullTotal matching items across all pages.
meta.pageinteger | nullCurrent page number (1-based).
meta.page_sizeinteger | nullItems per page.
meta.windowWindow | nullThe resolved date window the data covers.
meta.window.fromstring | nullWindow start date (ISO YYYY-MM-DD, inclusive).
meta.window.tostring | nullWindow end date (ISO YYYY-MM-DD, inclusive).
meta.next_cursorinteger | nullCursor pagination (present only when the request passed starting_after): the last id in this page — pass it as the next request's starting_after. null means the list is exhausted (has_more == next_cursor is not null).
POST/v1/analyse/narrative-conflict
curl -X POST -H "X-API-Key: $PRESSROOM_KEY" \
  -H "Content-Type: application/json" \
  -d '{"q": "election"}' \
  "https://api.pressroom.news/v1/analyse/narrative-conflict"
import requests

r = requests.post(
    "https://api.pressroom.news/v1/analyse/narrative-conflict",
    headers={"X-API-Key": PRESSROOM_KEY},
    json={"q": "election"},
)
print(r.json())
const res = await fetch("https://api.pressroom.news/v1/analyse/narrative-conflict", {
  headers: { "X-API-Key": PRESSROOM_KEY, "Content-Type": "application/json" },
  method: "POST",
  body: JSON.stringify({"q": "election"})
});
const data = await res.json();

Coverage momentum (volume/diversity/geo/sentiment derivative) POST /v1/analyse/narrative-momentum

Whether coverage of the subject is accelerating or decaying: derivatives of volume, source diversity, geographic spread, and sentiment across the window halves.

Returns the momentum components, a composite score, and the method.

Parameters

ParameterTypeRequiredDefaultDescription
q bodystring | nulloptionalKeyword subject (headline match). Provide q or entity.
entity bodyEntityRef | nulloptionalEntity subject. Provide q or entity.
date_from bodystring | nulloptionalWindow start, ISO YYYY-MM-DD (default: 90 days before date_to).
date_to bodystring | nulloptionalWindow end, ISO YYYY-MM-DD (default: today, UTC).

Response attributes

AttributeTypeDescription
dataAnalysisResultThe endpoint's payload.
data.subjectstring | nullThe resolved subject: 'q:<query>' or '<kind>:<id>'.
data.windowWindow | nullResolved date window.
data.window.fromstring | nullWindow start date (ISO YYYY-MM-DD, inclusive).
data.window.tostring | nullWindow end date (ISO YYYY-MM-DD, inclusive).
data.methodstring | nullExactly how the numbers were computed — every result is reproducible by an independent query.
data.interpretationstring | nullPlain-language reading of the finding, when provided.
data.reliabilitystring | nullDenominator-health grade of the underlying clusters, when relevant.
data.notestring | nullCaveats, when any.
metaMetaBlock | nullResponse metadata: compute time, pagination, endpoint extras.
meta.generated_atstring | nullUTC timestamp when the server computed this response — use it to honestly label cached/stale data ('as of …').
meta.totalinteger | nullTotal matching items across all pages.
meta.pageinteger | nullCurrent page number (1-based).
meta.page_sizeinteger | nullItems per page.
meta.windowWindow | nullThe resolved date window the data covers.
meta.window.fromstring | nullWindow start date (ISO YYYY-MM-DD, inclusive).
meta.window.tostring | nullWindow end date (ISO YYYY-MM-DD, inclusive).
meta.next_cursorinteger | nullCursor pagination (present only when the request passed starting_after): the last id in this page — pass it as the next request's starting_after. null means the list is exhausted (has_more == next_cursor is not null).
POST/v1/analyse/narrative-momentum
curl -X POST -H "X-API-Key: $PRESSROOM_KEY" \
  -H "Content-Type: application/json" \
  -d '{"q": "election"}' \
  "https://api.pressroom.news/v1/analyse/narrative-momentum"
import requests

r = requests.post(
    "https://api.pressroom.news/v1/analyse/narrative-momentum",
    headers={"X-API-Key": PRESSROOM_KEY},
    json={"q": "election"},
)
print(r.json())
const res = await fetch("https://api.pressroom.news/v1/analyse/narrative-momentum", {
  headers: { "X-API-Key": PRESSROOM_KEY, "Content-Type": "application/json" },
  method: "POST",
  body: JSON.stringify({"q": "election"})
});
const data = await res.json();

Every occurrence of a quote, chronologically POST /v1/analyse/quote-provenance

Traces a verbatim quote fragment through the corpus: who said it first, where it re-appeared, with article provenance on each occurrence.

Returns occurrences in chronological order.

Parameters

ParameterTypeRequiredDefaultDescription
fragment bodystringrequiredVerbatim quote fragment to trace (min 4 chars).
limit bodyintegeroptional50Max occurrences returned (default 50, max 200).

Response attributes

AttributeTypeDescription
dataQuoteProvenanceResultThe endpoint's payload.
data.fragmentstring | nullThe searched quote fragment.
data.occurrencesarray of object | nullChronological occurrences with article provenance.
data.totalinteger | nullTotal occurrences found.
data.methodstring | nullHow occurrences were matched.
metaMetaBlock | nullResponse metadata: compute time, pagination, endpoint extras.
meta.generated_atstring | nullUTC timestamp when the server computed this response — use it to honestly label cached/stale data ('as of …').
meta.totalinteger | nullTotal matching items across all pages.
meta.pageinteger | nullCurrent page number (1-based).
meta.page_sizeinteger | nullItems per page.
meta.windowWindow | nullThe resolved date window the data covers.
meta.window.fromstring | nullWindow start date (ISO YYYY-MM-DD, inclusive).
meta.window.tostring | nullWindow end date (ISO YYYY-MM-DD, inclusive).
meta.next_cursorinteger | nullCursor pagination (present only when the request passed starting_after): the last id in this page — pass it as the next request's starting_after. null means the list is exhausted (has_more == next_cursor is not null).
POST/v1/analyse/quote-provenance
curl -X POST -H "X-API-Key: $PRESSROOM_KEY" \
  -H "Content-Type: application/json" \
  -d '{"fragment": "example"}' \
  "https://api.pressroom.news/v1/analyse/quote-provenance"
import requests

r = requests.post(
    "https://api.pressroom.news/v1/analyse/quote-provenance",
    headers={"X-API-Key": PRESSROOM_KEY},
    json={"fragment": "example"},
)
print(r.json())
const res = await fetch("https://api.pressroom.news/v1/analyse/quote-provenance", {
  headers: { "X-API-Key": PRESSROOM_KEY, "Content-Type": "application/json" },
  method: "POST",
  body: JSON.stringify({"fragment": "example"})
});
const data = await res.json();

Sentiment per mentioned place POST /v1/analyse/sentiment-geography

Average article sentiment grouped by mentioned location (QID-deduped, country codes normalised to ISO-3166).

Returns per-place sentiment and coverage counts.

Parameters

ParameterTypeRequiredDefaultDescription
q bodystring | nulloptionalKeyword subject (headline match). Provide q or entity.
entity bodyEntityRef | nulloptionalEntity subject. Provide q or entity.
date_from bodystring | nulloptionalWindow start, ISO YYYY-MM-DD (default: 90 days before date_to).
date_to bodystring | nulloptionalWindow end, ISO YYYY-MM-DD (default: today, UTC).

Response attributes

AttributeTypeDescription
dataAnalysisResultThe endpoint's payload.
data.subjectstring | nullThe resolved subject: 'q:<query>' or '<kind>:<id>'.
data.windowWindow | nullResolved date window.
data.window.fromstring | nullWindow start date (ISO YYYY-MM-DD, inclusive).
data.window.tostring | nullWindow end date (ISO YYYY-MM-DD, inclusive).
data.methodstring | nullExactly how the numbers were computed — every result is reproducible by an independent query.
data.interpretationstring | nullPlain-language reading of the finding, when provided.
data.reliabilitystring | nullDenominator-health grade of the underlying clusters, when relevant.
data.notestring | nullCaveats, when any.
metaMetaBlock | nullResponse metadata: compute time, pagination, endpoint extras.
meta.generated_atstring | nullUTC timestamp when the server computed this response — use it to honestly label cached/stale data ('as of …').
meta.totalinteger | nullTotal matching items across all pages.
meta.pageinteger | nullCurrent page number (1-based).
meta.page_sizeinteger | nullItems per page.
meta.windowWindow | nullThe resolved date window the data covers.
meta.window.fromstring | nullWindow start date (ISO YYYY-MM-DD, inclusive).
meta.window.tostring | nullWindow end date (ISO YYYY-MM-DD, inclusive).
meta.next_cursorinteger | nullCursor pagination (present only when the request passed starting_after): the last id in this page — pass it as the next request's starting_after. null means the list is exhausted (has_more == next_cursor is not null).
POST/v1/analyse/sentiment-geography
curl -X POST -H "X-API-Key: $PRESSROOM_KEY" \
  -H "Content-Type: application/json" \
  -d '{"q": "election"}' \
  "https://api.pressroom.news/v1/analyse/sentiment-geography"
import requests

r = requests.post(
    "https://api.pressroom.news/v1/analyse/sentiment-geography",
    headers={"X-API-Key": PRESSROOM_KEY},
    json={"q": "election"},
)
print(r.json())
const res = await fetch("https://api.pressroom.news/v1/analyse/sentiment-geography", {
  headers: { "X-API-Key": PRESSROOM_KEY, "Content-Type": "application/json" },
  method: "POST",
  body: JSON.stringify({"q": "election"})
});
const data = await res.json();

% of a result set that is net-new vs repetition POST /v1/analyse/signal-vs-noise

Splits the subject's articles into originals vs near-duplicate repetition using the story clusters.

Returns signal/noise counts, the ratio, and cluster health.

Parameters

ParameterTypeRequiredDefaultDescription
q bodystring | nulloptionalKeyword subject (headline match). Provide q or entity.
entity bodyEntityRef | nulloptionalEntity subject. Provide q or entity.
date_from bodystring | nulloptionalWindow start, ISO YYYY-MM-DD (default: 90 days before date_to).
date_to bodystring | nulloptionalWindow end, ISO YYYY-MM-DD (default: today, UTC).

Response attributes

AttributeTypeDescription
dataAnalysisResultThe endpoint's payload.
data.subjectstring | nullThe resolved subject: 'q:<query>' or '<kind>:<id>'.
data.windowWindow | nullResolved date window.
data.window.fromstring | nullWindow start date (ISO YYYY-MM-DD, inclusive).
data.window.tostring | nullWindow end date (ISO YYYY-MM-DD, inclusive).
data.methodstring | nullExactly how the numbers were computed — every result is reproducible by an independent query.
data.interpretationstring | nullPlain-language reading of the finding, when provided.
data.reliabilitystring | nullDenominator-health grade of the underlying clusters, when relevant.
data.notestring | nullCaveats, when any.
metaMetaBlock | nullResponse metadata: compute time, pagination, endpoint extras.
meta.generated_atstring | nullUTC timestamp when the server computed this response — use it to honestly label cached/stale data ('as of …').
meta.totalinteger | nullTotal matching items across all pages.
meta.pageinteger | nullCurrent page number (1-based).
meta.page_sizeinteger | nullItems per page.
meta.windowWindow | nullThe resolved date window the data covers.
meta.window.fromstring | nullWindow start date (ISO YYYY-MM-DD, inclusive).
meta.window.tostring | nullWindow end date (ISO YYYY-MM-DD, inclusive).
meta.next_cursorinteger | nullCursor pagination (present only when the request passed starting_after): the last id in this page — pass it as the next request's starting_after. null means the list is exhausted (has_more == next_cursor is not null).
POST/v1/analyse/signal-vs-noise
curl -X POST -H "X-API-Key: $PRESSROOM_KEY" \
  -H "Content-Type: application/json" \
  -d '{"q": "election"}' \
  "https://api.pressroom.news/v1/analyse/signal-vs-noise"
import requests

r = requests.post(
    "https://api.pressroom.news/v1/analyse/signal-vs-noise",
    headers={"X-API-Key": PRESSROOM_KEY},
    json={"q": "election"},
)
print(r.json())
const res = await fetch("https://api.pressroom.news/v1/analyse/signal-vs-noise", {
  headers: { "X-API-Key": PRESSROOM_KEY, "Content-Type": "application/json" },
  method: "POST",
  body: JSON.stringify({"q": "election"})
});
const data = await res.json();

Who broke each story + how it propagated POST /v1/analyse/story-attribution

For each multi-article story cluster on the subject: the earliest article (the originator) and the propagation chain, with denominator-health caveats.

Returns the attributed stories and cluster health.

Parameters

ParameterTypeRequiredDefaultDescription
q bodystring | nulloptionalKeyword subject (headline match). Provide q or entity.
entity bodyEntityRef | nulloptionalEntity subject. Provide q or entity.
date_from bodystring | nulloptionalWindow start, ISO YYYY-MM-DD (default: 90 days before date_to).
date_to bodystring | nulloptionalWindow end, ISO YYYY-MM-DD (default: today, UTC).

Response attributes

AttributeTypeDescription
dataAnalysisResultThe endpoint's payload.
data.subjectstring | nullThe resolved subject: 'q:<query>' or '<kind>:<id>'.
data.windowWindow | nullResolved date window.
data.window.fromstring | nullWindow start date (ISO YYYY-MM-DD, inclusive).
data.window.tostring | nullWindow end date (ISO YYYY-MM-DD, inclusive).
data.methodstring | nullExactly how the numbers were computed — every result is reproducible by an independent query.
data.interpretationstring | nullPlain-language reading of the finding, when provided.
data.reliabilitystring | nullDenominator-health grade of the underlying clusters, when relevant.
data.notestring | nullCaveats, when any.
metaMetaBlock | nullResponse metadata: compute time, pagination, endpoint extras.
meta.generated_atstring | nullUTC timestamp when the server computed this response — use it to honestly label cached/stale data ('as of …').
meta.totalinteger | nullTotal matching items across all pages.
meta.pageinteger | nullCurrent page number (1-based).
meta.page_sizeinteger | nullItems per page.
meta.windowWindow | nullThe resolved date window the data covers.
meta.window.fromstring | nullWindow start date (ISO YYYY-MM-DD, inclusive).
meta.window.tostring | nullWindow end date (ISO YYYY-MM-DD, inclusive).
meta.next_cursorinteger | nullCursor pagination (present only when the request passed starting_after): the last id in this page — pass it as the next request's starting_after. null means the list is exhausted (has_more == next_cursor is not null).
POST/v1/analyse/story-attribution
curl -X POST -H "X-API-Key: $PRESSROOM_KEY" \
  -H "Content-Type: application/json" \
  -d '{"q": "election"}' \
  "https://api.pressroom.news/v1/analyse/story-attribution"
import requests

r = requests.post(
    "https://api.pressroom.news/v1/analyse/story-attribution",
    headers={"X-API-Key": PRESSROOM_KEY},
    json={"q": "election"},
)
print(r.json())
const res = await fetch("https://api.pressroom.news/v1/analyse/story-attribution", {
  headers: { "X-API-Key": PRESSROOM_KEY, "Content-Type": "application/json" },
  method: "POST",
  body: JSON.stringify({"q": "election"})
});
const data = await res.json();

Emerging → Building → Peak → Declining → Dead POST /v1/analyse/story-lifecycle

Classifies the subject's coverage curve into a lifecycle stage from bucketed volume.

Returns the stage, the volume curve, and the method.

Parameters

ParameterTypeRequiredDefaultDescription
q bodystring | nulloptionalKeyword subject (headline match). Provide q or entity.
entity bodyEntityRef | nulloptionalEntity subject. Provide q or entity.
date_from bodystring | nulloptionalWindow start, ISO YYYY-MM-DD (default: 90 days before date_to).
date_to bodystring | nulloptionalWindow end, ISO YYYY-MM-DD (default: today, UTC).

Response attributes

AttributeTypeDescription
dataAnalysisResultThe endpoint's payload.
data.subjectstring | nullThe resolved subject: 'q:<query>' or '<kind>:<id>'.
data.windowWindow | nullResolved date window.
data.window.fromstring | nullWindow start date (ISO YYYY-MM-DD, inclusive).
data.window.tostring | nullWindow end date (ISO YYYY-MM-DD, inclusive).
data.methodstring | nullExactly how the numbers were computed — every result is reproducible by an independent query.
data.interpretationstring | nullPlain-language reading of the finding, when provided.
data.reliabilitystring | nullDenominator-health grade of the underlying clusters, when relevant.
data.notestring | nullCaveats, when any.
metaMetaBlock | nullResponse metadata: compute time, pagination, endpoint extras.
meta.generated_atstring | nullUTC timestamp when the server computed this response — use it to honestly label cached/stale data ('as of …').
meta.totalinteger | nullTotal matching items across all pages.
meta.pageinteger | nullCurrent page number (1-based).
meta.page_sizeinteger | nullItems per page.
meta.windowWindow | nullThe resolved date window the data covers.
meta.window.fromstring | nullWindow start date (ISO YYYY-MM-DD, inclusive).
meta.window.tostring | nullWindow end date (ISO YYYY-MM-DD, inclusive).
meta.next_cursorinteger | nullCursor pagination (present only when the request passed starting_after): the last id in this page — pass it as the next request's starting_after. null means the list is exhausted (has_more == next_cursor is not null).
POST/v1/analyse/story-lifecycle
curl -X POST -H "X-API-Key: $PRESSROOM_KEY" \
  -H "Content-Type: application/json" \
  -d '{"q": "election"}' \
  "https://api.pressroom.news/v1/analyse/story-lifecycle"
import requests

r = requests.post(
    "https://api.pressroom.news/v1/analyse/story-lifecycle",
    headers={"X-API-Key": PRESSROOM_KEY},
    json={"q": "election"},
)
print(r.json())
const res = await fetch("https://api.pressroom.news/v1/analyse/story-lifecycle", {
  headers: { "X-API-Key": PRESSROOM_KEY, "Content-Type": "application/json" },
  method: "POST",
  body: JSON.stringify({"q": "election"})
});
const data = await res.json();

Briefs & QA

Deterministic cited analyst brief POST /v1/briefs/generate

Composes an analyst brief for a subject + window from the substrate: a templated summary, top co-covered entities, highest-confidence quotes, source volumes, and citations — no LLM, fully reproducible.

Returns the Brief object.

Parameters

ParameterTypeRequiredDefaultDescription
q bodystring | nulloptionalKeyword subject (headline match). Provide q or entity.
entity bodyEntityRef | nulloptionalEntity subject. Provide q or entity.
date_from bodystring | nulloptionalWindow start, ISO YYYY-MM-DD (default: 90 days before date_to).
date_to bodystring | nulloptionalWindow end, ISO YYYY-MM-DD (default: today, UTC).

Response attributes

AttributeTypeDescription
dataBriefThe endpoint's payload.
data.subjectstring | nullThe resolved subject.
data.windowWindow | nullResolved date window.
data.window.fromstring | nullWindow start date (ISO YYYY-MM-DD, inclusive).
data.window.tostring | nullWindow end date (ISO YYYY-MM-DD, inclusive).
data.total_articlesinteger | nullArticles covering the subject in the window.
data.total_sourcesinteger | nullDistinct sources across those articles.
data.summarystring | nullTemplated narrative summary (no LLM).
data.top_entitiesobject | nullTop co-covered {people, organisations, locations} with co-counts.
data.top_quotesarray of object | nullHighest-confidence attributed quotes with provenance.
data.top_sourcesarray of object | nullSources by article volume.
data.citationsarray of object | nullArticle citations backing the brief.
data.generation_modestring | nullAlways 'deterministic'.
metaMetaBlock | nullResponse metadata: compute time, pagination, endpoint extras.
meta.generated_atstring | nullUTC timestamp when the server computed this response — use it to honestly label cached/stale data ('as of …').
meta.totalinteger | nullTotal matching items across all pages.
meta.pageinteger | nullCurrent page number (1-based).
meta.page_sizeinteger | nullItems per page.
meta.windowWindow | nullThe resolved date window the data covers.
meta.window.fromstring | nullWindow start date (ISO YYYY-MM-DD, inclusive).
meta.window.tostring | nullWindow end date (ISO YYYY-MM-DD, inclusive).
meta.next_cursorinteger | nullCursor pagination (present only when the request passed starting_after): the last id in this page — pass it as the next request's starting_after. null means the list is exhausted (has_more == next_cursor is not null).
POST/v1/briefs/generate
curl -X POST -H "X-API-Key: $PRESSROOM_KEY" \
  -H "Content-Type: application/json" \
  -d '{"q": "election"}' \
  "https://api.pressroom.news/v1/briefs/generate"
import requests

r = requests.post(
    "https://api.pressroom.news/v1/briefs/generate",
    headers={"X-API-Key": PRESSROOM_KEY},
    json={"q": "election"},
)
print(r.json())
const res = await fetch("https://api.pressroom.news/v1/briefs/generate", {
  headers: { "X-API-Key": PRESSROOM_KEY, "Content-Type": "application/json" },
  method: "POST",
  body: JSON.stringify({"q": "election"})
});
const data = await res.json();

Deterministic retrieval QA with citations (no LLM) POST /v1/chat

Retrieval question-answering: stopword-filtered terms plus entity resolution pull ranked evidence passages, deduped by story cluster; the answer is templated from them.

Returns the answer, passages, and citations.

Parameters

ParameterTypeRequiredDefaultDescription
question bodystringrequiredA natural-language question over the corpus.
date_from bodystring | nulloptionalWindow start, ISO YYYY-MM-DD.
date_to bodystring | nulloptionalWindow end, ISO YYYY-MM-DD.
k bodyintegeroptional8Max evidence passages to retrieve (default 8, max 25).

Response attributes

AttributeTypeDescription
dataChatAnswerThe endpoint's payload.
data.questionstring | nullThe question asked.
data.answerstring | nullTemplated answer composed from the passages.
data.passagesarray of object | nullRanked evidence passages with article provenance.
data.citationsarray of object | nullArticle citations.
data.generation_modestring | nullAlways 'deterministic'.
metaMetaBlock | nullResponse metadata: compute time, pagination, endpoint extras.
meta.generated_atstring | nullUTC timestamp when the server computed this response — use it to honestly label cached/stale data ('as of …').
meta.totalinteger | nullTotal matching items across all pages.
meta.pageinteger | nullCurrent page number (1-based).
meta.page_sizeinteger | nullItems per page.
meta.windowWindow | nullThe resolved date window the data covers.
meta.window.fromstring | nullWindow start date (ISO YYYY-MM-DD, inclusive).
meta.window.tostring | nullWindow end date (ISO YYYY-MM-DD, inclusive).
meta.next_cursorinteger | nullCursor pagination (present only when the request passed starting_after): the last id in this page — pass it as the next request's starting_after. null means the list is exhausted (has_more == next_cursor is not null).
POST/v1/chat
curl -X POST -H "X-API-Key: $PRESSROOM_KEY" \
  -H "Content-Type: application/json" \
  -d '{"question": "What has been reported about elections?"}' \
  "https://api.pressroom.news/v1/chat"
import requests

r = requests.post(
    "https://api.pressroom.news/v1/chat",
    headers={"X-API-Key": PRESSROOM_KEY},
    json={"question": "What has been reported about elections?"},
)
print(r.json())
const res = await fetch("https://api.pressroom.news/v1/chat", {
  headers: { "X-API-Key": PRESSROOM_KEY, "Content-Type": "application/json" },
  method: "POST",
  body: JSON.stringify({"question": "What has been reported about elections?"})
});
const data = await res.json();

Embeddable widgets

Embeddable widget catalogue GET /v1/widgets

The catalogue of embeddable intelligence widgets (sellable-backed only), with each widget's data URL and whether it needs a subject.

Returns the widget list plus embed/billing pointers in meta.

Parameters

No parameters (beyond the auth header).

Response attributes

AttributeTypeDescription
dataarray of WidgetCatalogueItemThe endpoint's payload.
data[].idstring | nullWidget id (pass to /v1/widgets/{id}/data).
data[].namestring | nullDisplay name of the widget.
data[].needs_subjectboolean | nullTrue when the widget requires ?subject=<entity or query>.
data[].data_urlstring | nullPath of the widget's metered data endpoint.
metaMetaBlock | nullResponse metadata: compute time, pagination, endpoint extras.
meta.generated_atstring | nullUTC timestamp when the server computed this response — use it to honestly label cached/stale data ('as of …').
meta.totalinteger | nullTotal matching items across all pages.
meta.pageinteger | nullCurrent page number (1-based).
meta.page_sizeinteger | nullItems per page.
meta.windowWindow | nullThe resolved date window the data covers.
meta.window.fromstring | nullWindow start date (ISO YYYY-MM-DD, inclusive).
meta.window.tostring | nullWindow end date (ISO YYYY-MM-DD, inclusive).
meta.next_cursorinteger | nullCursor pagination (present only when the request passed starting_after): the last id in this page — pass it as the next request's starting_after. null means the list is exhausted (has_more == next_cursor is not null).
GET/v1/widgets
curl -H "X-API-Key: $PRESSROOM_KEY" \
  "https://api.pressroom.news/v1/widgets"
import requests

r = requests.get(
    "https://api.pressroom.news/v1/widgets",
    headers={"X-API-Key": PRESSROOM_KEY},
)
print(r.json())
const res = await fetch("https://api.pressroom.news/v1/widgets", {
  headers: { "X-API-Key": PRESSROOM_KEY }
});
const data = await res.json();

Widget data payload (metered) GET /v1/widgets/{widget_id}/data

The data payload for one widget, composed from the same v1 service layer as the public endpoints. Each fetch meters as one API request on the key's account; publishable (pk_) keys are domain-locked.

Returns the widget-specific payload with widget/subject/window context in meta; 501 for gallery ids without a sellable public substrate.

Parameters

ParameterTypeRequiredDefaultDescription
widget_id pathstringrequiredWidget id from the catalogue.
subject querystring | nulloptionalThe widget subject (an entity name or query); required when the catalogue marks needs_subject.
days queryintegeroptional30Coverage window in days (default 30, clamped to 1-365).

Response attributes

AttributeTypeDescription
dataanyThe endpoint's payload.
metaMetaBlock | nullResponse metadata: compute time, pagination, endpoint extras.
meta.generated_atstring | nullUTC timestamp when the server computed this response — use it to honestly label cached/stale data ('as of …').
meta.totalinteger | nullTotal matching items across all pages.
meta.pageinteger | nullCurrent page number (1-based).
meta.page_sizeinteger | nullItems per page.
meta.windowWindow | nullThe resolved date window the data covers.
meta.window.fromstring | nullWindow start date (ISO YYYY-MM-DD, inclusive).
meta.window.tostring | nullWindow end date (ISO YYYY-MM-DD, inclusive).
meta.next_cursorinteger | nullCursor pagination (present only when the request passed starting_after): the last id in this page — pass it as the next request's starting_after. null means the list is exhausted (has_more == next_cursor is not null).
GET/v1/widgets/{widget_id}/data
curl -H "X-API-Key: $PRESSROOM_KEY" \
  "https://api.pressroom.news/v1/widgets/quote_provenance/data?subject=Adani"
import requests

r = requests.get(
    "https://api.pressroom.news/v1/widgets/quote_provenance/data",
    headers={"X-API-Key": PRESSROOM_KEY},
    params={"subject": "Adani"},
)
print(r.json())
const res = await fetch("https://api.pressroom.news/v1/widgets/quote_provenance/data?subject=Adani", {
  headers: { "X-API-Key": PRESSROOM_KEY }
});
const data = await res.json();

Sources

Source registry with audience taxonomy GET /v1/sources

The full source registry with the audience taxonomy (origin country, audience country, language, category) and per-source article counts.

Returns the list of sources ordered by name.

Parameters

No parameters (beyond the auth header).

Response attributes

AttributeTypeDescription
dataarray of SourceThe endpoint's payload.
data[].objectstring | nullObject type discriminator: always "source".
data[].idstring | nullStable registry id of the source (canonical).
data[].source_idstring | nullDeprecated alias of `id`; will be removed in the next API version.
data[].namestring | nullSource display name.
data[].country_of_originstring | nullWhere the publisher is based.
data[].country_of_audiencestring | nullPrimary audience: a country name or 'International'.
data[].languagestring | nullPrimary publication language (ISO code).
data[].nav_groupstring | nullNavigation grouping (e.g. Global, India).
data[].categoryarray of any | nullEditorial categories (wire, daily, business, …).
data[].article_countinteger | nullArticles extracted from this source.
metaMetaBlock | nullResponse metadata: compute time, pagination, endpoint extras.
meta.generated_atstring | nullUTC timestamp when the server computed this response — use it to honestly label cached/stale data ('as of …').
meta.totalinteger | nullTotal matching items across all pages.
meta.pageinteger | nullCurrent page number (1-based).
meta.page_sizeinteger | nullItems per page.
meta.windowWindow | nullThe resolved date window the data covers.
meta.window.fromstring | nullWindow start date (ISO YYYY-MM-DD, inclusive).
meta.window.tostring | nullWindow end date (ISO YYYY-MM-DD, inclusive).
meta.next_cursorinteger | nullCursor pagination (present only when the request passed starting_after): the last id in this page — pass it as the next request's starting_after. null means the list is exhausted (has_more == next_cursor is not null).
GET/v1/sources
curl -H "X-API-Key: $PRESSROOM_KEY" \
  "https://api.pressroom.news/v1/sources"
import requests

r = requests.get(
    "https://api.pressroom.news/v1/sources",
    headers={"X-API-Key": PRESSROOM_KEY},
)
print(r.json())
const res = await fetch("https://api.pressroom.news/v1/sources", {
  headers: { "X-API-Key": PRESSROOM_KEY }
});
const data = await res.json();

Source-cluster facets (origin / audience / language) GET /v1/sources/facets

Source counts aggregated per taxonomy dimension — the cluster facets used by narrative-conflict grouping.

Returns per-dimension value/count buckets.

Parameters

No parameters (beyond the auth header).

Response attributes

AttributeTypeDescription
dataSourceFacetsThe endpoint's payload.
data.country_of_originarray of FacetBucket | nullSources by publisher origin country.
data.country_of_origin[].valuestring | nullThe facet value.
data.country_of_origin[].sourcesinteger | nullSources carrying this value.
data.country_of_audiencearray of FacetBucket | nullSources by audience country/region.
data.country_of_audience[].valuestring | nullThe facet value.
data.country_of_audience[].sourcesinteger | nullSources carrying this value.
data.languagearray of FacetBucket | nullSources by language.
data.language[].valuestring | nullThe facet value.
data.language[].sourcesinteger | nullSources carrying this value.
data.nav_grouparray of FacetBucket | nullSources by navigation group.
data.nav_group[].valuestring | nullThe facet value.
data.nav_group[].sourcesinteger | nullSources carrying this value.
metaMetaBlock | nullResponse metadata: compute time, pagination, endpoint extras.
meta.generated_atstring | nullUTC timestamp when the server computed this response — use it to honestly label cached/stale data ('as of …').
meta.totalinteger | nullTotal matching items across all pages.
meta.pageinteger | nullCurrent page number (1-based).
meta.page_sizeinteger | nullItems per page.
meta.windowWindow | nullThe resolved date window the data covers.
meta.window.fromstring | nullWindow start date (ISO YYYY-MM-DD, inclusive).
meta.window.tostring | nullWindow end date (ISO YYYY-MM-DD, inclusive).
meta.next_cursorinteger | nullCursor pagination (present only when the request passed starting_after): the last id in this page — pass it as the next request's starting_after. null means the list is exhausted (has_more == next_cursor is not null).
GET/v1/sources/facets
curl -H "X-API-Key: $PRESSROOM_KEY" \
  "https://api.pressroom.news/v1/sources/facets"
import requests

r = requests.get(
    "https://api.pressroom.news/v1/sources/facets",
    headers={"X-API-Key": PRESSROOM_KEY},
)
print(r.json())
const res = await fetch("https://api.pressroom.news/v1/sources/facets", {
  headers: { "X-API-Key": PRESSROOM_KEY }
});
const data = await res.json();

System & account

Your key's metering + quota state GET /v1/account/usage

Self-service usage introspection for the presented key.

Returns your tier, monthly quota and consumption, lifetime call totals, and your 20 most recent requests.

Parameters

No parameters (beyond the auth header).

Response attributes

AttributeTypeDescription
dataAccountUsageThe endpoint's payload.
data.tierstring | nullYour tier (free | pro | …).
data.monthly_quotainteger | nullYour monthly request quota (-1 = unlimited).
data.monthly_usedinteger | nullRequests used in the current period.
data.usage_periodstring | nullThe current usage period (YYYY-MM).
data.lifetime_callsinteger | nullTotal calls ever made on the key.
data.lifetime_cost_unitsinteger | number | nullTotal metered cost units.
data.recentarray of UsageRecord | nullYour 20 most recent calls.
data.recent[].tsstring | nullWhen the call was made (UTC).
data.recent[].methodstring | nullHTTP method.
data.recent[].endpointstring | nullPath called.
data.recent[].statusinteger | nullHTTP status returned.
data.recent[].latency_msinteger | nullServer-side latency in milliseconds.
metaMetaBlock | nullResponse metadata: compute time, pagination, endpoint extras.
meta.generated_atstring | nullUTC timestamp when the server computed this response — use it to honestly label cached/stale data ('as of …').
meta.totalinteger | nullTotal matching items across all pages.
meta.pageinteger | nullCurrent page number (1-based).
meta.page_sizeinteger | nullItems per page.
meta.windowWindow | nullThe resolved date window the data covers.
meta.window.fromstring | nullWindow start date (ISO YYYY-MM-DD, inclusive).
meta.window.tostring | nullWindow end date (ISO YYYY-MM-DD, inclusive).
meta.next_cursorinteger | nullCursor pagination (present only when the request passed starting_after): the last id in this page — pass it as the next request's starting_after. null means the list is exhausted (has_more == next_cursor is not null).
GET/v1/account/usage
curl -H "X-API-Key: $PRESSROOM_KEY" \
  "https://api.pressroom.news/v1/account/usage"
import requests

r = requests.get(
    "https://api.pressroom.news/v1/account/usage",
    headers={"X-API-Key": PRESSROOM_KEY},
)
print(r.json())
const res = await fetch("https://api.pressroom.news/v1/account/usage", {
  headers: { "X-API-Key": PRESSROOM_KEY }
});
const data = await res.json();

Outcome-based corpus health GET /v1/health

Unauthenticated liveness + data-honesty probe: corpus reachability, article count, serving backend, deployment mode, and metering-reconciliation state.

Returns a Health object (not enveloped).

Parameters

No parameters (beyond the auth header).

Response attributes

AttributeTypeDescription
statusstring | nullOverall health verdict.
corpus_reachableboolean | nullWhether the corpus database answered.
articlesinteger | nullArticles in the corpus.
backendstring | nullDatabase backend serving reads: postgresql | sqlite.
corpus_dbstring | nullCredential-stripped identifier of the corpus database.
deployment_modestring | nullprimary | snapshot_replica (replicas report a data horizon instead of queue freshness).
snapshot_as_ofstring | nullData horizon of a snapshot replica (latest extraction).
metering_reconciliation_requiredinteger | nullUsage reservations awaiting finalization (operator signal).
oldest_metering_reservationstring | nullTimestamp of the oldest unfinalized reservation.
error_classstring | nullException class when the corpus is unreachable.
GET/v1/health
curl -H "X-API-Key: $PRESSROOM_KEY" \
  "https://api.pressroom.news/v1/health"
import requests

r = requests.get(
    "https://api.pressroom.news/v1/health",
    headers={"X-API-Key": PRESSROOM_KEY},
)
print(r.json())
const res = await fetch("https://api.pressroom.news/v1/health", {
  headers: { "X-API-Key": PRESSROOM_KEY }
});
const data = await res.json();

API version + tier catalog GET /v1/version

Unauthenticated API identity endpoint.

Returns the API name, version, and the tier catalog with monthly quotas and per-minute rate limits (not enveloped).

Parameters

No parameters (beyond the auth header).

Response attributes

AttributeTypeDescription
namestring | nullAPI product name.
versionstring | nullAPI version string.
tiersobject | nullAvailable tiers with quota and rate limits.
GET/v1/version
curl -H "X-API-Key: $PRESSROOM_KEY" \
  "https://api.pressroom.news/v1/version"
import requests

r = requests.get(
    "https://api.pressroom.news/v1/version",
    headers={"X-API-Key": PRESSROOM_KEY},
)
print(r.json())
const res = await fetch("https://api.pressroom.news/v1/version", {
  headers: { "X-API-Key": PRESSROOM_KEY }
});
const data = await res.json();

Request access

Pressroom is in private beta. To request an API key, email shobhit.dixit@gmail.com with your use case. Keys are provisioned manually and mapped to a tier.

Pressroom API · v1.0.0-dev · deterministic & provenance-cited · examples on this page are real responses from the live API.