History and base rates
The venue's public settlement record, searchable by speaker and phrase, with base rates and mispricing per pair.
Every mention market that has settled is a public fact: the venue published the rules, the open and close prices, and the result. Langbot's history scanner collects those facts into one table, market_outcomes, and keys each row by the speaker and the phrase the market was about. That table is what the history endpoints, the terminal and the MCP history tools read.
It is the venue's record, not ours. We add two things: a canonical speaker key (so "Chair Powell", "Powell" and "the Chair" are one speaker) and a phrase key taken from the market's own rules text. Because the record is public and factual, it is redistributable in full on every plan, including Free. The same is not true of transcripts; see Legal.
What a row looks like
{ "ticker": "KXFEDMENTION-26SEP-TRANSITORY", "venue": "kalshi", "series_ticker": "KXFEDMENTION", "event_ticker": "KXFEDMENTION-26SEP", "title": "Powell says “transitory”?", "speaker": "powell", "phrase": "transitory", "phrases": ["transitory"], "category": "fomc", "event_id": "ev_fomc_sep_2026_presser", "result": "no", "open_ts": 1757980800000, "close_ts": 1758654000000, "settled_ts": 1758661200000, "yes_price_open": 18, "yes_price_close": 2, "volume": 41250}result is yes, no or void. Prices are in cents. event_id is set when the market maps to an event Langbot listened to, which is how outcomes join to our own phrase_detected facts.
Endpoints
All four are public and need no auth. Every plan, Free included, sees the full record; plans differ only in how many history queries a day they include (Free 500, paid plans unlimited).
Outcomes
GET /v1/history/outcomes?speaker=powell&phrase=transitory&limit=50GET /v1/history/outcomes?series=KXEARNINGSMENTIONNVDA&since=1735689600000{ "items": [ { "...": "market_outcomes row" } ], "next_cursor": "eyJzZXR0bGVkX3RzIjoxNzU4NjYxMjAwMDAwfQ"}Filters: speaker, phrase, series, venue, since, until (ms epoch), limit (max 200). Pass next_cursor back as cursor for the next page; it is null on the last page.
Base rates
GET /v1/history/base-rates?speaker=powell&phrase=transitory&window_days=365{ "speaker": "powell", "phrase": "transitory", "window_days": 365, "markets": 8, "yes": 3, "no": 5, "void": 0, "yes_rate": 0.375, "by_month": [ { "month": "2026-03", "markets": 1, "yes_rate": 1.0 }, { "month": "2026-05", "markets": 1, "yes_rate": 0.0 }, { "month": "2026-06", "markets": 2, "yes_rate": 0.5 }, { "month": "2026-07", "markets": 1, "yes_rate": 0.0 }, { "month": "2026-09", "markets": 3, "yes_rate": 0.333 } ], "avg_open_price": 22.5, "avg_close_price": 37.1, "mispricing": 15.0}Omit phrase for a speaker-wide rate (how often any market on this speaker settles yes) or omit speaker for a phrase-wide rate across every speaker. series narrows either to one venue series.
Speakers
GET /v1/history/speakers?q=powReturns {"speakers": [...]}: speaker_profiles rows (speaker_key, display_name, role, org, aliases) with markets, mentions and the speaker-wide yes_rate.
Mentions
GET /v1/history/mentions?speaker=powell&phrase=tariff&limit=50Returns {"mentions": [...]}, Langbot's own facts: each time we detected the phrase, with phrase, speaker ({label, name, confidence}), event_id, source_id, t_spoken, t_detected, lag_vs_public_ms and market_refs. Never the surrounding text. Joining mentions to outcomes on event_id tells you how early in the event a phrase was said and what the market was pricing when it happened.
How base rates are computed
For a speaker and phrase pair inside the window:
marketscounts every settled row;yes,noandvoidpartition it.yes_rate = yes / (yes + no). Voids are excluded from the denominator.by_monthbuckets rows bysettled_tsin UTC and applies the same rate per bucket.avg_open_priceandavg_close_priceaverageyes_price_openandyes_price_closeover non-void rows, in cents.- Mispricing is the average of
result − yes_price_openover non-void rows, whereresultis 100 for yes and 0 for no. A positive number means the market opened cheaper than the outcome justified on average; a negative number means it opened rich. It is a historical summary of the venue's record, not a forecast for the next market.
Nothing in these numbers is a signal. They describe what has settled and at what price; what to do with that is your agent's decision.
SDK and CLI
from langbot import LangbotClient lb = LangbotClient(token="lb_...")rates = lb.base_rates(speaker="powell", phrase="transitory", window_days=365)print(rates.yes_rate, rates.mispricing) for o in lb.outcomes(speaker="powell", phrase="transitory"): print(o.ticker, o.result, o.yes_price_open, o.yes_price_close)import { LangbotClient } from "@langbot/sdk"; const lb = new LangbotClient({ token: process.env.LANGBOT_API_TOKEN });const rates = await lb.baseRates({ speaker: "powell", phrase: "transitory" });const page = await lb.outcomes({ speaker: "powell", phrase: "transitory", limit: 50 });langbot rates --speaker powell --phrase transitorylangbot context KXFEDMENTION-26OCT-TRANSITORYMCP
The same data as tools: get_base_rates(speaker?, phrase?, series?, window_days=365), search_outcomes(...), search_mentions(...), list_speakers(q?). Market context (get_market_context(ticker)) bundles the base rate for a market's own speaker and phrase with its rules, book and event. See The terminal for agents.
Exports
Standard and Desk and Dedicated can export any outcomes query as CSV from the terminal or with Accept: text/csv on /v1/history/outcomes. Columns match the JSON fields.