CLSTR
News for agents: 100k+ articles/day deduplicated into situations with timelines. Free, no key required.
Documentation
CLSTR News API and MCP Server
Structured "what is happening": ~100k articles/day ingested, then grouped into multi-source events and into situations with memory. 30 to 90 minutes behind the wires by design. Multi-source shape, not breaking-news speed.
Base URL
https://api.clstr.news/v1
MCP endpoint
https://mcp.clstr.news
Auth
Authorization: Bearer clstr_...
Format
JSON, data on success, error on failure
Versioning
v1, additive only
Quickstart
- 1. Create a free API key. In Settings, sign in first, no card needed.
-
curl -s "https://api.clstr.news/v1/situations?limit=3" \ -H "Authorization: Bearer clstr_YOUR_KEY" - 3. Or connect an agent instead. The same data is served over MCP: see MCP server.
Authentication
Every request needs an API key. Create a free one in Settings (sign in first, no card needed). Send it as a bearer token or an X-Api-Key header.
Authorization: Bearer clstr_YOUR_KEY
Responses allow any origin (Access-Control-Allow-Origin: *) so you can call the API from a browser during development. Keys are secrets: a key shipped inside front-end JavaScript is a key you have published. Call the API from your own server for anything real.
Endpoints
Base URL: https://api.clstr.news/v1
Four read-only endpoints. All responses are JSON: data on success, error.code + error.message on failure, including 404s on unknown paths and server errors. Timestamps are ISO-8601 UTC.
List situations
GET /situations
Top developing situations, ranked by relevance: a blend of significance, how many outlets are covering it, and how recently it moved. Pass sort=recent for newest activity first. significance_score is an integer from 1 to 10, where 8 and above is exceptional.
| Parameter | Type | Default | Description |
|---|---|---|---|
limit | integer | 20 | 1 to 50. |
days | integer | 7 | 1 to your tier's history window (7 on Free). Larger values are clamped, not rejected. |
sort | string | relevance | relevance or recent. |
category | string | all | One category slug, e.g. international. |
country | string | all | Comma separated ISO country codes, up to 10. Extras are dropped, not rejected. |
cursor | string | none | From the previous response's next_cursor. Paging stops after 9 pages. |
Example request
curl -s "https://api.clstr.news/v1/situations?days=1&category=international&sort=relevance&limit=5" \
-H "Authorization: Bearer clstr_YOUR_KEY"
Example response
{
"data": [
{
"id": "8f2b1c6e-0a47-4d93-b8e1-77c5d9a2f410",
"slug": "red-sea-shipping-attacks",
"title": "Red Sea shipping attacks",
"summary_preview": "Attacks on merchant shipping in the southern Red Sea have pushed most container lines onto the Cape route. Insurers have widened the war risk zone twice, and escort capacity remains the binding constraint on convoy slots",
"cluster_count": 34,
"source_count": 212,
"first_seen": "2026-07-02T06:14:00.000Z",
"last_updated": "2026-08-14T11:38:00.000Z",
"status": "ACTIVE",
"category": "international",
"categories": [
"international",
"business"
],
"countries": [
"YE",
"EG",
"US"
],
"significance_score": 9,
"latest_cluster_title": "Insurers widen the Red Sea war risk zone",
"url": "https://clstr.news/situations/red-sea-shipping-attacks"
}
],
"next_cursor": "eyJ2IjoxLCJzIjoicmVsZXZhbmNlIiwicCI6MiwicmVsIjo4LjIsImx1IjoiMjAyNi0wOC0xNFQxMTozODowMC4wMDBaIiwiaWQiOiI4ZjJiMWM2ZS0wYTQ3LTRkOTMtYjhlMS03N2M1ZDlhMmY0MTAiLCJmcCI6IiJ9"
}
statusisACTIVEorQUIET.categoryfilters on a situation's fullcategorieslist, so the top-levelcategoryfield (which is justcategories[0]) can differ from the value you requested.- Cursor pagination stops after 9 pages: past that,
next_cursorcomes back null. Narrow withdays,categoryorcountryrather than paging deeper.
Situation detail + timeline
GET /situations/{id}
One situation (by id or slug) with its ordered timeline of member events, newest first.
| Parameter | Type | Default | Description |
|---|---|---|---|
id | path | required | Situation id or slug. |
timeline_limit | integer | 100 | 1 to 500. |
timeline_before | string | none | Cluster id cursor, from timeline_cursor.next_before. Returns a continuation page. |
Example request
curl -s "https://api.clstr.news/v1/situations/SITUATION_ID_OR_SLUG" \
-H "Authorization: Bearer clstr_YOUR_KEY"
Example response (truncated timeline)
{
"data": {
"id": "8f2b1c6e-0a47-4d93-b8e1-77c5d9a2f410",
"slug": "red-sea-shipping-attacks",
"title": "Red Sea shipping attacks",
"summary": "Attacks on merchant shipping in the southern Red Sea have pushed most container lines onto the Cape route. Insurers have widened the war risk zone twice, and escort capacity remains the binding constraint on convoy slots.",
"summary_preview": "Attacks on merchant shipping in the southern Red Sea have pushed most container lines onto the Cape route. Insurers have widened the war risk zone twice, and escort capacity remains the binding constraint on convoy slots",
"cluster_count": 34,
"source_count": 212,
"first_seen": "2026-07-02T06:14:00.000Z",
"last_updated": "2026-08-14T11:38:00.000Z",
"status": "ACTIVE",
"category": "international",
"categories": [
"international",
"business"
],
"latest_cluster_title": "Insurers widen the Red Sea war risk zone",
"significance_score": 9,
"url": "https://clstr.news/situations/red-sea-shipping-attacks",
"is_timeline_page": false,
"timeline": [
{
"id": "4f3c0a11-9d21-4b7e-8a55-2c9f1d6b0e34",
"slug": "insurers-widen-red-sea-war-risk-zone",
"title": "Insurers widen the Red Sea war risk zone",
"summary": "Marine underwriters extended the listed war risk area to the southern Red Sea, raising premiums for Suez transits. Two carriers said they would keep routing around the Cape.",
"category": "business",
"countries": [
"YE",
"EG"
],
"significance_score": 8,
"sources": 18,
"published_at": "2026-08-14T09:12:00.000Z",
"updated_at": "2026-08-14T11:38:00.000Z",
"url": "https://clstr.news/cluster/insurers-widen-red-sea-war-risk-zone"
},
{
"id": "2d7ae803-64bf-41c0-9a1d-58e0c3f2b917",
"slug": "second-naval-escort-group-ordered-to-bab-el-mandeb",
"title": "A second naval escort group is ordered to Bab el-Mandeb",
"summary": "A second escort group was ordered to the strait, roughly doubling the convoy slots on offer to commercial traffic.",
"category": "international",
"countries": [
"YE",
"US",
"FR"
],
"significance_score": 7,
"sources": 24,
"published_at": "2026-08-13T17:45:00.000Z",
"updated_at": "2026-08-13T21:02:00.000Z",
"url": "https://clstr.news/cluster/second-naval-escort-group-ordered-to-bab-el-mandeb"
}
],
"timeline_cursor": {
"has_more": true,
"next_before": "2d7ae803-64bf-41c0-9a1d-58e0c3f2b917",
"remaining_count": 32,
"total_count": 34
},
"day_span": 43
}
}
Example continuation page (timeline_before set)
{
"data": {
"id": "8f2b1c6e-0a47-4d93-b8e1-77c5d9a2f410",
"slug": "red-sea-shipping-attacks",
"is_timeline_page": true,
"timeline": [
{
"id": "9c81b4f2-3e6d-42a8-b0d7-1f5a7e2c98db",
"slug": "cape-route-diversions-add-two-weeks-to-asia-europe-runs",
"title": "Cape route diversions add two weeks to Asia to Europe runs",
"summary": "Schedule reliability fell again as lines rerouted around southern Africa. Two alliances published revised port rotations with longer transit times.",
"category": "business",
"countries": [
"ZA",
"EG",
"CN"
],
"significance_score": 7,
"sources": 21,
"published_at": "2026-08-12T14:20:00.000Z",
"updated_at": "2026-08-12T18:05:00.000Z",
"url": "https://clstr.news/cluster/cape-route-diversions-add-two-weeks-to-asia-europe-runs"
},
{
"id": "b5e6f70a-8c13-4d29-9f44-6a2b0e17c3d5",
"slug": "container-lines-add-a-red-sea-risk-surcharge",
"title": "Container lines add a Red Sea risk surcharge",
"summary": "Three carriers filed a per-container surcharge covering the higher war risk premium. Shippers asked regulators to review how long it stays on.",
"category": "business",
"countries": [
"DK",
"CH"
],
"significance_score": 6,
"sources": 15,
"published_at": "2026-08-11T08:30:00.000Z",
"updated_at": "2026-08-11T12:15:00.000Z",
"url": "https://clstr.news/cluster/container-lines-add-a-red-sea-risk-surcharge"
}
],
"timeline_cursor": {
"has_more": true,
"next_before": "b5e6f70a-8c13-4d29-9f44-6a2b0e17c3d5",
"remaining_count": 30,
"total_count": 34
}
}
}
- Situation-wide
countriesis a list-endpoint field only. Each timeline entry carries its own. - A
timeline_beforerequest is a continuation page: it returns only id, slug,is_timeline_page: true, timeline[] and timeline_cursor. The header fields are deliberately absent rather than zeroed: keep the ones you got from page one instead of overwriting them. - The cursor is exclusive: a continuation page starts strictly after the cluster named by
next_before, so the entry you paged from is never repeated. - A situation that was merged into another returns the successor with
merged_into_shown: trueandrequested_idset to what you asked for. - A retired situation whose successor is an event answers 404 with
error.moved_to. A concluded situation whose archive was removed answers 410.
Cluster detail
GET /clusters/{id}
One event (a cluster of articles from many outlets reporting the same thing), with its source articles.
| Parameter | Type | Default | Description |
|---|---|---|---|
id | path | required | Cluster id or slug. |
Example request
curl -s "https://api.clstr.news/v1/clusters/CLUSTER_ID_OR_SLUG" \
-H "Authorization: Bearer clstr_YOUR_KEY"
Example response
{
"data": {
"id": "4f3c0a11-9d21-4b7e-8a55-2c9f1d6b0e34",
"slug": "insurers-widen-red-sea-war-risk-zone",
"title": "Insurers widen the Red Sea war risk zone",
"summary": "Marine underwriters extended the listed war risk area to the southern Red Sea, raising premiums for Suez transits. Two carriers said they would keep routing around the Cape.",
"summary_full": "Marine underwriters extended the listed war risk area to the southern Red Sea, raising premiums for Suez transits. Two carriers said they would keep routing around the Cape until escort capacity increases. Brokers put the added cost of a single transit in the low hundreds of thousands of dollars.",
"keywords": [
"red sea",
"war risk",
"marine insurance",
"suez"
],
"category": "business",
"countries": [
"YE",
"EG"
],
"significance_score": 8,
"sources": 18,
"published_at": "2026-08-14T09:12:00.000Z",
"updated_at": "2026-08-14T11:38:00.000Z",
"url": "https://clstr.news/cluster/insurers-widen-red-sea-war-risk-zone",
"articles": [
{
"source_host": "newswire.example.com",
"url": "https://newswire.example.com/business/red-sea-war-risk",
"title": "Underwriters widen Red Sea war risk area",
"published_at": "2026-08-14T09:12:00.000Z"
},
{
"source_host": "shipping.example.com",
"url": "https://shipping.example.com/premiums-rise",
"title": "Premiums rise again for southern Red Sea transits",
"published_at": "2026-08-14T10:40:00.000Z"
}
],
"situation": {
"id": "8f2b1c6e-0a47-4d93-b8e1-77c5d9a2f410",
"title": "Red Sea shipping attacks",
"slug": "red-sea-shipping-attacks",
"cluster_count": 34
}
}
}
summaryis the short form,summary_fullthe long one.situationis null for an event that has not joined one.- A merged-away cluster returns its successor with
merged_into_shown: true. A retired one answers 410.
Search
GET /search
Semantic search over recent events. Results are timeline entries, the same shape a situation timeline carries.
| Parameter | Type | Default | Description |
|---|---|---|---|
q | string | required | Max 200 characters. |
days | integer | 7 | 1 to 30, further clamped to your tier's history window. |
limit | integer | 30 | 1 to 30. |
cursor | string | none | From the previous response's next_cursor. Its own token, not the situations cursor. |
Example request
curl -s "https://api.clstr.news/v1/search?q=red%20sea%20shipping%20insurance&days=7" \
-H "Authorization: Bearer clstr_YOUR_KEY"
Example response
{
"data": [
{
"id": "4f3c0a11-9d21-4b7e-8a55-2c9f1d6b0e34",
"slug": "insurers-widen-red-sea-war-risk-zone",
"title": "Insurers widen the Red Sea war risk zone",
"summary": "Marine underwriters extended the listed war risk area to the southern Red Sea, raising premiums for Suez transits. Two carriers said they would keep routing around the Cape.",
"category": "business",
"countries": [
"YE",
"EG"
],
"significance_score": 8,
"sources": 18,
"published_at": "2026-08-14T09:12:00.000Z",
"updated_at": "2026-08-14T11:38:00.000Z",
"url": "https://clstr.news/cluster/insurers-widen-red-sea-war-risk-zone"
},
{
"id": "2d7ae803-64bf-41c0-9a1d-58e0c3f2b917",
"slug": "second-naval-escort-group-ordered-to-bab-el-mandeb",
"title": "A second naval escort group is ordered to Bab el-Mandeb",
"summary": "A second escort group was ordered to the strait, roughly doubling the convoy slots on offer to commercial traffic.",
"category": "international",
"countries": [
"YE",
"US",
"FR"
],
"significance_score": 7,
"sources": 24,
"published_at": "2026-08-13T17:45:00.000Z",
"updated_at": "2026-08-13T21:02:00.000Z",
"url": "https://clstr.news/cluster/second-naval-escort-group-ordered-to-bab-el-mandeb"
}
],
"query": {
"q": "red sea shipping insurance",
"days": 7
},
"next_cursor": "eyJ2IjoxLCJvIjozMH0"
}
- The search index reaches back at most 30 days, further clamped to your tier's history window.
query.daysin the response is the window actually searched, not what you asked for.- Results are relevance ordered and bounded: the pool is the top matches, not an unbounded scan. Pass
next_cursorback ascursorto page further; it comes back null once the candidate pool is exhausted. - Search has its own daily cap because every query runs an embedding, and each page re-runs the search: one page counts as one search against the cap. See Limits.
Limits & pricing
Caps are hard limits: requests past the cap get a 429, and there are no overage charges, ever. Search is capped separately because each search runs an embedding.
| Tier | Price | Requests/day | Requests/min | Searches/day | History window |
|---|---|---|---|---|---|
| Anonymous (MCP taster) | No key | 10 | 5 | Keyed only | 7 days |
| Free | Free | 100 | 20 | 10 | 7 days |
| Builder | $29/mo | 5,000 | 60 | 250 | 30 days |
| Scale | $299/mo | 100,000 | 1,000 | 10,000 | 30 days |
The anonymous row is the MCP taster: 2 tools (get_top_situations, get_situation_timeline), no signup, counted per network rather than per person and capped platform-wide. Everything else, including search, needs a key.
Caps apply per key, and an account may hold up to 5 active keys. The history window is the widest days value a tier can ask for; larger values are clamped, not rejected. Search reaches back at most 30 days, and never beyond the tier's history window.
Builder and Scale are live: upgrade on the pricing page. If the caps still do not fit, get in touch.
Free-tier use must link back to clstr.news when displayed publicly.
Every authenticated response carries X-RateLimit-Limit-Day, X-RateLimit-Remaining-Day and X-RateLimit-Limit-Minute (all exposed to browsers via CORS). There is no remaining-count for the per-minute window: it is enforced per Cloudflare location and reports no count, so any number here would be a guess. Pace against the daily remaining, and back off on a 429 using Retry-After. /search additionally carries X-RateLimit-Limit-Search and X-RateLimit-Remaining-Search for the daily search cap, which is the limit a free key reaches first. Responses rejected before authentication (a missing or invalid key) carry none of them, because there is no key to report usage for. There is also a generous per-address ceiling in front of authentication; a normal client reaches its own tier cap long before it.
A 429 carries Retry-After in seconds: the exact time left in the window that fired, not a fixed guess.
Errors
Every failure is {"error": {"code": "…", "message": "…"}} with a matching HTTP status. Branch on code; message is for humans and may change.
{
"error": {
"code": "rate_limited",
"message": "Daily cap reached for the hobby tier. Caps: https://clstr.news/developers#limits."
}
}
Error messages name tiers by their internal key, which is what your key record stores. The Free tier's key is hobby.
| Status | code | Meaning |
|---|---|---|
| 400 | bad_request | Missing or malformed parameter (e.g. no q on search). |
| 401 | unauthorized | No key, or the key is unknown or revoked. |
| 404 | not_found | Unknown id, slug, or endpoint. May carry moved_to. |
| 410 | gone | The record existed and was retired. Do not retry. |
| 429 | rate_limited | A cap was reached. The message names which one. |
| 500 | internal_error | Our fault. Safe to retry with backoff. |
| 502 | upstream_error | An upstream (embeddings) failed. Retry shortly. |
| 503 | unavailable | Temporarily unavailable. Retry shortly. |
The error object is additive in the same way data is: a 404 for a retired situation carries moved_to next to code and message. Read the fields you know and ignore the rest, because more of them will appear within v1.
A 429 may carry error.upgrade_url, a link to the plan that lifts the cap you hit.
Versioning & stability
v1 is stable: we only add fields, never rename or remove them within v1. Breaking changes get a new version prefix and at least 90 days' notice on this page. Caps are hard limits. No overage charges, ever.
MCP server
Your agent can search the web. It can't tell you what's happening: which developing situations matter, what 40 sources collectively report, and how a story evolved over six weeks. CLSTR can, over MCP.
One event covered by many outlets becomes a single cluster, deduplicated, with a source count and a significance score. Related clusters link into a situation that carries a maintained summary and a full timeline, so an agent gets memory, not just the latest headline.
Remote server, no install. Same API key, same caps (one tool call counts as one request).
Try it with no key: get_top_situations, get_situation_timeline answer anonymously, 10 calls per day per network (burst 5 per minute), and anonymous timelines come back at most 50 entries deep. A free key lifts that to 100 requests per day, full timeline depth, and search.
Endpoint: https://mcp.clstr.news
Auth: Authorization: Bearer clstr_YOUR_KEY
Tools: get_top_situations · search_situations · get_situation_timeline · get_cluster
| Tool | REST equivalent |
|---|---|
get_top_situations | GET /situations |
search_situations | GET /search |
get_situation_timeline | GET /situations/{id} |
get_cluster | GET /clusters/{id} |
Claude (custom connector): Settings > Connectors > Add custom connector > URL https://mcp.clstr.news, header Authorization: Bearer clstr_YOUR_KEY.
Clients with an mcpServers config:
"clstr": {
"url": "https://mcp.clstr.news",
"headers": { "Authorization": "Bearer clstr_YOUR_KEY" }
}
stdio-only client? Bridge with npx mcp-remote https://mcp.clstr.news --header "Authorization: Bearer clstr_YOUR_KEY".
Check it works, no key:
curl -s https://mcp.clstr.news -H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/call",
"params":{"name":"get_top_situations","arguments":{"window":"24h","limit":3}}}'
The tools obey the same windows as the REST endpoints: search_situations reaches back at most 30 days, and get_top_situations takes 24h, 7d or 30d. Refusals a caller can act on (a cap, an unknown id) come back as readable tool results; transport problems come back as JSON-RPC errors with the HTTP status to match.
get_top_situations and search_situations paginate: when more results exist, the tool ends with a cursor to pass back. For get_top_situations, send the same window and category with it. For search_situations, each page re-runs the search, so one page counts as one search against the cap.
Questions or a use case the caps don't fit? Contact us. There is a markdown copy of this page for agents.