V1 / Protocol

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.

REST / V1

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.

WEBSOCKET / V1

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/stream
Encoding

UTF-8 JSON text frames.

Compression

Per-message compression is disabled.

Credentials

Keys appear only in the first frame, never in URLs.

Backpressure

Slow clients are disconnected and must resnapshot.

CLIENT → SERVER

Authenticate

Send session.authenticate first. Missing, disabled, and invalid keys close generically without exposing coverage.

{
  "type": "session.authenticate",
  "api_key": "ol_test_••••••••"
}
SERVER → CLIENT

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"
}
CLIENT → SERVER

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

REST SNAPSHOTOPEN STREAMAUTHDECLARE POSITIONORDERED DELTAS

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.

CONTINUEExplore the entity model