{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# DraftKings API quickstart\n",
    "\n",
    "Independent service, not affiliated with or endorsed by DraftKings. Technical access is not a redistribution license. No wagering.\n",
    "\n",
    "This notebook spends at most 6 requests. It uses only the Python standard library and has no stored outputs or credentials. Set `DRAFTKINGS_API_KEY` in the environment that launches Jupyter, or the next cell will ask for it without echoing."
   ],
   "id": "cell-0"
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import getpass\n",
    "import json\n",
    "import os\n",
    "from urllib.error import HTTPError\n",
    "from urllib.parse import urlencode\n",
    "from urllib.request import Request, urlopen\n",
    "\n",
    "API_KEY = os.environ.get(\"DRAFTKINGS_API_KEY\") or getpass.getpass(\"DraftKings API key: \")\n",
    "BASE = os.environ.get(\"DRAFTKINGS_API_BASE\", \"https://draftkingsapi.com\").rstrip(\"/\")\n",
    "budget = {\"left\": 6}\n",
    "\n",
    "\n",
    "def get(path, **params):\n",
    "    if budget[\"left\"] <= 0:\n",
    "        raise RuntimeError(\"Notebook request budget used. Raise it deliberately.\")\n",
    "    budget[\"left\"] -= 1\n",
    "    url = BASE + path + (\"?\" + urlencode(params) if params else \"\")\n",
    "    request = Request(url, headers={\"Authorization\": \"Bearer \" + API_KEY})\n",
    "    try:\n",
    "        with urlopen(request, timeout=20) as response:\n",
    "            return json.load(response)\n",
    "    except HTTPError as error:\n",
    "        raise RuntimeError(f\"HTTP {error.code}: {error.read()[:300].decode('utf8', 'replace')}\") from None\n"
   ],
   "id": "cell-1"
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Check coverage first\n",
    "\n",
    "Coverage reports what is stored. A sport or stat you expect may be absent, and rows may be stale."
   ],
   "id": "cell-2"
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "coverage = get(\"/api/v1/coverage\")\n",
    "for row in coverage[\"sportsbook_by_sport\"]:\n",
    "    print(row[\"sport\"], row[\"events\"], \"events\", row[\"offers\"], \"offers\", row[\"markets\"])\n"
   ],
   "id": "cell-3"
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Pick6 rows\n",
    "\n",
    "A row is a market, not a unique athlete. Standings multipliers are not decimal odds, probabilities or established payouts."
   ],
   "id": "cell-4"
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "page = get(\"/api/v1/pick6/projections\", sport=\"NHL\", stat=\"Shots on Goal\", limit=5)\n",
    "for row in page[\"data\"]:\n",
    "    name = (row.get(\"entities\") or [{}])[0].get(\"full_name\")\n",
    "    sides = [(s.get(\"side\"), s.get(\"standings_multiplier\")) for s in row.get(\"selections\", [])]\n",
    "    print(name, row[\"target_value\"], sides, \"age\", row[\"age_seconds\"], \"stale\", row[\"stale\"])\n"
   ],
   "id": "cell-5"
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Sportsbook mainlines\n",
    "\n",
    "As of 2026-11-03 these are ESPN-attributed DraftKings moneyline, spread and total observations, with no native props, alternates or futures. They are stored observations, not executable offers."
   ],
   "id": "cell-6"
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "page = get(\"/api/v1/odds\", sport=\"NBA\", market=\"total\", limit=5)\n",
    "for row in page[\"data\"]:\n",
    "    print(row[\"event_id\"], row[\"side\"], row[\"line\"], row[\"american\"], row[\"event_status\"], \"stale\", row[\"stale\"])\n"
   ],
   "id": "cell-7"
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## DFS salaries\n",
    "\n",
    "A draftable is a roster-slot record, so one athlete can appear several times. Salary is not a price or a line. A 403 here is a real access boundary."
   ],
   "id": "cell-8"
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "page = get(\"/api/v1/dfs/players\", sport=\"NFL\", limit=5)\n",
    "for row in page[\"data\"]:\n",
    "    data = row[\"data\"]\n",
    "    print(data.get(\"displayName\"), data.get(\"position\"), data.get(\"salary\"), \"slate\", row[\"slate_id\"], \"draftable\", data.get(\"draftableId\"))\n"
   ],
   "id": "cell-9"
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Independent statistics\n",
    "\n",
    "NFL seasons use the starting year. NBA and NHL use the ending year. These are limited retained samples, not full seasons, and were captured after the games were played."
   ],
   "id": "cell-10"
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "page = get(\"/api/v1/stats/player-games\", sport=\"NFL\", season=2026, limit=5)\n",
    "print(\"available\", page[\"available\"], \"next\", page[\"pagination\"][\"next\"])\n",
    "for row in page[\"data\"]:\n",
    "    stats = {s[\"key\"]: s[\"display\"] for s in row[\"data\"][\"stats\"] if s[\"scope\"] == \"game\"}\n",
    "    print(row[\"data\"][\"name\"], row[\"data\"][\"category\"], stats)\n"
   ],
   "id": "cell-11"
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Read the clocks\n",
    "\n",
    "`observed_at` is when we captured a row. `source_updated_at` stays null when the venue clock is unknown. Do not substitute one for the other."
   ],
   "id": "cell-12"
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "page = get(\"/api/v1/pick6/projections\", limit=1)\n",
    "row = page[\"data\"][0]\n",
    "print({k: row.get(k) for k in (\"observed_at\", \"source_updated_at\", \"age_seconds\", \"stale\")})\n",
    "print(\"Requests left in this notebook:\", budget[\"left\"])\n"
   ],
   "id": "cell-13"
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "name": "python"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}