Feed API
HTTP, WebSocket, SSE and webhooks. Base URL https://api.langbot.ai.
Auth: Authorization: Bearer lb_<token> (API token) or the lb_session cookie set by the web app. Public endpoints need no auth. Timestamps are ms epoch integers. IDs are evt_ / ev_ / src_ / sub_ / tok_ + ULID.
Public
GET /healthGET /v1/events/live # live + upcoming events with sources + marketsGET /v1/events/{event_id}GET /v1/events/{event_id}/transcript?since=<seq>GET /v1/markets?q=&venue=&status=open # public metadataGET /v1/latency # per-source {source_id, kind, p50_lag_ms, p90_lag_ms, samples, last_ts}GET /v1/stats # sources this week, events today, p50 lag, installsGET /v1/calendar?days=7 # upcoming events with sources, markets, speaker_keys and rightsContext
GET /v1/context/{ticker} # Builder and aboveOne call for an agent: the market row and rules, the settlement source, the latest order-book snapshot (yes_bid, yes_ask, bids and asks as [price, count]), the base rate for the market's own speaker and phrase, the live or upcoming event it belongs to (with rights), and recent mentions.
{ "market": { "ticker": "KXFEDMENTION-26OCT-TRANSITORY", "venue": "kalshi", "title": "Powell says “transitory”?", "phrases": ["transitory"], "status": "open" }, "rules_text": "Resolves YES if Chair Powell says “transitory” during the press conference…", "settlement_source": "Federal Reserve press conference transcript", "book": { "ticker": "KXFEDMENTION-26OCT-TRANSITORY", "ts": 1760133600000, "yes_bid": 11, "yes_ask": 13, "bids": [[11, 240], [10, 410]], "asks": [[13, 180], [14, 350]] }, "base_rates": { "speaker": "powell", "phrase": "transitory", "markets": 8, "yes_rate": 0.375, "avg_open_price": 22.5, "avg_close_price": 37.1, "mispricing": 15.0, "by_month": [] }, "event": { "id": "ev_fomc_oct_2026_presser", "status": "upcoming", "starts_at": 1760151600000, "speaker_keys": ["powell"], "rights": "public_domain" }, "mentions": [ { "phrase": "transitory", "speaker": { "label": "SPK_1", "name": "Jerome Powell", "confidence": 0.93 }, "event_id": "ev_fomc_sep_2026_presser", "source_id": "src_fomc_sep_2026_cspan", "t_spoken": 1758654123412, "t_detected": 1758654123498, "lag_vs_public_ms": 9100, "market_refs": ["KXFEDMENTION-26SEP-TRANSITORY"] } ]}History
GET /v1/history/outcomes?speaker=&phrase=&series=&venue=&since=&until=&limit=&cursor=GET /v1/history/base-rates?speaker=&phrase=&series=&window_days=365GET /v1/history/speakers?q=GET /v1/history/mentions?speaker=&phrase=&since=&until=&limit=/v1/calendar returns {"events": [...]}; outcomes {"items": [...], "next_cursor": string|null}; speakers {"speakers": [...]}; mentions {"mentions": [...]}. Outcomes are the venue's public settlement record and are paginated by next_cursor. Base rates return {markets, yes, no, void, yes_rate, by_month[], avg_open_price, avg_close_price, mispricing}; mispricing is avg(result − yes_price_open) in cents. Mentions are Langbot's own phrase_detected facts and never include raw. Field-level detail and worked examples are in History and base rates. Every plan gets the full record; each call counts one history query of the plan's daily allowance.
Stream
WS /v1/stream?events=ev_a,ev_b&phrases=tariff,recession&types=phrase_detectedGET /v1/stream/sse # same filtersThe server sends {"type":"hello","seq":48213} first. You may send {"op":"subscribe","events":[...],"phrases":[...]} to change filters on an open socket.
Event
{ "id": "evt_01J9…", "type": "phrase_detected", "source_id": "src_nwb_q3_2026_dialin", "event_id": "ev_nwb_q3_2026", "market_refs": ["KXEARNINGSMENTIONNWB-26OCT-TARIFF"], "phrase": "tariff", "raw": "the bigger swing factor this quarter is tariff exposure", "speaker": { "label": "SPK_1", "name": "CFO", "confidence": 0.91 }, "confidence": 0.97, "t_spoken": 1760450523412, "t_detected": 1760450523503, "t_emitted": 1760450523507, "lag_vs_public_ms": 18000, "source_kind": "sip", "seq": 48213, "hash": "5d0e05c3…", "prev_hash": "b6e26232…", "raw_hash": "cd3e6e94…", "chain_seq": 2}type is one of phrase_detected, transcript_delta, speaker_change, event_started, event_ended, silence, source_status. source_status carries raw = JSON string with status (connecting / live / ended / error) and detail. lag_vs_public_ms is positive when Langbot heard the word before the public reference stream. The JSON Schema ships with both SDKs.
The last four fields are the event's receipt, set by the feed: hash is the sha256 of the canonical fact (including prev_hash), prev_hash is the hash of the previous event the feed emitted for the same source_id (null for the first), raw_hash is the sha256 of raw (survives redaction and retention; null when raw is null) and chain_seq is the 1-based position in the source's chain. They are null until the feed has seen the event. See Receipts.
Rights filter
Every public surface (/v1/stream, /v1/stream/sse, webhooks, /v1/events/{id}/transcript, MCP get_transcript) applies the source's rights. For public_domain and licensed sources events pass unchanged. For restricted and unknown sources (issuer earnings calls, broadcaster feeds) transcript_delta is not sent and raw on every other type is replaced by the matched phrase alone, so the example above would carry "raw": "tariff". The calendar reports rights per event so an agent knows in advance. See Legal.
Authenticated
POST /v1/subscriptions {event_id, phrases, webhook_url}GET /v1/subscriptionsDELETE /v1/subscriptions/{id} POST /v1/tokens {name} → {token} # shown onceGET /v1/tokensDELETE /v1/tokens/{id} GET /v1/me # {plan, plan_status, delays, limits, usage}POST /v1/agent/heartbeat # optional, from `langbot run`/v1/me returns the plan key (free | pro | team | enterprise), plan_status (active | past_due | canceled, absent on Free), delays (the delay on the record for this account, in seconds), limits and usage keyed requests_today, tokens, subscriptions, ws_connections. Plan keys map to the names on the pricing page: pro is Builder, team is Standard, enterprise is Desk and Dedicated.
Receipts
GET /v1/receipts/key # public; also /.well-known/langbot-receipts.jsonGET /v1/receipts/{evt_id} # public; {event, chain, checkpoint, rights, verified}POST /v1/receipts/verify {events:[...]} # public, ≤ 500; {results:[{id, status, computed_hash, stored_hash, chain_ok, chain_seq, checkpoint}]}GET /v1/receipts/checkpoints?source_id=&event_id=&limit=100 # publicGET /v1/events/{event_id}/replay?format=json|jsonl&books=0|1&mine=0|1&since_seq=&source_id=&types= # tokenPOST /v1/receipts/attest {evt_id, hash?, action, t_acted?} # tokenGET /v1/receipts/attestations?event_id=&evt_id=&since=&limit=100 # token, owner onlyLookup, verify, checkpoints and the key are public; replay and attestations need a token. Every plan can replay any recorded event; a replay counts one history query. Format, canonical form and the test vector are in Receipts.
Webhooks
Deliveries are POST with the JSON event as body and header X-Langbot-Signature: sha256=<hmac> computed over the raw body with your subscription secret. Retries with backoff for 5xx and timeouts.
Limits
Every feature is on every plan, Free included: webhooks, the full history, the fair-value and live models, Sim on any recorded event, MCP and every endpoint. Plans differ only in usage and speed.
Free (free) | Builder (pro) | Standard (team) | Desk and Dedicated (enterprise) | |
|---|---|---|---|---|
| Delay on the live record | +60 s | +10 s | +2 s | real time, capped seats |
| API requests | 60 / min | 600 / min | 3,000 / min | unlimited |
| Concurrent streams | 1 | 5 | 20 | unlimited |
| Subscriptions | 5 | 25 | 200 | 1,000 |
| API tokens | 5 | 20 | 100 | 500 |
| Webhook endpoints | 1 | 5 | unlimited | unlimited |
| Webhook deliveries | 100 / day | 10,000 / day | unlimited | unlimited |
| History queries (full archive) | 500 / day | unlimited | unlimited | unlimited |
| Fair-value and live model calls | 100 / day | unlimited | unlimited | unlimited |
| Sim runs (any recorded event) | 20 / month | 100 / month | unlimited | unlimited |
Days and months are UTC. A used-up allowance answers 429 with code: "quota_exceeded" and quota: {dimension, plan, used, limit, period, resets_at}; a held count at its cap (subscriptions, tokens, webhook endpoints) answers 402 with the same code. GET /v1/me returns quotas.<dimension> with used, limit, remaining and resets_at.