What the public
says about the story.
Echo enriches Polari story clusters with public discourse signal — controversy scores, dominant themes, counter-narratives, and sentiment from Reddit and Bluesky. It tells you what editorial coverage missed.
Echo sits above the pipeline
The four pipeline layers process editorial content — articles, their entities, their clusters, their relationships. Echo is different. It listens to what happens in public discourse after a story is established, and attaches that signal back to the cluster.
This means Echo enrichment is
always cluster-scoped. You need a cluster_id from Layer 2 to query Echo. The enrichment you get
back tells you how the public received that story — which entities they introduced that editorial coverage
didn't mention, whether they're pushing back on the dominant narrative, and how much controversy the story
generated across platforms.
Polari story clusters move through lifecycle states as editorial coverage evolves: breaking →
developing → mature → concluded. Echo introduces a fifth state:
contested.
A story becomes contested when editorial coverage has gone quiet — the story is
concluded from a journalism standpoint — but public discourse on Reddit or Bluesky is still
active and generating high controversy or counter-narrative signal. The story isn't over. The public just
hasn't caught up, or disagrees with how it was covered.
contested surfaces automatically on Layer 2 story responses when Echo
enrichment is present. No additional API calls needed — if a cluster is contested, the
lifecycle_state field on GET /v1/story/{id} will say so.
Endpoints Professional+
Base URL:
https://echo.api.polariapi.com · All endpoints require Authorization: Bearer {key}
except /health. Starter tier keys receive 403 tier_restricted.
The primary Echo endpoint. Returns the full public discourse enrichment for a story cluster — sentiment, controversy, dominant themes, novel entities, and counter-narrative analysis, broken down by platform.
| Field | Type | Description |
|---|---|---|
| platform | string | reddit or bluesky. One object per platform with enrichment data. |
| sentiment_score | float | Aggregate sentiment across analyzed posts/comments. –1 (negative) to +1 (positive). |
| controversy_score | float | 0–1. Scores ≥ 0.6 contribute to contested lifecycle state. |
| dominant_themes | string[] | LLM-synthesized themes from public discourse — often diverge from editorial framing. |
| editorial_entities | string[] | Named entities that appear in both editorial coverage and public posts. |
| novel_entities | string[] | Named entities that appear in public discourse but not in editorial coverage. The signal that something is being said that journalism isn't covering. |
| counter_narrative.detected | bool | Whether a counter-narrative was identified. Confidence ≥ 0.7 contributes to contested
state. |
| counter_narrative.summary | string | Natural language description of the counter-narrative, synthesized by LLM from public posts. |
| contested | bool | Convenience flag. true if any platform has controversy ≥ 0.6 or counter-narrative
confidence ≥ 0.7, and enrichment is within 48 hours. |
Batch retrieval of Echo enrichments for up to 50 story clusters in a single request.
| Parameter | Type | Description |
|---|---|---|
| ids required | string | Comma-separated cluster IDs. Max 50. |
Response is an object keyed by story ID. Clusters with no Echo enrichment are omitted from the response rather than returning null entries.
Stories with the highest public controversy or active counter-narratives right now — ordered by controversy score descending. Useful for surfacing what the editorial layer hasn't caught up with yet.
| Parameter | Type | Description |
|---|---|---|
| min_controversy | float | Minimum controversy score threshold. Default 0.6. |
| counter_narrative_only | bool | If true, only returns stories where a counter-narrative was detected. Default
false.
|
| platform | string | Filter by source platform: reddit or bluesky. Omit for all platforms. |
| limit | integer | Max results. Default 20, max 50. |
Using Echo from the Python SDK
Echo enrichment is accessed
directly via HTTP — the SDK does not yet have a typed client.echo layer. Use httpx
or any HTTP client alongside the SDK for pipeline calls.
client.echo layer with the same patterns as the
pipeline SDK — get_story(), get_stories(), get_trending() — is on the
roadmap. Until then, the HTTP interface above is the integration path.
Freshness & coverage
Echo enrichment
is written per platform per story — one row for Reddit, one for Bluesky. Each is updated in place as new
activity is detected. The fetched_at timestamp on each enrichment tells you when the latest
signal was collected.
| Platform | Update cadence | Signal type | Status |
|---|---|---|---|
| Inline with article fetch | Comment threads on editorial posts | Live | |
| Bluesky | Every 30 minutes | Targeted post search driven by story entities | Live |
| X / Twitter | — | Keyword search on high-velocity clusters | Coming soon |
Stories with no Echo
enrichment return a 404 from the single-story endpoint and are omitted from batch responses. Not
all clusters will have enrichment — Echo targets active, high-signal stories.