---
title: "Returns a sportsbook's native markets for a sport exactly as the source publishes them: no mapping, no team normalization, no merging. Each entry in data is one raw market blob keyed by the source's own market id."
method: GET
path: "/api/v2/{book}/{sport}"
tags: ["Source API (v2)"]
---

# Returns a sportsbook's native markets for a sport exactly as the source publishes them: no mapping, no team normalization, no merging. Each entry in data is one raw market blob keyed by the source's own market id.

`GET /api/v2/{book}/{sport}`

## Request

- Base URL: `https://api.owlsinsight.com`
- URL: `https://api.owlsinsight.com/api/v2/{book}/{sport}`
- Auth: HTTP bearer

Returns a sportsbook's native markets for a sport exactly as the source publishes them: no mapping, no team normalization, no merging. Each entry in data is one raw market blob keyed by the source's own market id.

marketIds narrows data to the listed market ids and prefixes (main,props-* or props-9524; case does not matter). Only those entries are returned, dated in meta.ageSeconds and hashed into the ETag, so a caller of one family keeps its 304s while another family moves. meta.marketIds echoes the filter and meta.marketsFilteredOut counts the entries left out. When the stored board exists but nothing matches, data is empty with meta.cached true and the status and age of the board's entries a default request returns, so it is not mistaken for an outage; if none of those is left (for example only DraftKings props-* is stored), meta.status is no-data. An entry that is neither an id (letters, digits, - or _) nor a prefix ending in * is a 400. bet365, hardrock, mybookie, stake, thunderpick and underdog ignore marketIds and always return their whole board.

DraftKings nba, nfl, nhl, mlb, ncaab and ncaaf hold main (the league page: game lines), live (the live feed snapshot, when it fits) and one props-<subcategoryId> entry per player-prop subcategory: DraftKings' response for that subcategory as received (parsed and re-serialized, not mapped), with its events, markets and selections. props-* entries refresh about every 3 to 5 minutes and are REST only (no draftkings-v2-update message). They are left out unless marketIds asks for them; meta.defaultExcluded then gives their count and the marketIds value that includes them. They are judged fresh against their own 480 second threshold, so they do not turn meta.status stale; meta.marketAges gives each returned entry's age in seconds. When one subcategory's request fails, its previous entry is kept for a cycle; if it reaches its 10 minute lifetime first, a request that includes it reads meta.status partial until the entry is cleared, at the latest on the next cycle.

meta.status is ok, stale, partial or no-data. It flips to stale once ageSeconds passes the book's own freshness threshold, which is set to that book's refresh cadence.

Send If-None-Match with the ETag response header, quoted exactly as received (a leading W/ is fine), to receive 304 when nothing changed. The body etag is the same value without quotes, so a bare etag never matches. A 304 on /api/v2 does not count toward your monthly quota; it still counts toward your per-minute limit and appears in your usage log. A key that has used up its monthly quota gets 429 even when the request would have been a 304.

meta.cached is false only when there is no stored board to serve (no-data, and partial-miss on the books that report it). It is not a freshness signal: read meta.ageSeconds and meta.status for that.

For a multi-league sport without league, most books return 400 with a hint to the /leagues index. Books flagged mergeWhenUnspecified instead return a merged board with league null, a leagues index attributing every market to its competition, and meta.merged true; if the merge had to truncate, meta.status is partial and meta.truncated says what was omitted.

A few books key data by event rather than by market, so one entry holds a whole game. Each book's sport set, single-league sports and league rules are in the v2Books entries of this manifest.

The SDKs expose one method per book: get<Book>V2 in TypeScript and get_<book>_v2 in Python.

The stake board is the exception to the meta shape above: it serves meta { source, cached, status, timestamp, ageSeconds, hash, now } on every response, with data as an array of fixtures. On no-data and partial-miss, timestamp, ageSeconds and hash are null because there is no board to date, and now is the response clock; never read freshness from a null timestamp.

On stake ice-hockey, a fixture also carries swish when Stake prices player props for it: Stake's player props (shots on goal, goals, assists, points, hits, plus minus, power play points, blocked shots, and goalie saves once Stake prices them) as { id, status, startTime, players: [{ id, name, position, markets: [{ id, stat: { name }, lines: [{ id, name, line, over, under, balanced, suspended }] }] }] }, over and under in decimal odds. Markets with no line are left out, and a game in progress has no swish block because Stake pulls player props once it starts.

propbuilder (Prop Builder) holds one entry per game and market family, family first: game-<gameId> (the game record with home, away, startTime and entries, the ids stored for that game), gfm-<gameId>-<statistic>-<hash> (one game-market group), ou-<gameId>-<statistic>-<hash> (Over/Under lines for one statistic), ss-<gameId>-<statistic>-<hash> (an N+ milestone ladder), ppd-<gameId> (Player Doubles) and h2h-<gameId> (player vs player). marketIds=ou-* returns every Over/Under entry, and game-<gameId>,ou-<gameId>-*,ss-<gameId>-*,gfm-<gameId>-*,ppd-<gameId>,h2h-<gameId> returns one game. gameId is the board's own game id, not the eventId of the /api/v1 endpoints. Odds are decimal, and a market's oddsOverride replaces odds when it is greater than 1. Games are listed until they start, and a market the board suspends or withdraws leaves its entry, as on the board itself. The board sells the game markets (gfm) as parlay legs only, and a leg's price is not the price of a parlay. Several sportsbooks embed this board, and its prices were identical on every site we compared. propbuilder-v2-update pushes changed entries within seconds: load this board on every connect, then apply the messages.

## Path parameters

- `book` string, required
- `sport` string, required

## Query parameters

- `league` string
- `marketIds` string

## Response `200`

OK

- V2BookResponse: Any v2 book's raw board. `data` is keyed by the book's market or event id and holds the book's own JSON. `league` is null on a merged response, which then lists `leagues`.
  - `data` unknown
  - `etag` string
  - `league` string, nullable
  - `leagues` V2LeagueSlice[]
    - `leagueKey` string
    - `leagueName` string
    - `marketCount` number, double
    - `marketIds` string[]
  - `marketCount` number, double
  - `meta` V2Meta
    - `ageSeconds` number, double
    - `cacheTimestamp` string
    - `cached` boolean
    - `duplicateMarketIdsDropped` number, double
    - `leagueCount` number, double
    - `leaguesOmitted` number, double
    - `marketsOmitted` number, double
    - `merged` boolean
    - `source` string
    - `status` string
    - `timestamp` string
    - `truncated` boolean
  - `sport` string
  - `success` boolean

---

[API](https://skmtc.dev/owlsinsight/apis/owls-insight-api.md) · [All operations](https://skmtc.dev/owlsinsight/apis/owls-insight-api/llms.txt) · [OpenAPI document](https://skmtc.dev/owlsinsight/apis/owls-insight-api/revisions/76c1ea016ff1?raw)
