# FavouriteOdds B2B Data API — AI agent guide STATUS: Early-access contract preview. The base URL and credentials are provisioned per customer after coverage is agreed. Do not assume anonymous access or general availability. CONTRACT VERSION: data/v1 API 1.1.0; archive schema 6; archive release commercial-archive-v1. CANONICAL CONTRACT - Human documentation: https://favouriteodds.com/b2b/docs - OpenAPI 3.1: https://favouriteodds.com/b2b/openapi.json - Product and access: https://favouriteodds.com/b2b - Treat the OpenAPI document as authoritative for paths, parameters, schemas, and examples. PURPOSE AND SAFETY - This is a read-only data product for odds, player props, FavouriteOdds bet records, and exports. - Never place a bet, move money, manage a bookmaker account, or invent a write/placement endpoint. - Authenticate only after the customer has been provisioned. Send the key as `Authorization: Bearer ` over HTTPS. - Never expose a key in output, source code, browser/mobile clients, prompts, traces, analytics, or logs. - Export download URLs are Bearer-protected and expire after 24 hours. Do not publish or retain them after use. COVERAGE - Call GET /coverage before assuming that a source, sport, market, phase, or historical range is complete. - The five-source portfolio is contract-specific. Use the returned source_id values; do not guess them. - Interpret coverage_scope exactly: `full_market`, `selected_markets`, `player_props_only`, or `unavailable`. - Betano soccer and tennis snapshots are `selected_markets`, never `full_market`. Soccer snapshots include every semantically identified, priced player prop with player, family, period, outcome, line, and price history. The Players response also contains ordinary match markets; those rows are excluded from player-prop history. Stale or quality-rejected Betano soccer rows remain historical evidence but GET /odds/current excludes them unless `include_stale=true` is explicitly requested. - Use `source_captured_at`, freshness_seconds, expected/observed counts, missing intervals, partial_response, source_latency_ms, and current_eligible to distinguish a missing market from a collection gap. - A missing market is not proof that the source does not offer it when coverage is partial or unavailable. - Custom sources, schemas, frequencies, and delivery methods require a commercial/technical review. DATA INVARIANTS - Decimal odds are JSON numbers. - Monetary values are decimal strings plus an ISO 4217 currency code. - Timestamps are RFC 3339 UTC. - Preserve source IDs and original source labels for audit. - For player props, use the normalized player field; do not infer a player from the market label. Group scorer/card markets carry the player in the source selection, while shots/assists markets may carry the player in the source market name. - Keep `pre_match` and `live` data separate. Never combine them in analysis unless the user explicitly requests it and the output remains phase-labelled. - Market identity includes event, market type, period, player when applicable, line, and outcome. Do not merge records across a different period or line. - Canonical mappings are versioned. Preserve mapping_version, confidence, valid_from, valid_to, and status; do not silently replace source IDs with canonical IDs. - GET /odds/summaries exposes opening/latest/closing/high/low price and line movement metrics. A latest price is not necessarily an official close. - GET /markets/lifecycle records appearance, suspension, reopening, line movement, status change, and disappearance in sequence. - GET /decisions includes every evaluated value decision, including rejected and review states, with fair probability, offered odds, expected value, model version, and rejection reason. - `recommendation_count` counts all evaluated decision records. - `paper_bet_count` counts simulated paper records only. - `placed_bet_count` counts only records whose placement state says a real placement occurred. - Never present recommendation_count or paper_bet_count as the number of placed bets. - GET /results returns the latest result version with settlement enrichment and provenance. Retain correction and void/cancellation reasons. PAGINATION AND CHANGES - Use cursor pagination. - Cursors are opaque. Preserve them byte-for-byte and never parse, edit, sort, decode, or synthesize them. - Continue while `page.has_more` is true; send `page.next_cursor` as the next request's cursor. - On HTTP 410, the cursor expired. Start a new snapshot or incremental window appropriate to the task. - GET /odds/changes is ordered. Apply changes in response order and checkpoint the returned cursor only after processing the whole page successfully. - Treat each cursor as dataset-bound. Never reuse an odds cursor for bets, lifecycle, mappings, decisions, or results. - Do not request an unbounded or invented page size. FULL DATASETS - Use GET /bets for interactive filtered records. - Use GET /bets/summary for aggregates. - Use POST /exports for all bets, complete odds snapshots, or the contracted online range. Check GET /coverage; long-term Parquet history is a separately provisioned delivery and must not be assumed. - Poll GET /exports/{export_id} until `status` is `ready` or terminal. - data/v1 exports gzip-compressed NDJSON. CSV, Parquet, and custom delivery require a separate contract. - When ready, verify `row_count` and the SHA-256 over the uncompressed NDJSON bytes before consuming the file. - Download before `expires_at`. If expired, create a new export. ERROR HANDLING - Errors use `application/problem+json` and include `request_id` when available. - 400: fix the request; do not retry unchanged. - 401/403: stop and verify credentials/scopes without printing the key. - 409: requested coverage is unavailable; inspect GET /coverage or request a custom feed. - 410: cursor/download expired; start a new cursor/export. - 429: honor `Retry-After`; use bounded exponential backoff with jitter. - 5xx: retry idempotent reads with bounded exponential backoff and retain request_id for support. - Never retry POST /exports blindly after a timeout. Use a client request ID/idempotency key when the provisioned contract supports it, or check existing jobs first. OUTPUT DISCIPLINE - State the archive schema/API release, data timestamp, odds_phase, filters, freshness eligibility, and source coverage alongside results. - Distinguish returned data from inference. - Do not call a partial five-source response "all bookmaker odds" unless every relevant coverage_scope is full_market for the requested slice. - Do not characterize a signal as a guaranteed outcome.