> ## Documentation Index
> Fetch the complete documentation index at: https://docs.odds-api.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Native ID Mappings

> Link our events and selections to a bookmaker's own market and outcome ids, such as Polymarket condition ids and CLOB token ids, so you can match our prices to the bookmaker's own markets.

## Overview

`/v3/odds` tells you the price. To find that exact selection at the bookmaker, you need the bookmaker's own id for it. `/v3/mappings` gives you that link: for every selection we price, it returns the bookmaker's event, market and outcome ids, together with our event's teams, start time, sport and league.

For **Polymarket**:

| Our field | Polymarket id |
| - | - |
| `nativeEventId` | Event id (the same value as `bookmakerIds.Polymarket` in `/v3/odds`) |
| `marketId` | `conditionId` |
| `outcomeId` | CLOB token id |

For **Kalshi**:

| Our field | Kalshi id |
| - | - |
| `nativeEventId` | Event ticker. For `ML` this is the game ticker, the same value as `bookmakerIds.Kalshi` in `/v3/odds`. Spread and total contracts sit under their own event tickers |
| `marketId` | Contract (market) ticker |
| `outcomeId` | `yes` or `no` |

Kalshi outcome ids are always `yes` or `no`, so an `outcomeId` on its own is not unique. For Kalshi, look up a selection with `marketId` and `outcomeId` together.

The endpoint and response are the same for every bookmaker, so more books can be added without changes on your side.

<Note>
  Native id mappings are available on paid plans. The bookmaker you query must be in your bookmaker selection, as with `/v3/odds`.
</Note>

## Request

```bash theme={null}
curl "https://api.odds-api.io/v3/mappings?bookmaker=Polymarket&eventId=71515842&apiKey=YOUR_API_KEY"
```

### Modes

| Mode | Parameters | Returns |
| - | - | - |
| One event | `eventId` | Every mapping of the event |
| Several events | `eventIds` (up to 10) | Every mapping of those events |
| One market | `marketId` | Every outcome of that market |
| Several markets | `marketIds` (up to 10) | Every outcome of those markets |
| One outcome | `outcomeId` | The selection it maps to. Returns 400 when the id matches outcomes in more than one market, as Kalshi's `yes` and `no` do; add `marketId` |
| Market and outcome | `marketId` or `marketIds`, plus `outcomeId` | That outcome of those markets |
| Bulk sync | `bookmaker` with none of the above | Every mapping for upcoming and live events, one page at a time |

Every mode returns `{ "data": [...], "nextCursor": ... }`. `nextCursor` is always `null` for lookups and is only set in [bulk sync](#bulk-sync) while more pages remain. A lookup that matches more than 5,000 mappings returns 400: request fewer ids, add a `market` filter, or use bulk sync. A batch or bulk call counts as one request against your rate limit, like `/v3/odds/multi`.

### Query Parameters

| Parameter | Required | Description |
| - | - | - |
| `apiKey` | Yes | Your API key |
| `bookmaker` | Yes | Bookmaker name, for example `Polymarket` or `Kalshi` |
| `eventId` | No | Our event id |
| `eventIds` | No | Comma-separated list of up to 10 of our event ids. Use instead of `eventId` |
| `marketId` | No | The bookmaker's market id (Polymarket `conditionId`, Kalshi contract ticker) |
| `marketIds` | No | Comma-separated list of up to 10 market ids. Use instead of `marketId` |
| `outcomeId` | No | The bookmaker's outcome id (Polymarket CLOB token id, Kalshi `yes` or `no`) |
| `market` | No | Only this market, by its exact name as served in `/v3/odds`, for example `Spread` or `Totals (Games)`. Case-sensitive. Works in every mode |
| `active` | No | `true` for outcomes the bookmaker still lists, `false` for withdrawn ones. Default: both. Works in every mode |
| `sport` | No | Bulk sync only. Sport slug or name, for example `american-football` |
| `league` | No | Bulk sync only. League slug, for example `usa-nfl`. Requires `sport` |
| `limit` | No | Bulk sync only. Rows per page, 1 to 5000. Default 1000 |
| `cursor` | No | Bulk sync only. The `nextCursor` of the previous page |

`eventId`/`eventIds` cannot be combined with `marketId`/`marketIds`/`outcomeId`. Duplicate ids in a batch are ignored.

## Example Response

```json theme={null}
{
  "data": [
    {
      "eventId": 71515842,
      "home": "Pittsburgh Steelers",
      "away": "Cleveland Browns",
      "date": "2026-10-04T17:00:00Z",
      "sport": { "name": "American Football", "slug": "american-football" },
      "league": { "name": "USA - NFL", "slug": "usa-nfl" },
      "bookmaker": "Polymarket",
      "market": "ML",
      "side": "home",
      "nativeEventId": "885112",
      "marketId": "0x3b5c0c7e8a1f2d4b6c9e0a7d5f3b1c2e4a6d8f0b1c3e5a7d9f2b4c6e8a0d2f4b",
      "outcomeId": "71321045679252212594626385532706912750332728571942532289631379312455583992563",
      "outcomeLabel": "Steelers",
      "extra": { "question": "Steelers vs. Browns" },
      "active": true,
      "firstSeenAt": "2026-09-28T09:15:02Z",
      "lastCheckedAt": "2026-09-30T11:55:10Z"
    },
    {
      "eventId": 71515842,
      "home": "Pittsburgh Steelers",
      "away": "Cleveland Browns",
      "date": "2026-10-04T17:00:00Z",
      "sport": { "name": "American Football", "slug": "american-football" },
      "league": { "name": "USA - NFL", "slug": "usa-nfl" },
      "bookmaker": "Polymarket",
      "market": "Spread",
      "hdp": 2.5,
      "side": "home",
      "nativeEventId": "885112",
      "marketId": "0x9d41f6a2c8e03b7d5a1f4c6e8b0d2a4c6e8f0a2b4d6c8e0f2a4b6d8c0e2f4a6b",
      "outcomeId": "10457830011928374650192837465019283746501928374650192837465019283746",
      "outcomeLabel": "Browns",
      "extra": { "question": "Spread: Browns (-2.5)", "groupItemTitle": "Spread -2.5" },
      "active": true,
      "firstSeenAt": "2026-09-28T09:15:02Z",
      "lastCheckedAt": "2026-09-30T11:55:10Z"
    }
  ],
  "nextCursor": null
}
```

### Kalshi example

```bash theme={null}
curl "https://api.odds-api.io/v3/mappings?bookmaker=Kalshi&marketId=KXNFLSPREAD-26OCT04NEBUF-BUF3&outcomeId=no&apiKey=YOUR_API_KEY"
```

```json theme={null}
{
  "data": [
    {
      "eventId": 71600001,
      "home": "Buffalo Bills",
      "away": "New England Patriots",
      "date": "2026-10-04T17:00:00Z",
      "sport": { "name": "American Football", "slug": "american-football" },
      "league": { "name": "USA - NFL", "slug": "usa-nfl" },
      "bookmaker": "Kalshi",
      "market": "Spread",
      "hdp": -2.5,
      "side": "away",
      "nativeEventId": "KXNFLSPREAD-26OCT04NEBUF",
      "marketId": "KXNFLSPREAD-26OCT04NEBUF-BUF3",
      "outcomeId": "no",
      "outcomeLabel": "No: Buffalo wins by more than 2.5 points",
      "active": true,
      "firstSeenAt": "2026-09-28T09:15:02Z",
      "lastCheckedAt": "2026-09-30T11:55:10Z"
    }
  ],
  "nextCursor": null
}
```

Each Kalshi contract is a yes/no question. `yes` on "Buffalo wins by more than 2.5" is home -2.5, and `no` on the same contract is the other side of that line (away, shown with the home handicap -2.5). An `ML` contract per team works the same way: `yes` on the home team's ticker is `home`, and `no` on it is `away`. On a football `ML`, Kalshi lists a separate contract per result (home, away, tie), and we map the `yes` outcome of each. `Both Teams To Score` uses `side` `yes` and `no`.

## Response Fields

| Field | Description |
| - | - |
| `eventId` | Our event id |
| `home`, `away` | Our event's teams, in our orientation |
| `date` | Our event's start time |
| `sport`, `league` | Our event's sport and league, with name and slug as in `/v3/odds` |
| `bookmaker` | The bookmaker these ids belong to |
| `market` | Market name, the same as `name` in `/v3/odds` (`ML`, `Spread`, `Totals`, ...) |
| `hdp` | The line as `/v3/odds` shows it. On a `Spread` this is the home team's handicap, on both the home and the away row. Omitted on markets without a line |
| `side` | `home`, `away`, `draw`, `over`, `under`, `yes` or `no` |
| `nativeEventId` | The bookmaker's event id (Kalshi: the event ticker the contract belongs to) |
| `marketId` | The bookmaker's market id |
| `outcomeId` | The bookmaker's outcome id for this exact selection |
| `outcomeLabel` | The outcome's name at the bookmaker |
| `extra` | Bookmaker-specific details. For Polymarket: `question`, `groupItemTitle`, and `sourceEventId` when the contract sits on a separate Polymarket event (for example a "More Markets" page) |
| `active` | `false` once the bookmaker stopped listing the outcome. The row stays in the response so ids you stored keep resolving |
| `firstSeenAt` | When we first saw the mapping |
| `lastCheckedAt` | When we last confirmed it at the bookmaker |

## Bulk sync

To keep a local copy of every mapping, call `/v3/mappings` with only `bookmaker` (and optionally `sport`, `league`, `market` or `active`). It returns the mappings of every upcoming and live event we have linked to that bookmaker, ordered for paging:

```json theme={null}
{
  "data": [ { "eventId": 71515842, "market": "ML", "side": "home", "...": "..." } ],
  "nextCursor": "WyI3MTUxNTg0MiIsIjB4M2I1Yy4uLiIsIjcxMzIxLi4uIl0"
}
```

Pass `nextCursor` back as `cursor` until it is `null`. Each page is cached for about 30 seconds, so there is no benefit in polling faster than that.

```python theme={null}
import requests

API = "https://api.odds-api.io/v3"

def sync_mappings(bookmaker, api_key, **filters):
    params = {"apiKey": api_key, "bookmaker": bookmaker, "limit": 5000, **filters}
    mappings = {}
    while True:
        page = requests.get(f"{API}/mappings", params=params, timeout=30).json()
        for m in page["data"]:
            mappings[(m["marketId"], m["outcomeId"])] = m
        if page["nextCursor"] is None:
            return mappings
        params["cursor"] = page["nextCursor"]

polymarket = sync_mappings("Polymarket", "YOUR_API_KEY")
kalshi_nfl = sync_mappings("Kalshi", "YOUR_API_KEY", sport="american-football", league="usa-nfl")
```

Key your copy on `(marketId, outcomeId)`: for Kalshi the `outcomeId` alone repeats across contracts. Bulk sync covers events that start in the future or are live. Events that started more than 6 hours ago, or are settled or cancelled, drop out of it; look those up by `eventId` instead.

## Orientation

`market`, `hdp`, `side`, `home` and `away` always follow our event's home and away, the same as `/v3/odds`. Polymarket and Kalshi often list a game the other way round ("Away vs. Home" or "Away at Home"). When they do, we swap the sides and flip the sign of `hdp` for you, and team markets such as `Team Total Home` take our name for that team. A `side: "home"` row is the outcome that pays when our home team covers, whatever the order at the bookmaker.

To match a price to its id, join on `eventId`, `market`, `hdp` and `side`:

```python theme={null}
import requests

API = "https://api.odds-api.io/v3"
params = {"apiKey": "YOUR_API_KEY", "eventId": 71515842}

odds = requests.get(
    f"{API}/odds", params={**params, "bookmakers": "Polymarket", "markets": "ML,Spread,Totals"}
).json()
mappings = requests.get(f"{API}/mappings", params={**params, "bookmaker": "Polymarket"}).json()["data"]

token_for = {
    (m["market"], m.get("hdp"), m["side"]): m["outcomeId"]
    for m in mappings
    if m["active"]
}

for market in odds["bookmakers"].get("Polymarket", []):
    for line in market["odds"]:
        home_token = token_for.get((market["name"], line.get("hdp"), "home"))
        print(market["name"], line.get("hdp"), line.get("home"), home_token)
```

## Things to Know

* **Mappings do not depend on prices.** We refresh them separately from prices. When a book is briefly empty and the price disappears from `/v3/odds`, the mapping is still returned.
* **Polymarket refreshes stop at kickoff.** Mappings of in-play Polymarket events are still returned, with the ids we last confirmed before kickoff, but `lastCheckedAt` no longer moves and new in-play contracts are not added.
* **Spreads are often listed twice.** Polymarket lists many spreads once from each team's side, for example "Steelers (-2.5)" and "Browns (-2.5)". These are two different lines (home +2.5 and home -2.5 in our orientation), each with its own `marketId` and tokens, and both are returned.
* **Football results are three contracts.** A football `ML` on Polymarket is three Yes/No markets (home, draw, away). We map the Yes token of each, so each side has its own `marketId`.
* **Tennis totals.** `Totals` is total sets and `Totals (Games)` is total games, the same names `/v3/odds` uses.
* **Only confirmed sides are mapped.** If we cannot tell from the outcome names which team a token belongs to, we leave it out rather than guess. For Kalshi, a contract is mapped to a side only when Kalshi's own ticker identifies the team.
* **Cache locally.** Mappings change rarely. Run a [bulk sync](#bulk-sync) on start and refresh it every few minutes, then look up anything new with `eventIds`, `marketIds` or `outcomeId`.

## Next Steps

<CardGroup cols={2}>
  <Card title="Prediction Markets" icon="chart-line" href="/guides/prediction-markets">
    How Polymarket and Kalshi prices appear in /v3/odds
  </Card>

  <Card title="Fetching Odds" icon="download" href="/guides/fetching-odds">
    Fetch the prices these ids belong to
  </Card>
</CardGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.