{
  "openapi": "3.1.0",
  "info": {
    "title": "ArbiScan Odds API",
    "version": "1.2.0",
    "description": "Local contract reference reviewed against backend sources on 2026-10-04. Fresh prematch reads, retained cursor v4 traversals (240 seconds), successful-response quota accounting. Examples are offline test-client responses with synthetic events; not live coverage or deployed-service verification.",
    "license": {
      "name": "Proprietary",
      "identifier": "LicenseRef-Proprietary"
    },
    "x-source-files": [
      "runtime/vps1/odds_api.py",
      "runtime/vps1/odds_pagination.py",
      "runtime/vps1/odds_ladders.py",
      "runtime/vps1/odds_realtime.py"
    ]
  },
  "servers": [
    {
      "url": "https://api.arbiscan.pl/v1"
    }
  ],
  "x-websocket": {
    "x-availability": "odds.changed unavailable while history is disabled; ready/heartbeat do not prove price delivery",
    "url": "wss://api.arbiscan.pl/v1/stream",
    "plan": "API Data Plus",
    "authentication": "X-API-Key request header",
    "queryParameters": [
      "cursor",
      "sports",
      "bookmakers"
    ],
    "messageTypes": [
      "ready",
      "odds.changed",
      "heartbeat",
      "error"
    ],
    "limits": {
      "connectionsPerAccount": 1,
      "changesPerMessage": 50,
      "pollIntervalMilliseconds": 1000
    }
  },
  "security": [
    {
      "ApiKeyAuth": []
    }
  ],
  "paths": {
    "/health": {
      "get": {
        "summary": "Service liveness and data freshness; no key, no quota charge.",
        "operationId": "getHealth",
        "security": [],
        "responses": {
          "200": {
            "description": "Current feed health",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Health"
                }
              }
            },
            "headers": {
              "X-Request-ID": {
                "schema": {
                  "type": "string"
                },
                "description": "Support correlation ID; never log API keys."
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "x-example-id": "health",
        "x-summary-pl": "Stan usługi i świeżość danych; bez klucza, bez opłaty za odczyt."
      }
    },
    "/sports": {
      "get": {
        "summary": "Sports with fresh prematch offers.",
        "operationId": "listSports",
        "responses": {
          "200": {
            "description": "Canonical sport names",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "sports"
                  ],
                  "properties": {
                    "sports": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  }
                }
              }
            },
            "headers": {
              "X-Request-ID": {
                "schema": {
                  "type": "string"
                },
                "description": "Support correlation ID; never log API keys."
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "x-example-id": "sports",
        "x-summary-pl": "Sporty z aktualnymi ofertami prematch."
      }
    },
    "/leagues": {
      "get": {
        "summary": "Leagues with fresh offers; optionally filter by sport.",
        "operationId": "listLeagues",
        "parameters": [
          {
            "name": "sport",
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "League names",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "leagues"
                  ],
                  "properties": {
                    "leagues": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  }
                }
              }
            },
            "headers": {
              "X-Request-ID": {
                "schema": {
                  "type": "string"
                },
                "description": "Support correlation ID; never log API keys."
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "x-example-id": "leagues",
        "x-summary-pl": "Ligi z aktualnymi ofertami; opcjonalnie jeden sport."
      }
    },
    "/bookmakers": {
      "get": {
        "summary": "Source names with fresh offers. Use the returned names in filters.",
        "operationId": "listBookmakers",
        "responses": {
          "200": {
            "description": "Bookmaker names",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "bookmakers"
                  ],
                  "properties": {
                    "bookmakers": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  }
                }
              }
            },
            "headers": {
              "X-Request-ID": {
                "schema": {
                  "type": "string"
                },
                "description": "Support correlation ID; never log API keys."
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "x-example-id": "bookmakers",
        "x-summary-pl": "Nazwy źródeł z aktualnymi ofertami. Używaj zwróconych nazw w filtrach."
      }
    },
    "/markets": {
      "get": {
        "summary": "Available market keys. A separate catalogue cursor, without the 240-second retained-result guarantee.",
        "operationId": "listMarkets",
        "parameters": [
          {
            "name": "sport",
            "in": "query",
            "schema": {
              "type": "string",
              "maxLength": 256
            }
          },
          {
            "$ref": "#/components/parameters/League"
          },
          {
            "name": "bookmaker",
            "in": "query",
            "schema": {
              "type": "string",
              "maxLength": 256
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 2000,
              "default": 500
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "description": "Opaque next_cursor value from the previous catalogue page.",
            "schema": {
              "type": "string",
              "maxLength": 2048
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Fresh market catalogue",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "count",
                    "markets",
                    "has_more",
                    "next_cursor"
                  ],
                  "properties": {
                    "count": {
                      "type": "integer",
                      "minimum": 0
                    },
                    "has_more": {
                      "type": "boolean"
                    },
                    "next_cursor": {
                      "type": [
                        "string",
                        "null"
                      ]
                    },
                    "markets": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/MarketCoverage"
                      }
                    }
                  }
                }
              }
            },
            "headers": {
              "X-Request-ID": {
                "schema": {
                  "type": "string"
                },
                "description": "Support correlation ID; never log API keys."
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "x-example-id": "markets",
        "x-summary-pl": "Dostępne klucze rynków. Osobny kursor katalogu, bez gwarancji zachowania zestawu przez 240 s."
      }
    },
    "/odds": {
      "get": {
        "summary": "Prematch events and odds with retained-result pagination.",
        "operationId": "listOdds",
        "description": "Cursor v4 retains the complete ordered result for 240 seconds after materialization. Continuations accept cursor alone (and an optional limit); if any filters are supplied, the complete normalized filter context must match. Cursors are bound to the quota subject. New publications and worker recycling do not invalidate an admitted traversal. Filter/subject mismatch: 400 cursor_filter_mismatch. include_stale is rejected even when false.",
        "parameters": [
          {
            "$ref": "#/components/parameters/Sport"
          },
          {
            "$ref": "#/components/parameters/League"
          },
          {
            "name": "bookmakers",
            "in": "query",
            "description": "Comma-separated values; at most 100 distinct values, each at most 256 characters. Case-insensitive exact matches; no fuzzy matching.",
            "schema": {
              "type": "string"
            },
            "x-description-pl": "Wartości oddzielone przecinkami: do 100 różnych wartości, każda do 256 znaków. Dokładne dopasowanie bez rozróżniania wielkości liter; bez wyszukiwania przybliżonego."
          },
          {
            "name": "markets",
            "in": "query",
            "description": "Comma-separated values; at most 100 distinct values, each at most 256 characters. Case-insensitive exact matches; no fuzzy matching.",
            "schema": {
              "type": "string"
            },
            "x-description-pl": "Wartości oddzielone przecinkami: do 100 różnych wartości, każda do 256 znaków. Dokładne dopasowanie bez rozróżniania wielkości liter; bez wyszukiwania przybliżonego."
          },
          {
            "name": "commence_from",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "commence_to",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "max_age_seconds",
            "in": "query",
            "description": "60–900 seconds. Defaults to the service freshness setting (900 by default); inspect freshness.max_age_seconds in the response.",
            "schema": {
              "type": "integer",
              "minimum": 60,
              "maximum": 900
            },
            "x-description-pl": "60–900 sekund. Domyślnie próg usługi (standardowo 900); sprawdź freshness.max_age_seconds w odpowiedzi."
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 500,
              "default": 200
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "description": "Opaque next_cursor value from the previous page.",
            "schema": {
              "type": "string",
              "maxLength": 2048
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Fresh prematch odds page",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OddsPage"
                }
              }
            },
            "headers": {
              "X-Request-ID": {
                "schema": {
                  "type": "string"
                },
                "description": "Support correlation ID; never log API keys."
              },
              "X-Odds-Snapshot-ID": {
                "schema": {
                  "type": "string"
                }
              },
              "X-Odds-Publication-Generation": {
                "schema": {
                  "type": "string"
                }
              },
              "X-Odds-Active-Generation": {
                "schema": {
                  "type": "string"
                }
              },
              "X-RateLimit-Limit": {
                "schema": {
                  "type": "string"
                }
              },
              "X-RateLimit-Remaining": {
                "schema": {
                  "type": "string"
                }
              },
              "X-RateLimit-Reset": {
                "schema": {
                  "type": "string"
                }
              },
              "X-DailyLimit-Limit": {
                "schema": {
                  "type": "string"
                }
              },
              "X-DailyLimit-Remaining": {
                "schema": {
                  "type": "string"
                }
              },
              "X-TotalLimit-Limit": {
                "schema": {
                  "type": "string"
                }
              },
              "X-TotalLimit-Remaining": {
                "schema": {
                  "type": "string"
                }
              },
              "Cache-Control": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "409": {
            "description": "snapshot_changed: traversal expired/missing; cursor_version_expired: legacy v1–v3 cursor. Restart and discard partial results.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "X-Request-ID": {
                "schema": {
                  "type": "string"
                },
                "description": "Support correlation ID; never log API keys."
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "description": "pagination_capacity: retry after Retry-After (2 seconds); narrow oversized results",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "X-Request-ID": {
                "schema": {
                  "type": "string"
                },
                "description": "Support correlation ID; never log API keys."
              },
              "Retry-After": {
                "schema": {
                  "type": "string"
                },
                "example": "2"
              }
            }
          }
        },
        "x-description-pl": "Kursor v4 zachowuje pełny uporządkowany wynik przez 240 s od przygotowania zestawu. Kontynuacja przyjmuje sam cursor i opcjonalnie limit. Jeśli podasz filtry, cały znormalizowany zestaw musi być identyczny. Kursor jest przypisany do puli konta. Nowa publikacja ani restart procesu nie unieważnia przyjętego kursora. Niezgodność filtrów lub konta: 400 cursor_filter_mismatch. include_stale jest odrzucany także z wartością false.",
        "x-example-id": "odds",
        "x-summary-pl": "Wydarzenia i kursy prematch z paginacją zachowanego zestawu.",
        "x-known-limit": "The current cursor decoder rejects offsets above 10000 events even though retention admits more. Partition broad queries by date and deduplicate inclusive boundaries; windows are separate snapshots."
      }
    },
    "/events/{event_id}": {
      "get": {
        "summary": "Current read of one event or alias; 410 after kickoff.",
        "operationId": "getEvent",
        "parameters": [
          {
            "$ref": "#/components/parameters/EventId"
          }
        ],
        "responses": {
          "200": {
            "description": "Event with current odds",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EventDetail"
                }
              }
            },
            "headers": {
              "X-Request-ID": {
                "schema": {
                  "type": "string"
                },
                "description": "Support correlation ID; never log API keys."
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "410": {
            "description": "event_expired: event has started or expired",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "X-Request-ID": {
                "schema": {
                  "type": "string"
                },
                "description": "Support correlation ID; never log API keys."
              }
            }
          }
        },
        "x-example-id": "event",
        "x-summary-pl": "Bieżący odczyt jednego wydarzenia lub jego aliasu; 410 po rozpoczęciu."
      }
    },
    "/events/{event_id}/history": {
      "get": {
        "summary": "History is unavailable: 403 without access, 410 after entitlement verification. Parameters below describe the contract if re-enabled.",
        "operationId": "getEventHistory",
        "description": "Currently unavailable: API Data Plus receives HTTP 410 with disabled=true. The retained contract describes observed changes up to 48 hours once history recording is re-enabled; it is not a current purchase benefit.",
        "x-availability": "disabled",
        "parameters": [
          {
            "$ref": "#/components/parameters/EventId"
          },
          {
            "name": "bookmaker",
            "in": "query",
            "schema": {
              "type": "string",
              "maxLength": 256
            }
          },
          {
            "name": "market",
            "in": "query",
            "schema": {
              "type": "string",
              "default": "h2h",
              "maxLength": 512
            }
          },
          {
            "name": "since",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "until",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 5000,
              "default": 1000
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "description": "Opaque continuation cursor. Keep event, market, bookmaker, since and until filters unchanged between pages.",
            "schema": {
              "type": "string",
              "maxLength": 2048
            }
          }
        ],
        "responses": {
          "410": {
            "description": "History recording is disabled; response includes disabled=true.",
            "headers": {
              "X-Request-ID": {
                "schema": {
                  "type": "string"
                },
                "description": "Support correlation ID; never log API keys."
              }
            }
          },
          "200": {
            "description": "Observed history rows and coverage metadata",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HistoryPage"
                }
              }
            },
            "headers": {
              "X-Request-ID": {
                "schema": {
                  "type": "string"
                },
                "description": "Support correlation ID; never log API keys."
              }
            }
          },
          "403": {
            "description": "The current plan does not include odds history",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "X-Request-ID": {
                "schema": {
                  "type": "string"
                },
                "description": "Support correlation ID; never log API keys."
              }
            }
          }
        },
        "x-example-id": "history_disabled",
        "x-summary-pl": "Historia jest niedostępna: 403 bez uprawnienia, 410 po sprawdzeniu uprawnienia. Poniższe parametry dotyczą kontraktu po ponownym włączeniu."
      }
    },
    "/webhooks": {
      "get": {
        "summary": "List API Data Plus webhook destinations",
        "operationId": "listWebhooks",
        "responses": {
          "200": {
            "description": "Webhook configurations without signing secrets",
            "headers": {
              "X-Request-ID": {
                "schema": {
                  "type": "string"
                },
                "description": "Support correlation ID; never log API keys."
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "headers": {
              "X-Request-ID": {
                "schema": {
                  "type": "string"
                },
                "description": "Support correlation ID; never log API keys."
              }
            }
          },
          "403": {
            "description": "Active API Data Plus access is required",
            "headers": {
              "X-Request-ID": {
                "schema": {
                  "type": "string"
                },
                "description": "Support correlation ID; never log API keys."
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "x-summary-pl": "Lista webhooków; API Data Plus.",
        "x-example-id": "webhooks"
      },
      "post": {
        "summary": "Create an API Data Plus webhook destination",
        "operationId": "createWebhook",
        "description": "The HMAC signing secret is returned once. Currently use snapshot.updated notifications and read current odds through REST. odds.changed has no price events while history recording is disabled.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "url"
                ],
                "properties": {
                  "url": {
                    "type": "string",
                    "format": "uri",
                    "maxLength": 2048
                  },
                  "events": {
                    "type": "array",
                    "minItems": 1,
                    "uniqueItems": true,
                    "items": {
                      "type": "string",
                      "enum": [
                        "odds.changed",
                        "snapshot.updated"
                      ]
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Webhook created; signing_secret is shown once",
            "headers": {
              "X-Request-ID": {
                "schema": {
                  "type": "string"
                },
                "description": "Support correlation ID; never log API keys."
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "description": "Missing or invalid API key",
            "headers": {
              "X-Request-ID": {
                "schema": {
                  "type": "string"
                },
                "description": "Support correlation ID; never log API keys."
              }
            }
          },
          "403": {
            "description": "Active API Data Plus access is required",
            "headers": {
              "X-Request-ID": {
                "schema": {
                  "type": "string"
                },
                "description": "Support correlation ID; never log API keys."
              }
            }
          },
          "409": {
            "description": "Three active webhook destinations already exist",
            "headers": {
              "X-Request-ID": {
                "schema": {
                  "type": "string"
                },
                "description": "Support correlation ID; never log API keys."
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "x-summary-pl": "Utworzenie webhooka; API Data Plus.",
        "x-example-id": "webhook_create"
      }
    },
    "/webhooks/{webhook_id}": {
      "delete": {
        "summary": "Disable a webhook destination",
        "operationId": "deleteWebhook",
        "parameters": [
          {
            "name": "webhook_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^wh_[0-9a-f]{32}$"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Webhook disabled",
            "headers": {
              "X-Request-ID": {
                "schema": {
                  "type": "string"
                },
                "description": "Support correlation ID; never log API keys."
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "headers": {
              "X-Request-ID": {
                "schema": {
                  "type": "string"
                },
                "description": "Support correlation ID; never log API keys."
              }
            }
          },
          "403": {
            "description": "Active API Data Plus access is required",
            "headers": {
              "X-Request-ID": {
                "schema": {
                  "type": "string"
                },
                "description": "Support correlation ID; never log API keys."
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "x-summary-pl": "Wyłącza webhook; API Data Plus.",
        "x-example-id": "webhook_delete"
      }
    },
    "/webhooks/{webhook_id}/test": {
      "post": {
        "summary": "Queue a signed test delivery",
        "operationId": "testWebhook",
        "parameters": [
          {
            "name": "webhook_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^wh_[0-9a-f]{32}$"
            }
          }
        ],
        "responses": {
          "202": {
            "description": "Test event queued",
            "headers": {
              "X-Request-ID": {
                "schema": {
                  "type": "string"
                },
                "description": "Support correlation ID; never log API keys."
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "headers": {
              "X-Request-ID": {
                "schema": {
                  "type": "string"
                },
                "description": "Support correlation ID; never log API keys."
              }
            }
          },
          "403": {
            "description": "Active API Data Plus access is required",
            "headers": {
              "X-Request-ID": {
                "schema": {
                  "type": "string"
                },
                "description": "Support correlation ID; never log API keys."
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "x-summary-pl": "Kolejkuje testową dostawę; API Data Plus.",
        "x-example-id": "webhook_test"
      }
    },
    "/usage": {
      "get": {
        "summary": "Shared account usage for managed keys; free read, 30/min.",
        "operationId": "getUsage",
        "description": "Does not consume the data quota; protected by a separate 30 requests/minute limit.",
        "responses": {
          "200": {
            "description": "Daily and lifetime quota usage",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Usage"
                }
              }
            },
            "headers": {
              "X-Request-ID": {
                "schema": {
                  "type": "string"
                },
                "description": "Support correlation ID; never log API keys."
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "x-example-id": "usage",
        "x-summary-pl": "Wspólne zużycie konta dla zarządzanych kluczy; odczyt bez opłaty, 30/min."
      }
    },
    "/events/{event_id}/ladders": {
      "get": {
        "summary": "Football corner/card/point ladders; source rules and completeness are not verified",
        "x-summary-pl": "Drabinki linii piłkarskich: rożne, kartki i punkty. Bez potwierdzenia zasad rozliczenia i kompletności źródła.",
        "parameters": [
          {
            "$ref": "#/components/parameters/EventId"
          },
          {
            "name": "bookmakers",
            "in": "query",
            "schema": {
              "type": "string",
              "maxLength": 1000
            },
            "description": "Comma-separated names; at most 20 distinct values, each at most 64 characters. All other query parameters are rejected. Defaults: Sts, Betclic, LvBet, Fuksiarz. Case-insensitive source matching.",
            "x-description-pl": "Nazwy oddzielone przecinkami: do 20 różnych wartości, każda do 64 znaków. Inne parametry są odrzucane. Domyślnie: Sts, Betclic, LvBet, Fuksiarz. Dopasowanie bez rozróżniania wielkości liter."
          }
        ],
        "responses": {
          "200": {
            "description": "Football ladders",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Ladders"
                }
              }
            },
            "headers": {
              "X-Request-ID": {
                "schema": {
                  "type": "string"
                },
                "description": "Support correlation ID; never log API keys."
              }
            }
          },
          "400": {
            "description": "invalid_filters",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "X-Request-ID": {
                "schema": {
                  "type": "string"
                },
                "description": "Support correlation ID; never log API keys."
              }
            }
          },
          "401": {
            "description": "invalid_api_key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "X-Request-ID": {
                "schema": {
                  "type": "string"
                },
                "description": "Support correlation ID; never log API keys."
              }
            }
          },
          "404": {
            "description": "event_not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "X-Request-ID": {
                "schema": {
                  "type": "string"
                },
                "description": "Support correlation ID; never log API keys."
              }
            }
          },
          "410": {
            "description": "event_expired",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "X-Request-ID": {
                "schema": {
                  "type": "string"
                },
                "description": "Support correlation ID; never log API keys."
              }
            }
          },
          "422": {
            "description": "unsupported_sport",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "X-Request-ID": {
                "schema": {
                  "type": "string"
                },
                "description": "Support correlation ID; never log API keys."
              }
            }
          },
          "429": {
            "description": "Rate limit",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "X-Request-ID": {
                "schema": {
                  "type": "string"
                },
                "description": "Support correlation ID; never log API keys."
              },
              "Retry-After": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        },
        "x-example-id": "ladders"
      }
    }
  },
  "components": {
    "securitySchemes": {
      "ApiKeyAuth": {
        "type": "apiKey",
        "in": "header",
        "name": "X-API-Key"
      }
    },
    "parameters": {
      "Sport": {
        "name": "sport",
        "in": "query",
        "schema": {
          "type": "string",
          "maxLength": 128
        },
        "description": "Case-insensitive exact name returned by the corresponding discovery endpoint.",
        "x-description-pl": "Dokładna nazwa zwracana przez odpowiedni katalog; wielkość liter nie ma znaczenia."
      },
      "League": {
        "name": "league",
        "in": "query",
        "schema": {
          "type": "string",
          "maxLength": 256
        },
        "description": "Case-insensitive exact name returned by the corresponding discovery endpoint.",
        "x-description-pl": "Dokładna nazwa zwracana przez odpowiedni katalog; wielkość liter nie ma znaczenia."
      },
      "EventId": {
        "name": "event_id",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string"
        }
      }
    },
    "responses": {
      "BadRequest": {
        "description": "Invalid query parameters",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "Unauthorized": {
        "description": "Missing, invalid, expired, disabled, or suspended-account API key",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "NotFound": {
        "description": "Fresh prematch event not found",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "RateLimited": {
        "description": "Per-minute, daily, or lifetime quota exceeded",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "required": [
          "error",
          "code"
        ],
        "properties": {
          "error": {
            "type": "string"
          },
          "code": {
            "type": "string"
          },
          "quota": {
            "$ref": "#/components/schemas/Quota"
          },
          "canonical_id": {
            "type": "string"
          },
          "disabled": {
            "type": "boolean"
          },
          "required_plan": {
            "type": "string"
          }
        }
      },
      "Health": {
        "type": "object",
        "required": [
          "ok",
          "data_fresh",
          "data_status",
          "events_known",
          "fresh_events_known",
          "stale_events_known",
          "active_max_age_seconds"
        ],
        "properties": {
          "ok": {
            "type": "boolean"
          },
          "data_fresh": {
            "type": "boolean",
            "description": "True only when at least one future event has both a fresh event observation and a fresh quote."
          },
          "data_status": {
            "type": "string",
            "enum": [
              "fresh",
              "degraded"
            ]
          },
          "events_known": {
            "type": "integer",
            "minimum": 0
          },
          "fresh_events_known": {
            "type": "integer",
            "minimum": 0
          },
          "stale_events_known": {
            "type": "integer",
            "minimum": 0
          },
          "active_max_age_seconds": {
            "type": "integer",
            "maximum": 900
          },
          "last_ingest": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "last_ingest_age_seconds": {
            "type": [
              "integer",
              "null"
            ]
          }
        }
      },
      "MarketCoverage": {
        "type": "object",
        "required": [
          "key",
          "family",
          "specifier",
          "event_count",
          "bookmaker_count"
        ],
        "properties": {
          "key": {
            "type": "string"
          },
          "family": {
            "type": "string"
          },
          "specifier": {
            "type": [
              "string",
              "null"
            ]
          },
          "event_count": {
            "type": "integer",
            "minimum": 1
          },
          "bookmaker_count": {
            "type": "integer",
            "minimum": 1
          }
        }
      },
      "Outcome": {
        "type": "object",
        "required": [
          "name",
          "price",
          "last_update"
        ],
        "properties": {
          "name": {
            "type": "string"
          },
          "price": {
            "type": "number",
            "exclusiveMinimum": 1
          },
          "last_update": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "Market": {
        "type": "object",
        "required": [
          "key",
          "last_update",
          "outcomes"
        ],
        "properties": {
          "key": {
            "type": "string"
          },
          "last_update": {
            "type": "string",
            "format": "date-time"
          },
          "outcomes": {
            "type": "array",
            "minItems": 1,
            "items": {
              "$ref": "#/components/schemas/Outcome"
            }
          }
        }
      },
      "Bookmaker": {
        "type": "object",
        "required": [
          "key",
          "last_update",
          "markets",
          "bookmaker_event_id"
        ],
        "properties": {
          "key": {
            "type": "string"
          },
          "last_update": {
            "type": "string",
            "format": "date-time"
          },
          "markets": {
            "type": "array",
            "minItems": 1,
            "items": {
              "$ref": "#/components/schemas/Market"
            }
          },
          "bookmaker_event_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "bookmaker_event_path": {
            "type": "string"
          },
          "bookmaker_event_slug": {
            "type": "string"
          }
        }
      },
      "Event": {
        "type": "object",
        "required": [
          "id",
          "sport",
          "discipline",
          "event_scope",
          "league",
          "source_leagues",
          "home_team",
          "away_team",
          "commence_time",
          "last_seen",
          "bookmakers",
          "canonical_id",
          "portfolio"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "sport": {
            "type": "string"
          },
          "discipline": {
            "type": "string"
          },
          "event_scope": {
            "type": "string"
          },
          "league": {
            "type": "string"
          },
          "home_team": {
            "type": "string"
          },
          "away_team": {
            "type": "string"
          },
          "commence_time": {
            "type": "string",
            "format": "date-time"
          },
          "last_seen": {
            "type": "string",
            "format": "date-time"
          },
          "source_leagues": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "bookmakers": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Bookmaker"
            }
          },
          "canonical_id": {
            "type": "string"
          },
          "portfolio": {
            "type": "object",
            "required": [
              "truncated",
              "reason",
              "outcome_limit",
              "outcomes_before",
              "outcomes_after"
            ],
            "properties": {
              "truncated": {
                "type": "boolean"
              },
              "reason": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "outcome_limit": {
                "type": [
                  "integer",
                  "null"
                ]
              },
              "outcomes_before": {
                "type": [
                  "integer",
                  "null"
                ]
              },
              "outcomes_after": {
                "type": [
                  "integer",
                  "null"
                ]
              }
            }
          }
        }
      },
      "Quota": {
        "type": "object",
        "properties": {
          "used": {
            "type": "integer",
            "minimum": 0
          },
          "limit": {
            "type": "integer",
            "minimum": 1
          },
          "minute_used": {
            "type": "integer",
            "minimum": 0
          },
          "minute_limit": {
            "type": "integer",
            "minimum": 1
          },
          "minute_remaining": {
            "type": "integer",
            "minimum": 0
          },
          "total_used": {
            "type": "integer",
            "minimum": 0
          },
          "total_limit": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 1
          },
          "remaining": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 0
          }
        }
      },
      "OddsPage": {
        "type": "object",
        "required": [
          "count",
          "events",
          "has_more",
          "next_cursor",
          "freshness",
          "snapshot",
          "quota"
        ],
        "properties": {
          "count": {
            "type": "integer",
            "minimum": 0,
            "maximum": 500
          },
          "events": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Event"
            }
          },
          "has_more": {
            "type": "boolean"
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ]
          },
          "freshness": {
            "type": "object",
            "required": [
              "mode",
              "max_age_seconds",
              "as_of"
            ],
            "properties": {
              "mode": {
                "const": "fresh_prematch"
              },
              "max_age_seconds": {
                "type": "integer",
                "minimum": 60,
                "maximum": 900
              },
              "as_of": {
                "type": "string",
                "format": "date-time"
              }
            }
          },
          "snapshot": {
            "$ref": "#/components/schemas/Snapshot"
          },
          "quota": {
            "$ref": "#/components/schemas/Quota"
          }
        }
      },
      "HistoryPoint": {
        "type": "object",
        "required": [
          "bookmaker",
          "outcome",
          "price",
          "timestamp"
        ],
        "properties": {
          "bookmaker": {
            "type": "string"
          },
          "outcome": {
            "type": "string"
          },
          "price": {
            "type": "number",
            "exclusiveMinimum": 1
          },
          "timestamp": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "HistoryPage": {
        "type": "object",
        "required": [
          "event_id",
          "market",
          "bookmaker",
          "count",
          "has_more",
          "next_cursor",
          "retention_hours",
          "observed_from",
          "observed_until",
          "history"
        ],
        "properties": {
          "event_id": {
            "type": "string"
          },
          "market": {
            "type": "string"
          },
          "bookmaker": {
            "type": [
              "string",
              "null"
            ]
          },
          "count": {
            "type": "integer",
            "minimum": 0,
            "maximum": 5000
          },
          "has_more": {
            "type": "boolean"
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ]
          },
          "retention_hours": {
            "const": 48
          },
          "observed_from": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "observed_until": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "history": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/HistoryPoint"
            }
          }
        }
      },
      "Snapshot": {
        "type": "object",
        "required": [
          "id",
          "generated_at",
          "accepted_at",
          "event_count",
          "outcome_count",
          "publication_generation",
          "changed_at",
          "active_generation"
        ],
        "properties": {
          "id": {
            "type": [
              "string",
              "null"
            ]
          },
          "generated_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "accepted_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "event_count": {
            "type": "integer",
            "minimum": 0
          },
          "outcome_count": {
            "type": "integer",
            "minimum": 0
          },
          "publication_generation": {
            "type": "integer",
            "minimum": 0,
            "description": "Changes after every accepted partial or authoritative odds write."
          },
          "changed_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "active_generation": {
            "type": "integer",
            "minimum": 0
          },
          "read_at": {
            "type": "string",
            "format": "date-time",
            "description": "Present on /odds; first-page read time, retained across the traversal."
          }
        }
      },
      "Usage": {
        "type": "object",
        "required": [
          "today",
          "total",
          "expires_at",
          "history"
        ],
        "properties": {
          "owner": {
            "type": "string"
          },
          "today": {
            "type": "object",
            "required": [
              "used",
              "limit"
            ],
            "properties": {
              "used": {
                "type": "integer",
                "minimum": 0
              },
              "limit": {
                "type": "integer",
                "minimum": 1
              }
            }
          },
          "total": {
            "type": "object",
            "required": [
              "used",
              "limit",
              "remaining"
            ],
            "properties": {
              "used": {
                "type": "integer",
                "minimum": 0
              },
              "limit": {
                "type": [
                  "integer",
                  "null"
                ]
              },
              "remaining": {
                "type": [
                  "integer",
                  "null"
                ]
              }
            }
          },
          "expires_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "history": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "date",
                "requests"
              ],
              "properties": {
                "date": {
                  "type": "string",
                  "format": "date"
                },
                "requests": {
                  "type": "integer",
                  "minimum": 0
                }
              }
            }
          }
        }
      },
      "EventDetail": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Event"
          },
          {
            "type": "object",
            "required": [
              "requested_id",
              "availability",
              "featured",
              "featured_rank"
            ],
            "properties": {
              "requested_id": {
                "type": "string"
              },
              "availability": {
                "type": "string",
                "enum": [
                  "available",
                  "stale",
                  "no_visible_markets"
                ]
              },
              "featured": {
                "type": "boolean"
              },
              "featured_rank": {
                "type": [
                  "integer",
                  "null"
                ]
              }
            }
          }
        ]
      },
      "Ladders": {
        "type": "object",
        "required": [
          "availability",
          "away_team",
          "bookmakers",
          "commence_time",
          "event_id",
          "home_team",
          "league",
          "portfolio",
          "price_format",
          "requested_id",
          "schema_version",
          "sport",
          "tax_applied"
        ],
        "properties": {
          "availability": {
            "type": "string"
          },
          "away_team": {
            "type": "string"
          },
          "bookmakers": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "availability",
                "bookmaker",
                "bookmaker_event_id",
                "excluded_invalid_markets",
                "ladders",
                "source_ladder_completeness"
              ],
              "properties": {
                "availability": {
                  "type": "string"
                },
                "bookmaker": {
                  "type": "string"
                },
                "bookmaker_event_id": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "excluded_invalid_markets": {
                  "type": "integer"
                },
                "ladders": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "required": [
                      "counting_system",
                      "family",
                      "line_count",
                      "lines",
                      "metric",
                      "participant",
                      "participant_name",
                      "period",
                      "rules_verified",
                      "settlement_rules"
                    ],
                    "properties": {
                      "counting_system": {
                        "type": "object",
                        "required": [
                          "basis",
                          "unit"
                        ],
                        "properties": {
                          "basis": {
                            "type": "string"
                          },
                          "unit": {
                            "type": "string"
                          },
                          "yellow_card_weight": {
                            "type": "integer"
                          },
                          "red_card_weight": {
                            "type": "integer"
                          }
                        }
                      },
                      "family": {
                        "type": "string"
                      },
                      "line_count": {
                        "type": "integer"
                      },
                      "lines": {
                        "type": "array",
                        "items": {
                          "type": "object",
                          "required": [
                            "line",
                            "market_key",
                            "over",
                            "pair_complete",
                            "under"
                          ],
                          "properties": {
                            "line": {
                              "type": "string"
                            },
                            "market_key": {
                              "type": "string"
                            },
                            "over": {
                              "type": [
                                "object",
                                "null"
                              ],
                              "required": [
                                "observed_at",
                                "price"
                              ],
                              "properties": {
                                "observed_at": {
                                  "type": "string"
                                },
                                "price": {
                                  "type": "number"
                                }
                              }
                            },
                            "pair_complete": {
                              "type": "boolean"
                            },
                            "under": {
                              "type": [
                                "object",
                                "null"
                              ],
                              "required": [
                                "observed_at",
                                "price"
                              ],
                              "properties": {
                                "observed_at": {
                                  "type": "string"
                                },
                                "price": {
                                  "type": "number"
                                }
                              }
                            }
                          }
                        }
                      },
                      "metric": {
                        "type": "string"
                      },
                      "participant": {
                        "type": "string"
                      },
                      "participant_name": {
                        "type": [
                          "string",
                          "null"
                        ]
                      },
                      "period": {
                        "type": "string"
                      },
                      "rules_verified": {
                        "type": "boolean"
                      },
                      "settlement_rules": {
                        "type": "string"
                      }
                    }
                  }
                },
                "source_ladder_completeness": {
                  "type": "string"
                }
              }
            }
          },
          "commence_time": {
            "type": "string"
          },
          "event_id": {
            "type": "string"
          },
          "home_team": {
            "type": "string"
          },
          "league": {
            "type": "string"
          },
          "portfolio": {
            "type": "object",
            "required": [
              "truncated",
              "reason",
              "outcome_limit",
              "outcomes_before",
              "outcomes_after"
            ],
            "properties": {
              "truncated": {
                "type": "boolean"
              },
              "reason": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "outcome_limit": {
                "type": [
                  "integer",
                  "null"
                ]
              },
              "outcomes_before": {
                "type": [
                  "integer",
                  "null"
                ]
              },
              "outcomes_after": {
                "type": [
                  "integer",
                  "null"
                ]
              }
            }
          },
          "price_format": {
            "type": "string"
          },
          "requested_id": {
            "type": "string"
          },
          "schema_version": {
            "type": "string"
          },
          "sport": {
            "type": "string"
          },
          "tax_applied": {
            "type": "boolean"
          }
        }
      }
    }
  }
}
