Your data market, in your own AI.
Everything a market knows - every document read and scored, every field answered, the running narrative - is yours over REST and the Model Context Protocol. This is data intelligence in the plain sense: the intelligence is the data, and it drops into Claude, your agents, your notebooks and your models the way any other source does. Read-only, JSON, one bearer key per market.
Connect an assistant in one line
A market's MCP server speaks JSON-RPC 2.0 over streamable HTTP at one URL. Any MCP client - Claude Desktop, Claude Code, your own agent - authenticates with the market's key and gets eight read-only tools. Ask what changed overnight, pull the documents behind it, draft the note: the assistant answers from what was actually published, cited document by document.
{
"mcpServers": {
"beacon49-<market>": {
"type": "http",
"url": "https://beacon49.com/api/markets/<market>/mcp",
"headers": {
"Authorization": "Bearer <key>"
}
}
}
}claude mcp add beacon49-<market> https://beacon49.com/api/markets/<market>/mcp \
--transport http --header "Authorization: Bearer <key>"| Tool | What it returns | Arguments |
|---|---|---|
| board | The dashboard rollup for a time window: how many signals per section and per tier, extraction-field counts, and totals. Start here to see what is worth drilling into. | Argumentswindow |
| executive_overview | The latest AI-written narrative of the past hour and past day, as HTML fragments, with the timestamp it was written. | Argumentsnone |
| section_signals | Documents in one dashboard section (e.g. sanctions, competitors, client): the three most recent alert/lead rows pinned first, then everything newest first. | Argumentssection (required), window, limit |
| extraction_feed | Documents where one of the market's extraction fields fired, newest first. `flag` is a field name (capacity_offline), field:LEAD (route_disruption:SEVERE), or field:LEAD1|LEAD2 for several grades of one field (capacity_offline:UNPLANNED|PLANNED). | Argumentsflag (required), window, limit |
| document | One collected document by uuid: full text, scores, topics, extractions. | Argumentsuuid (required) |
| search | Full-text search over everything stored for this market - documents and extractions - returning the best twenty matches with relevance scores. Unwindowed: use this to find something from weeks ago. | Argumentsquery (required) |
| entity_rollup | The window's extracted entities ranked by mentions - people, companies, vessels, places - each with counts of the documents carrying it that lean positive or negative. | Argumentswindow, limit |
| topic_pulse | Mention volume, mean sentiment (-1..1) and a bucketed time series for up to twelve topic keys - how loudly each is covered, in what tone, and when. | Argumentstopics (required), window |
The same market, as plain JSON
Every route is a GET that takes the market in the path and the key in an Authorization header, and returns what the dashboard shows - which is the point: a notebook, a quant model or an internal tool reads exactly what your team reads. Windows are 1h, 1d or 1w.
curl -H "Authorization: Bearer <key>" \
"https://beacon49.com/api/markets/<market>/signals?section=supply&window=1d"| Route | What it returns | Parameters |
|---|---|---|
BoardGET /api/markets/{market}/signals | The whole dashboard in one call: per-section and per-tier counts, extraction-marker counts, movement against the previous window, the latest executive overview and engine freshness. | Parameterswindow - 1h | 1d | 1w. Default 1d. |
Section feedGET /api/markets/{market}/signals | Documents in one section: the three most recent alert or lead rows pinned first, then everything newest first - the same ordering the dashboard shows. | Parameterssection - Section key from the sections route, e.g. sanctions. window - 1h | 1d | 1w. Default 1d. limit - 1-100. Default 40. |
Extraction feedGET /api/markets/{market}/signals | Documents where one of the market's extraction fields fired, newest first. | Parametersflag - Field name (capacity_offline), field:LEAD (route_disruption:SEVERE), or field:LEAD1|LEAD2 (capacity_offline:UNPLANNED|PLANNED). window - 1h | 1d | 1w. Default 1d. limit - 1-100. Default 40. |
WireGET /api/markets/{market}/wire | This market's raw stream: the newest documents, newest first, in the light wire projection - what has arrived rather than what ranked. Unwindowed, so a poller sees the flow itself. | Parameterslimit - 1-100. Default 50. |
Facet drill-downGET /api/markets/{market}/signals | The documents behind one cross-tab cell: every signal matching all of the given extraction answers, newest first, with the count they were drawn from. Selects the same documents as the matrix, so the list and the number it was clicked from agree. | Parametersfacet - field:ANSWER, repeated to narrow further (peril_class:FLOOD plus exposed_line:HULL drills one cell, not a row). Use field:__none__ for documents where the field named no recognised answer - the matrix's unattributed column. Up to four. window - 1h | 1d | 1w. Default 1d. limit - 1-100. Default 40. cursor - The previous response's cursor, for the next page. Null there means the end. |
DocumentGET /api/markets/{market}/signals/{uuid} | One collected document in full: article body, scores, topics, entities, extractions. | Parametersuuid - The document uuid, from any feed row. |
Executive overviewGET /api/markets/{market}/narrative | The latest AI-written narrative alone: past hour and past day as sanitised HTML fragments, with the written-at timestamp. | Parametersnone |
Overview historyGET /api/markets/{market}/narrative/history | Every stored executive overview, newest first - how the story developed rather than only where it stands. | Parameterslimit - 1-200. Default 24. |
SectionsGET /api/markets/{market}/sections | Discovery: which sections exist and how full each is, plus tier and extraction-marker counts for the window. | Parameterswindow - 1h | 1d | 1w. Default 1d. |
Topic pulseGET /api/markets/{market}/pulse | Mention volume, mean sentiment and a bucketed time series for a set of topic keys - what the pulse visuals poll. Up to twelve topics per call. | Parameterstopics - Comma-separated topic keys, e.g. CRUDE,LNG. Max 12. window - 1h | 1d | 1w. Default 1d. buckets - Series resolution, 6-48. Default 24. |
Entity rollupGET /api/markets/{market}/entities | The window's extracted entities ranked by mentions, each with counts of the documents carrying it that lean positive or negative. | Parameterswindow - 1h | 1d | 1w. Default 1d. limit - 1-100. Default 20. section - Optional: scope the rollup to one section. |
Field recordsGET /api/markets/{market}/records | Rows parsed from a list-shaped extraction field (injuries, contracts, trades, betting_lines), newest first, deduped on the first column so the latest telling wins. | Parametersfield - The list-shaped field name, e.g. injuries. columns - How many parts each record has, 2-8. window - 1h | 1d | 1w. Default 1d. limit - 1-60. Default 30. |
SearchGET /api/markets/{market}/search | Full-text search over everything stored for this market - documents and extraction findings folded together, best twenty matches first. Unwindowed: this is the route for "that tanker from three weeks ago". | Parametersq - Search terms, 2-200 characters. |
What one document looks like
The document, its provenance, the baseline read, and your market's own fields, each with the token that makes it a filter and the sentence that makes it a finding. Timestamps are captured as things happened, so the record can be replayed: what was known, and when.
Abridged and invented; the shape is real. Field names are your market's own, so yours will not be these.
{
"uuid": "6f2c…",
"subject": "Compressor fire takes 400,000 bpd offline; operator guides three weeks",
"source": { "siteName": "Wire report", "title": "…", "byline": "" },
"url": "https://…",
"publishedAt": "2026-08-14T07:02:00Z",
"collectedAt": "2026-08-14T07:11:00Z",
"topics": ["CRUDE", "GULF_COAST"],
"sections": ["supply"],
"tier": "alert",
"entities": ["…"],
"sentiment": -0.42,
"influence": 81,
"fields": {
"capacity_offline": {
"lead": "UNPLANNED",
"text": "400,000 bpd down after a compressor fire; operator guides three weeks",
"inVocabulary": true
}
},
"flags": ["capacity_offline", "capacity_offline:UNPLANNED"]
}Built to be depended on
A bearer key reads exactly one market and grants nothing else. Your market's administrator generates it and rotates it from the dashboard; rotating revokes the old key at once, which is also how you revoke it.
Every route and every tool reads. The engine writes; nothing a client or an agent can send changes what was collected or how it was scored, so an integration cannot corrupt the record it depends on.
A denied request is a 404, not a 403. Confirming that a market exists would tell whoever holds a key which clients we serve, so a bad key and a missing market look the same.
Every score and field carries the document it came from and the time it was captured, so an answer your assistant gives from the market can be opened, checked and defended.
The API and the MCP server come with the data layer, from $5,000 a month, and with every complete system. There is no public or sample dataset: a market is built for one organization and served to it alone.
Define it once. Query it for as long as it matters.
A scoping call is thirty minutes; a key is issued the day the market goes live. Bring the engineer who will connect it.