{
 "openapi": "3.0.3",
 "info": {
  "title": "pinnodds \u2014 Pinnacle Odds API",
  "version": "1.0.0",
  "description": "Real-time Pinnacle odds: live and prematch REST snapshots with full line depth (quarter lines included), player props via include_specials, and a queryable odds-drops buffer. Server-Sent Events drop streams (/odds-drop, /odds-drop-prematch) and a raw WebSocket feed (wss /ws/feed) are documented at https://pinnodds.com/docs \u2014 SSE/WS cannot be represented in OpenAPI paths. Free trial key at https://pinnodds.com \u2014 no card, no Pinnacle account.",
  "contact": {
   "name": "pinnodds",
   "url": "https://pinnodds.com",
   "email": "info@pinnodds.com"
  },
  "license": {
   "name": "Terms",
   "url": "https://pinnodds.com/docs"
  }
 },
 "servers": [
  {
   "url": "https://pinnodds.com"
  }
 ],
 "security": [
  {
   "ApiKeyHeader": []
  }
 ],
 "components": {
  "securitySchemes": {
   "ApiKeyHeader": {
    "type": "apiKey",
    "in": "header",
    "name": "x-api-key",
    "description": "Your pinnodds API key \u2014 it doubles as your account login."
   }
  }
 },
 "paths": {
  "/kit/v1/markets": {
   "get": {
    "summary": "Live or prematch markets for one sport",
    "operationId": "getMarkets",
    "description": "Full board for a sport. Sport ids: Soccer=1, Tennis=2, Basketball=3, Hockey=4, Football=5, Baseball=6, Rugby=7, MMA=8, Boxing=9, Other=10 (Volleyball+Handball combined), Esports=11, Golf=12, Cricket=13, Volleyball=14, Handball=15. Ids 14 and 15 are narrower views of 10 and overlap it — requesting 10, 14 and 15 returns those events twice.",
    "parameters": [
     {
      "name": "sport_id",
      "in": "query",
      "description": "Dropping-odds sport id",
      "required": true,
      "schema": {
       "type": "integer"
      },
      "example": 1
     },
     {
      "name": "event_type",
      "in": "query",
      "description": "live (default) or prematch",
      "required": false,
      "schema": {
       "type": "string",
       "enum": [
        "live",
        "prematch"
       ]
      },
      "example": "live"
     },
     {
      "name": "since",
      "in": "query",
      "description": "Delta cursor \u2014 pass the `last` value from the previous response",
      "required": false,
      "schema": {
       "type": "integer"
      }
     },
     {
      "name": "include_specials",
      "in": "query",
      "description": "1 adds player/team props, exact scores and futures as extra event rows (soccer prematch grows ~1.5k -> ~12k events)",
      "required": false,
      "schema": {
       "type": "string",
       "enum": [
        "1",
        "nested"
       ]
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Board envelope: sport_id, sport_name, last (cursor), events[]",
      "content": {
       "application/json": {
        "schema": {
         "type": "object"
        }
       }
      }
     },
     "401": {
      "description": "Missing or invalid API key"
     },
     "429": {
      "description": "Plan rate limit hit; retry_after_ms in the body"
     }
    }
   }
  },
  "/kit/v1/details": {
   "get": {
    "summary": "Single live event by id",
    "operationId": "getDetails",
    "parameters": [
     {
      "name": "event_id",
      "in": "query",
      "description": "Event id from a board response",
      "required": true,
      "schema": {
       "type": "integer"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "events[] with one entry",
      "content": {
       "application/json": {
        "schema": {
         "type": "object"
        }
       }
      }
     },
     "401": {
      "description": "Missing or invalid API key"
     },
     "429": {
      "description": "Plan rate limit hit; retry_after_ms in the body"
     }
    }
   }
  },
  "/kit/v1/prematch/fixtures": {
   "get": {
    "summary": "All prematch fixtures for a sport",
    "operationId": "getPrematchFixtures",
    "parameters": [
     {
      "name": "sport_id",
      "in": "query",
      "description": "Dropping-odds sport id",
      "required": true,
      "schema": {
       "type": "integer"
      },
      "example": 1
     },
     {
      "name": "since",
      "in": "query",
      "description": "Delta cursor",
      "required": false,
      "schema": {
       "type": "integer"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Fixtures with full markets envelope",
      "content": {
       "application/json": {
        "schema": {
         "type": "object"
        }
       }
      }
     },
     "401": {
      "description": "Missing or invalid API key"
     },
     "429": {
      "description": "Plan rate limit hit; retry_after_ms in the body"
     }
    }
   }
  },
  "/kit/v1/prematch/markets": {
   "get": {
    "summary": "Full markets for one prematch event",
    "operationId": "getPrematchMarkets",
    "parameters": [
     {
      "name": "event_id",
      "in": "query",
      "description": "Prematch event id",
      "required": true,
      "schema": {
       "type": "integer"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Markets for the event",
      "content": {
       "application/json": {
        "schema": {
         "type": "object"
        }
       }
      }
     },
     "401": {
      "description": "Missing or invalid API key"
     },
     "429": {
      "description": "Plan rate limit hit; retry_after_ms in the body"
     }
    }
   }
  },
  "/kit/v1/prematch/lines": {
   "get": {
    "summary": "Compact line view for one prematch event",
    "operationId": "getPrematchLines",
    "parameters": [
     {
      "name": "event_id",
      "in": "query",
      "description": "Prematch event id",
      "required": true,
      "schema": {
       "type": "integer"
      }
     },
     {
      "name": "market_type",
      "in": "query",
      "description": "moneyline | spread | total | team_total",
      "required": false,
      "schema": {
       "type": "string"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Compact lines",
      "content": {
       "application/json": {
        "schema": {
         "type": "object"
        }
       }
      }
     },
     "401": {
      "description": "Missing or invalid API key"
     },
     "429": {
      "description": "Plan rate limit hit; retry_after_ms in the body"
     }
    }
   }
  },
  "/api/drops": {
   "get": {
    "summary": "Recent odds drops (queryable buffer)",
    "operationId": "getDrops",
    "description": "Pull-style access to recent dropping odds. ALWAYS send max_age_sec \u2014 omitting it returns zero rows. For push delivery use the SSE streams (see /docs).",
    "parameters": [
     {
      "name": "mode",
      "in": "query",
      "description": "live (default) or prematch",
      "required": false,
      "schema": {
       "type": "string",
       "enum": [
        "live",
        "prematch"
       ]
      },
      "example": "live"
     },
     {
      "name": "max_age_sec",
      "in": "query",
      "description": "Only drops newer than this many seconds. Required in practice.",
      "required": true,
      "schema": {
       "type": "integer"
      },
      "example": 3600
     },
     {
      "name": "min_drop_pct",
      "in": "query",
      "description": "Minimum drop percent",
      "required": false,
      "schema": {
       "type": "number"
      },
      "example": 5
     },
     {
      "name": "sport_id",
      "in": "query",
      "description": "Filter to one sport",
      "required": false,
      "schema": {
       "type": "integer"
      }
     },
     {
      "name": "limit",
      "in": "query",
      "description": "Max rows",
      "required": false,
      "schema": {
       "type": "integer"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "{ total, drops[], meta } \u2014 rows carry market, designation, from/to, drop_pct",
      "content": {
       "application/json": {
        "schema": {
         "type": "object"
        }
       }
      }
     },
     "401": {
      "description": "Missing or invalid API key"
     },
     "429": {
      "description": "Plan rate limit hit; retry_after_ms in the body"
     }
    }
   }
  },
  "/health": {
   "get": {
    "summary": "Service status",
    "operationId": "getHealth",
    "responses": {
     "200": {
      "description": "Event counts, feed source, SSE client counts",
      "content": {
       "application/json": {
        "schema": {
         "type": "object"
        }
       }
      }
     },
     "401": {
      "description": "Missing or invalid API key"
     },
     "429": {
      "description": "Plan rate limit hit; retry_after_ms in the body"
     }
    }
   }
  },
  "/ping": {
   "get": {
    "summary": "Liveness probe (no auth)",
    "operationId": "getPing",
    "security": [],
    "responses": {
     "200": {
      "description": "\"ok\" as text/plain"
     }
    }
   }
  }
 }
}