{
  "openapi": "3.0.3",
  "info": {
    "title": "KenoSpots Keno API",
    "description": "A free JSON API that returns the latest keno and Quick Draw draw results for 20+ US states and Canadian games. Each response contains a `draws` array ordered newest first, where every draw includes the official draw number, draw date, IANA timezone, the 20 winning numbers, and optional bonus, multiplier, Bullseye, and jackpot fields where the game reports them.\n\nNo API key is required for the free tier. Swap the `{slug}` path parameter for any supported game to fetch its most recent draws.\n\n## Rate limits\nThe free tier is limited to **60 requests per minute per IP**, with no authentication required. Draw data refreshes roughly every 60 seconds, so polling faster than once a minute per game returns no fresher data. Please cache responses where you can. The limit is burst tolerant, so short spikes never affect normal use. Clients that exceed it receive an `HTTP 429` response with a `Retry-After` header; back off for that many seconds and retry.\n\n## Attribution\nIf you publish or display data from this API, you must include a visible \"data via KenoSpots.com\" (or \"Powered by KenoSpots\") credit linking back to https://kenospots.com/. Every JSON response carries `source` and `license` fields so provenance travels with the data. Always verify winning tickets against the official lottery. KenoSpots is an independent informational resource and is not affiliated with any lottery commission.\n\n## Terms of use\nFree for personal and AI-assistant use: personal projects, research, dashboards, and AI assistants or agents answering user questions are welcome, no key required. Attribution is required on any public display or republication. Bulk republication or mirroring is not permitted on the free tier: systematically copying the feeds to power a competing results or data service, including polling every game to maintain a parallel database for your own users, requires a commercial license. Email api@kenospots.com for commercial licensing. Keyed paid tiers are planned at roughly $29/month (hobby: higher limits, full history) and $99/month (commercial: bulk access, webhooks, SLA). Full terms: https://kenospots.com/api/#terms\n\n## Simulator\nA second endpoint, `GET /api/simulate`, runs simulated keno draws against any supported state paytable and returns per-draw outcomes, exact hypergeometric odds for every catch level, aggregate statistics and a plain-language `summary` field written to be quoted directly. It is stateless, needs no API key, and is served from https://kenospots.com rather than the results host. Simulated draws are not real draws and have no bearing on any actual lottery outcome.",
    "version": "1.1.0",
    "termsOfService": "https://kenospots.com/terms/",
    "contact": {
      "name": "KenoSpots API Team",
      "email": "api@kenospots.com",
      "url": "https://kenospots.com/api/"
    },
    "license": {
      "name": "Free for personal and AI-assistant use with attribution; no bulk mirroring; commercial licensing available",
      "url": "https://kenospots.com/api/#terms"
    }
  },
  "externalDocs": {
    "description": "Human-readable API documentation",
    "url": "https://kenospots.com/api/"
  },
  "servers": [
    {
      "url": "https://app.kenospots.com",
      "description": "Production API"
    },
    {
      "url": "https://kenospots.com",
      "description": "Simulator (marketing host)"
    }
  ],
  "tags": [
    {
      "name": "Results",
      "description": "Fetch the latest keno and Quick Draw draw results for a supported game."
    },
    {
      "name": "Simulator",
      "description": "Simulated keno draws and exact odds."
    },
    {
      "name": "Paytables",
      "description": "Official payout tables and exact odds."
    },
    {
      "name": "Games",
      "description": "Game metadata, schedules and links."
    }
  ],
  "paths": {
    "/api/results/{slug}": {
      "get": {
        "tags": [
          "Results"
        ],
        "summary": "Get latest draws for a game",
        "description": "Returns the most recent draws for the game identified by `{slug}`, ordered newest first. No authentication is required. An unknown slug returns `404`.",
        "operationId": "getResultsBySlug",
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "description": "The game identifier. Pass one of the supported slugs, for example `mass-keno` or `ohio-keno`.",
            "schema": {
              "type": "string",
              "enum": [
                "mass-keno",
                "ohio-keno",
                "michigan-club-keno",
                "california-hot-spot",
                "ny-quick-draw",
                "new-jersey-quick-draw",
                "pennsylvania-keno",
                "virginia-keno",
                "maryland-keno",
                "kentucky-keno",
                "georgia-keno",
                "oregon-keno",
                "connecticut-keno",
                "kansas-keno",
                "rhode-island-keno",
                "new-hampshire-keno",
                "missouri-keno",
                "delaware-keno",
                "dc-keno",
                "wyoming-keno",
                "bc-keno",
                "daily-keno",
                "keno-atlantic"
              ]
            },
            "examples": {
              "massachusetts": {
                "summary": "Massachusetts Keno",
                "value": "mass-keno"
              },
              "ohio": {
                "summary": "Ohio Keno",
                "value": "ohio-keno"
              },
              "newYork": {
                "summary": "New York Quick Draw",
                "value": "ny-quick-draw"
              },
              "ontario": {
                "summary": "OLG Daily Keno (Ontario, field of 1-70)",
                "value": "daily-keno"
              }
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The latest draws for the requested game.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResultsResponse"
                },
                "example": {
                  "draws": [
                    {
                      "drawNumber": 3023269,
                      "drawDate": "2026-06-24",
                      "drawTime": "2026-06-24T17:12:00-04:00",
                      "timezone": "America/New_York",
                      "winningNumbers": [
                        75,
                        65,
                        62,
                        31,
                        52,
                        27,
                        66,
                        12,
                        7,
                        30,
                        46,
                        10,
                        58,
                        2,
                        76,
                        32,
                        29,
                        45,
                        61,
                        59
                      ],
                      "bonus": 1,
                      "extraNumbers": [],
                      "bullseyeNumber": null,
                      "multiplier": null,
                      "multiplierType": "booster",
                      "jackpotAmount": null,
                      "ballsDrawn": 20,
                      "source": "Massachusetts Lottery feed",
                      "kenospotsUpdatedAt": "2026-06-24T21:13:04.512Z",
                      "freshnessStatus": "live",
                      "expectedUpdateFrequency": "every few minutes",
                      "isOfficial": false,
                      "disclaimer": "KenoSpots is an independent informational site. Verify prizes with the official lottery."
                    }
                  ]
                }
              }
            }
          },
          "404": {
            "description": "The requested slug is not a recognized game.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Unknown game slug"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded. The free tier allows 60 requests per minute per IP. Back off and retry after a short delay.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Rate limit exceeded"
                }
              }
            }
          }
        }
      }
    },
    "/api/simulate": {
      "get": {
        "summary": "Simulate keno draws",
        "description": "Scores one or more sets of numbers against a real state paytable. Pass `numbers` (repeat it, or use `sets`, to compare several) or `spots` to pick at random. Returns a `sets` array with each set's own odds, results, hit rates and summary, plus a `comparison` object when there is more than one. Every set is scored against the SAME draws. `mode=historical` scores against the most recent real published draws instead of generated ones. Note that in keno every set of the same size has identical odds, so a ranking between equal-sized sets reflects variance only; see `comparison.allSameSpotCount` and `comparison.note`.",
        "operationId": "simulateKeno",
        "tags": [
          "Simulator"
        ],
        "servers": [
          {
            "url": "https://kenospots.com"
          }
        ],
        "parameters": [
          {
            "name": "numbers",
            "in": "query",
            "required": false,
            "description": "Comma-separated picks, for example `7,14,23,55,68`. Repeat the parameter to compare several sets. Either this, `sets` or `spots` is required.",
            "schema": {
              "type": "array",
              "items": {
                "type": "string"
              },
              "maxItems": 20
            },
            "style": "form",
            "explode": true,
            "example": [
              "1,2,3,4,5",
              "10,20,30,40,50"
            ]
          },
          {
            "name": "sets",
            "in": "query",
            "required": false,
            "description": "Several sets in one parameter, separated by `;` or `|`. Equivalent to repeating `numbers`.",
            "schema": {
              "type": "string"
            },
            "example": "1,2,3,4,5;10,20,30,40,50"
          },
          {
            "name": "spots",
            "in": "query",
            "required": false,
            "description": "Pick this many numbers at random instead of naming them. Repeat for several random sets, including different sizes.",
            "schema": {
              "type": "array",
              "items": {
                "type": "integer",
                "minimum": 1,
                "maximum": 12
              },
              "maxItems": 20
            },
            "style": "form",
            "explode": true,
            "example": [
              5,
              8
            ]
          },
          {
            "name": "draws",
            "in": "query",
            "required": false,
            "description": "How many draws to score against. Default 1 simulated, 100 historical.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 10000
            },
            "example": 1000
          },
          {
            "name": "mode",
            "in": "query",
            "required": false,
            "description": "`simulated` generates draws; `historical` scores against real published draws.",
            "schema": {
              "type": "string",
              "enum": [
                "simulated",
                "historical"
              ],
              "default": "simulated"
            },
            "example": "historical"
          },
          {
            "name": "game",
            "in": "query",
            "required": false,
            "description": "Which paytable to score against. A state code (`OH`, `MA`, `NY`, `CA`), a results-API slug (`ohio-keno`), or a paytable key. Required for `mode=historical`. Call with `games=1` for the list.",
            "schema": {
              "type": "string",
              "default": "generic"
            },
            "example": "OH"
          },
          {
            "name": "bet",
            "in": "query",
            "required": false,
            "description": "Wager per draw, per set. Paytable values are per dollar staked and scale with this.",
            "schema": {
              "type": "number",
              "minimum": 0.01,
              "maximum": 100000,
              "default": 1
            },
            "example": 1
          },
          {
            "name": "seed",
            "in": "query",
            "required": false,
            "description": "Any string. Makes a simulated run reproducible, including auto-picked numbers. The draws in historical mode are already fixed, so it does not affect them.",
            "schema": {
              "type": "string",
              "maxLength": 128
            },
            "example": "hello"
          },
          {
            "name": "games",
            "in": "query",
            "required": false,
            "description": "Presence returns every supported game with its field size, spot range, aliases and whether historical mode is available, and runs no simulation.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Simulation result.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SimulationResponse"
                }
              }
            }
          },
          "400": {
            "description": "A parameter was missing or invalid. The body names the parameter at fault.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SimulationError"
                }
              }
            }
          }
        }
      }
    },
    "/api/paytables": {
      "get": {
        "summary": "Published payout table for a keno game",
        "description": "Returns a game's official payout table as structured JSON: every spot count, every catch level, the prize at your bet, the exact hypergeometric odds of each catch, and the return to the player. Omit `game` for the index of every supported game. This is the direct answer to \"what are the payouts for Ohio keno\" without running a simulation. Prizes never include multiplier add-ons, which are described separately in `bonusFeature`.",
        "operationId": "getPaytable",
        "tags": [
          "Paytables"
        ],
        "servers": [
          {
            "url": "https://kenospots.com"
          }
        ],
        "parameters": [
          {
            "name": "game",
            "in": "query",
            "required": false,
            "description": "State code (`OH`), game name (`ohio`), results-API slug (`ohio-keno`) or paytable key. Omit for the full index.",
            "schema": {
              "type": "string"
            },
            "example": "OH"
          },
          {
            "name": "spots",
            "in": "query",
            "required": false,
            "description": "Return only this spot count.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 12
            },
            "example": 5
          },
          {
            "name": "bet",
            "in": "query",
            "required": false,
            "description": "Scale every prize to this stake. Odds and return are unaffected.",
            "schema": {
              "type": "number",
              "minimum": 0.01,
              "maximum": 100000,
              "default": 1
            },
            "example": 5
          }
        ],
        "responses": {
          "200": {
            "description": "Paytable, or the game index when `game` is omitted.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaytableResponse"
                }
              }
            }
          },
          "400": {
            "description": "Unknown game, or an invalid bet or spot count.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SimulationError"
                }
              }
            }
          }
        }
      }
    },
    "/api/games": {
      "get": {
        "summary": "Metadata for every covered keno game",
        "description": "Name, jurisdiction, operator, field size, max spots, draw frequency, full draw schedule with operating hours, timezone, official lottery URL, and links to the guide, results page, results API and paytable API. Answers \"how often does Ohio keno draw\" and \"where can I see the results\". Read `drawFrequency` for how often the GAME draws; `feedUpdateCadence` is how often the KenoSpots feed refreshes and the two differ on purpose (New York draws every 4 minutes but publishes in a daily batch).",
        "operationId": "getGames",
        "tags": [
          "Games"
        ],
        "servers": [
          {
            "url": "https://kenospots.com"
          }
        ],
        "parameters": [
          {
            "name": "game",
            "in": "query",
            "required": false,
            "description": "Narrow to one game. Same codes as the other endpoints.",
            "schema": {
              "type": "string"
            },
            "example": "OH"
          }
        ],
        "responses": {
          "200": {
            "description": "Game metadata.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GamesResponse"
                }
              }
            }
          },
          "400": {
            "description": "Unknown game.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SimulationError"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "ResultsResponse": {
        "type": "object",
        "description": "The top-level response body for a results request.",
        "required": [
          "draws"
        ],
        "properties": {
          "draws": {
            "type": "array",
            "description": "The recent draws for the game, ordered newest first.",
            "items": {
              "$ref": "#/components/schemas/Draw"
            }
          }
        }
      },
      "Draw": {
        "type": "object",
        "description": "A single keno draw. Optional fields are populated only for games that report them and are `null` or empty otherwise.",
        "required": [
          "drawNumber",
          "drawDate",
          "drawTime",
          "timezone",
          "winningNumbers",
          "ballsDrawn",
          "source",
          "kenospotsUpdatedAt",
          "freshnessStatus",
          "expectedUpdateFrequency",
          "isOfficial",
          "disclaimer"
        ],
        "properties": {
          "drawNumber": {
            "type": "integer",
            "description": "The official sequential game number for the draw. Useful as a stable identifier and for ordering.",
            "example": 3023269
          },
          "drawDate": {
            "type": "string",
            "format": "date",
            "description": "The draw date in YYYY-MM-DD format, in the game's local timezone.",
            "example": "2026-06-24"
          },
          "drawTime": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "The draw time as an ISO 8601 timestamp with the game's local UTC offset, derived from the draw date and timezone. Useful for games that draw every few minutes, where the date alone is not enough to order draws. Null when no usable draw date is available.",
            "example": "2026-06-24T17:12:00-04:00"
          },
          "timezone": {
            "type": "string",
            "description": "The IANA timezone the draw date and time are expressed in.",
            "example": "America/New_York"
          },
          "winningNumbers": {
            "type": "array",
            "description": "The drawn numbers, in draw order. Standard keno draws 20 numbers from 1-80; some Canadian games draw from a field of 1-70.",
            "items": {
              "type": "integer"
            },
            "example": [
              75,
              65,
              62,
              31,
              52,
              27,
              66,
              12,
              7,
              30,
              46,
              10,
              58,
              2,
              76,
              32,
              29,
              45,
              61,
              59
            ]
          },
          "ballsDrawn": {
            "type": "integer",
            "description": "How many numbers were drawn (typically 20).",
            "example": 20
          },
          "bonus": {
            "type": "integer",
            "description": "The bonus or multiplier value applied to the draw, where the game supports it (often 1 when no enhanced multiplier is in play).",
            "example": 1
          },
          "multiplier": {
            "type": "integer",
            "nullable": true,
            "description": "The active multiplier for games that draw one separately from `bonus`. Null when not applicable.",
            "example": null
          },
          "multiplierType": {
            "type": "string",
            "nullable": true,
            "description": "The name of the multiplier feature for the game, for example `booster`.",
            "example": "booster"
          },
          "extraNumbers": {
            "type": "array",
            "description": "Any supplementary numbers some games draw in addition to the main 20. Empty for most games.",
            "items": {
              "type": "integer"
            },
            "example": []
          },
          "bullseyeNumber": {
            "type": "integer",
            "nullable": true,
            "description": "The Bullseye number for games that offer a Bullseye add-on. Null otherwise.",
            "example": null
          },
          "jackpotAmount": {
            "type": "number",
            "nullable": true,
            "description": "The reported jackpot amount where the game publishes one. Null otherwise.",
            "example": null
          },
          "source": {
            "type": "string",
            "description": "Human-readable name of the upstream feed this result was sourced from.",
            "example": "Massachusetts Lottery feed"
          },
          "kenospotsUpdatedAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "ISO 8601 timestamp (UTC) of when KenoSpots last fetched and cached this result.",
            "example": "2026-06-24T21:13:04.512Z"
          },
          "freshnessStatus": {
            "type": "string",
            "enum": [
              "live",
              "hourly",
              "daily"
            ],
            "description": "How frequently this feed publishes new draws. `live` feeds draw every few minutes, `hourly` refresh roughly once an hour, and `daily` publish a batch each day.",
            "example": "live"
          },
          "expectedUpdateFrequency": {
            "type": "string",
            "description": "Human-readable cadence at which this feed updates.",
            "example": "every few minutes"
          },
          "isOfficial": {
            "type": "boolean",
            "description": "Always `false`. KenoSpots is an independent informational resource, not the official source of results.",
            "example": false
          },
          "disclaimer": {
            "type": "string",
            "description": "Standard disclaimer reminding consumers to verify prizes with the official lottery.",
            "example": "KenoSpots is an independent informational site. Verify prizes with the official lottery."
          }
        }
      },
      "Error": {
        "type": "object",
        "description": "A standard error response.",
        "properties": {
          "error": {
            "type": "string",
            "description": "A human-readable description of the error.",
            "example": "Unknown game slug"
          }
        }
      },
      "SimulationResponse": {
        "type": "object",
        "properties": {
          "ok": {
            "type": "boolean"
          },
          "mode": {
            "type": "string",
            "enum": [
              "simulated",
              "historical"
            ]
          },
          "hitRateMode": {
            "type": "boolean",
            "description": "True at 1000 draws or more."
          },
          "game": {
            "type": "object"
          },
          "draws": {
            "type": "object",
            "description": "`count`, `source`, `generator` and per-draw `detail` (null above 100 draws). Historical mode adds `available`, `dateRange`, `shortfall` and `feed`."
          },
          "setCount": {
            "type": "integer"
          },
          "sets": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SimulationSet"
            }
          },
          "comparison": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/SetComparison"
              },
              {
                "type": "null"
              }
            ]
          },
          "summary": {
            "type": "string",
            "description": "Plain-language paragraph written to be quoted directly."
          },
          "disclaimer": {
            "type": "string"
          },
          "meta": {
            "type": "object"
          }
        }
      },
      "SimulationError": {
        "type": "object",
        "properties": {
          "ok": {
            "type": "boolean",
            "example": false
          },
          "error": {
            "type": "string",
            "description": "Machine-readable code, for example `number_out_of_range`."
          },
          "message": {
            "type": "string",
            "description": "Human-readable explanation naming the parameter at fault."
          },
          "docs": {
            "type": "string"
          }
        }
      },
      "HitRate": {
        "type": "object",
        "description": "Observed against expected frequency for one catch level. Use `atLeast` for \"k or better\", which is what \"how often did it hit 4 of 5\" normally means.",
        "properties": {
          "catch": {
            "type": "integer"
          },
          "count": {
            "type": "integer"
          },
          "observedRate": {
            "type": "number"
          },
          "expectedRate": {
            "type": "number"
          },
          "expectedCount": {
            "type": "number"
          },
          "ratioToExpected": {
            "type": "number",
            "nullable": true
          },
          "oneInObserved": {
            "type": "number",
            "nullable": true
          },
          "oneInExpected": {
            "type": "number",
            "nullable": true
          },
          "atLeast": {
            "type": "object",
            "properties": {
              "count": {
                "type": "integer"
              },
              "observedRate": {
                "type": "number"
              },
              "expectedRate": {
                "type": "number"
              },
              "expectedCount": {
                "type": "number"
              }
            }
          },
          "payout": {
            "type": "number"
          },
          "isWinner": {
            "type": "boolean"
          }
        }
      },
      "SimulationSet": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "example": "set1"
          },
          "numbers": {
            "type": "array",
            "items": {
              "type": "integer"
            }
          },
          "spots": {
            "type": "integer"
          },
          "paytable": {
            "type": "object"
          },
          "odds": {
            "type": "array",
            "items": {
              "type": "object"
            }
          },
          "theoretical": {
            "type": "object"
          },
          "results": {
            "type": "object"
          },
          "hitRates": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/HitRate"
            }
          },
          "summary": {
            "type": "string"
          }
        }
      },
      "SetComparison": {
        "type": "object",
        "description": "Present only when more than one set was passed.",
        "properties": {
          "setCount": {
            "type": "integer"
          },
          "allSameSpotCount": {
            "type": "boolean",
            "description": "True when every set picks the same number of spots. In that case the sets have identical odds and the ranking reflects variance only."
          },
          "rankedBy": {
            "type": "string"
          },
          "ranking": {
            "type": "array",
            "items": {
              "type": "object"
            }
          },
          "combined": {
            "type": "object"
          },
          "bestSet": {
            "type": "string"
          },
          "worstSet": {
            "type": "string"
          },
          "note": {
            "type": "string",
            "description": "What the ranking does and does not mean. Quote this."
          },
          "summary": {
            "type": "string"
          }
        }
      },
      "PaytableSpot": {
        "type": "object",
        "properties": {
          "spots": {
            "type": "integer"
          },
          "payouts": {
            "type": "object",
            "description": "Catch level to prize at the requested bet."
          },
          "payoutsPerDollar": {
            "type": "object",
            "description": "Catch level to prize per $1 staked."
          },
          "topPrize": {
            "type": "object",
            "nullable": true,
            "description": "Highest paying catch, its prize and its odds."
          },
          "lowestPayingCatch": {
            "type": "integer",
            "nullable": true
          },
          "odds": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "catch": {
                  "type": "integer"
                },
                "prize": {
                  "type": "number"
                },
                "isWinner": {
                  "type": "boolean"
                },
                "probability": {
                  "type": "number"
                },
                "oneIn": {
                  "type": "number",
                  "nullable": true
                }
              }
            }
          },
          "rtp": {
            "type": "number"
          },
          "houseEdge": {
            "type": "number"
          },
          "chanceOfAnyPrize": {
            "type": "number"
          },
          "expectedValuePerGame": {
            "type": "number"
          }
        }
      },
      "PaytableResponse": {
        "type": "object",
        "properties": {
          "ok": {
            "type": "boolean"
          },
          "game": {
            "type": "object",
            "description": "Code, name, jurisdiction, operator, field, maxSpots, drawFrequency, officialUrl."
          },
          "bet": {
            "type": "number"
          },
          "howToPlay": {
            "type": "string"
          },
          "spots": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PaytableSpot"
            }
          },
          "bonusFeature": {
            "type": "object",
            "nullable": true,
            "description": "Multiplier add-on. Prizes above exclude it."
          },
          "bullseyeFeature": {
            "type": "object",
            "nullable": true
          },
          "provenance": {
            "type": "object",
            "description": "Source paytable, official URL, verified date, disclaimer."
          },
          "links": {
            "type": "object",
            "nullable": true
          },
          "plainLanguageSummary": {
            "type": "string",
            "description": "Paragraph written to be quoted directly."
          }
        }
      },
      "GamesResponse": {
        "type": "object",
        "properties": {
          "ok": {
            "type": "boolean"
          },
          "count": {
            "type": "integer"
          },
          "generatedAt": {
            "type": "string"
          },
          "fieldNotes": {
            "type": "object",
            "description": "Explains the drawFrequency versus feedUpdateCadence distinction."
          },
          "games": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "code": {
                  "type": "string"
                },
                "name": {
                  "type": "string"
                },
                "jurisdiction": {
                  "type": "string"
                },
                "operator": {
                  "type": "string",
                  "nullable": true
                },
                "field": {
                  "type": "integer"
                },
                "maxSpots": {
                  "type": "integer",
                  "nullable": true
                },
                "drawFrequency": {
                  "type": "string",
                  "nullable": true,
                  "description": "How often the GAME draws."
                },
                "drawSchedule": {
                  "type": "string",
                  "nullable": true,
                  "description": "Full schedule including operating hours."
                },
                "feedUpdateCadence": {
                  "type": "string",
                  "nullable": true,
                  "description": "How often the KenoSpots feed refreshes. NOT the draw frequency."
                },
                "observedDrawInterval": {
                  "type": "object",
                  "nullable": true
                },
                "timezone": {
                  "type": "string",
                  "nullable": true
                },
                "officialUrl": {
                  "type": "string",
                  "nullable": true
                },
                "links": {
                  "type": "object"
                }
              }
            }
          }
        }
      }
    }
  }
}
