Snapshot first. Then ordered deltas.
Beta-0 bootstraps from an authenticated versioned REST snapshot, then opens an authenticated WebSocket at that declared snapshot position for contiguous ordered deltas.
Fetch a fresh snapshot
GET /v1/snapshot returns a coherent canonical view and a short-lived snapshot_position for the same filtered scope. If state changes before subscription, the stream rejects the stale position and the client refetches.
GET https://api.oddsloom.com/v1/snapshot?event_phase=pregame,live
Authorization: Bearer <api-key>
{
"schema_version": "1",
"type": "state.snapshot",
"sequence": 1,
"snapshot_position": 1,
"message_id": "550e8400-e29b-41d4-a716-446655440000",
"emitted_at": "2026-08-12T15:20:00.120Z",
"effective_scope": { ... },
"coverage_manifest": [ ... ],
"sources": [ ... ],
"snapshot": { ... }
}The effective scope is bounded by the release manifest. It includes canonical markets only, lists the exact books, jurisdictions, event phases, sports, leagues, and market families currently present, and reports degraded sources separately.
Connect to the stream
GET /v1/stream upgrades to a WebSocket session. The first application frame must authenticate within five seconds.
wss://stream.oddsloom.com/v1/streamUTF-8 JSON text frames.
Per-message compression is disabled.
Keys appear only in the first frame, never in URLs.
Slow clients are disconnected and must resnapshot.
Authenticate
Send session.authenticate first. Missing, disabled, and invalid keys close generically without exposing coverage.
{
"type": "session.authenticate",
"api_key": "ol_test_••••••••"
}Authentication accepted
The server confirms the non-secret key identifier. Control frames use sequence zero before a subscription exists.
{
"schema_version": "1",
"type": "session.authenticated",
"sequence": 0,
"message_id": "550e8400-e29b-41d4-a716-446655440001",
"emitted_at": "2026-08-12T15:20:00.981Z",
"key_id": "design-partner"
}Create one subscription
Declare the applied REST snapshot_position and the same filters. Unsupported filters or a stale position are rejected; the returned effective scope is never silently broadened.
{
"type": "subscription.create",
"snapshot_position": 1,
"filters": {
"book_ids": ["draftkings", "betmgm"],
"event_phases": ["pregame", "live"]
}
}snapshot_positionintegerPosition from the atomically applied REST snapshot.sport_idsstring[]?Canonical sport IDs.league_idsstring[]?Canonical league IDs.book_idsstring[]?Released sportsbook IDs.market_typesstring[]?Canonical market families.event_phasesenum[]?pregame, live, or both.Lifecycle
One connection owns one subscription. Its first relevant delta starts at sequence 1. On reconnect, a stale-position rejection, or any gap, discard uncommitted deltas, fetch a new REST snapshot, atomically replace state, and declare its position on a new subscription. Beta-0 has no replay path.