---
id: odds-model
title: Odds and Betting Market Data Model
description: Description of the odds, betting market groups, and outcomes returned by Sport Line API.
version: v1
language: en
---

# Odds and Betting Market Data Model

## What an Odds Object Represents

Each object inside `oc_list` describes a specific betting selection: a team win, a draw,
a total, a handicap, a player event, or another available outcome.

Example of a standard Prematch selection:

```json
{
  "oc_group_name": "1X2",
  "oc_name": "W1",
  "oc_rate": 1.525,
  "oc_size": 0,
  "oc_pointer": "730321837|1|1|0",
  "oc_block": false,
  "op_id": null
}
```

The complete field list is also available in the
[Sport Line API Field Reference](field-reference.md).

## Where Odds Are Returned

| Method | Contents of `game_oc_list` |
|---|---|
| [`events`](../api-reference/events.md) | Short list of the match's main markets and best odds |
| [`event`](../api-reference/event.md) | Full available list of markets and odds for a specific match |
| `topmatches?full=true` | Short list equivalent to `events` |
| `toplist?full=true` | Short list equivalent to `events` |
| `topmatches` and `toplist` without `full=true` | No odds |
| [`search`](../api-reference/search.md) | No odds |

To retrieve all available betting selections for a chosen match, request `event` using
its `game_id`.

## Betting Market in `game_oc_list`

`game_oc_list` contains betting markets. Each market groups related selections, such as
`1X2`, totals, handicaps, or player-specific bets.

| Field | Type | Description |
|---|---|---|
| `group_id` | number | Technical betting market ID |
| `group_name` | string | Market name in the requested language |
| `columns` | number | Recommended number of columns for displaying the market |
| `oc_list` | array | Market selections; the structure depends on the method |

Use `group_id` for application logic. `group_name` and `oc_group_name` are intended for
display and depend on the requested language.

### Market Structure in `events`

In the short match list, `oc_list` is a regular flat selection array:

```text
game_oc_list[]
└── betting market
    └── oc_list[]
        └── selection
```

Actual shortened example:

```json
{
  "group_id": 1,
  "group_name": "1X2",
  "columns": 3,
  "oc_list": [
    {
      "oc_name": "W1",
      "oc_rate": 1.36,
      "oc_pointer": "745865082|1|1|0"
    },
    {
      "oc_name": "X",
      "oc_rate": 5.35,
      "oc_pointer": "745865082|1|2|0"
    },
    {
      "oc_name": "W2",
      "oc_rate": 9.4,
      "oc_pointer": "745865082|1|3|0"
    }
  ]
}
```

`columns` indicates how to lay out the selections in the interface, but the `oc_list`
array in `events` is not divided into nested columns.

The same short structure is used by `topmatches?full=true` and
`toplist?full=true`.

### Market Structure in `event/group`

In the detailed response for a specific match, the outer `oc_list` array contains
columns, and each nested array contains the selections for that column:

```text
game_oc_list[]
└── betting market
    └── oc_list[]       ← market columns
        └── selections in the selected column
```

`columns` defines the recommended layout, but it is not a count of the nested arrays.
In actual responses, the number of items in the outer `oc_list` may differ from
`columns`. Do not require these values to be equal when validating a response.

The `group` format is recommended for a specific match because the API already:

- separates selections into betting markets;
- creates columns within each market;
- sorts related values within the columns;
- provides the recommended column count in `columns`.

Preserve the market, column, and selection order returned by the API. There is no need
to rebuild the columns or re-sort their values on the client side.

## Market Examples

### `1X2`: Three Columns

For the `1X2` market, the API returned three columns: first-team win, draw, and
second-team win.

```json
{
  "group_id": 1,
  "group_name": "1X2",
  "columns": 3,
  "oc_list": [
    [
      {
        "oc_name": "W1",
        "oc_rate": 1.525,
        "oc_size": 0,
        "oc_pointer": "730321837|1|1|0"
      }
    ],
    [
      {
        "oc_name": "X",
        "oc_rate": 5.08,
        "oc_size": 0,
        "oc_pointer": "730321837|1|2|0"
      }
    ],
    [
      {
        "oc_name": "W2",
        "oc_rate": 6.15,
        "oc_size": 0,
        "oc_pointer": "730321837|1|3|0"
      }
    ]
  ]
}
```

### Total: Two Columns

In a Total market, the first column contains Over selections and the second contains
Under selections. Values within each column are already sorted by `oc_size`.

```json
{
  "group_id": 17,
  "group_name": "Total",
  "columns": 2,
  "oc_list": [
    [
      { "oc_name": "Over 0.5", "oc_size": "0.5", "oc_rate": 1.019 },
      { "oc_name": "Over 1",   "oc_size": "1",   "oc_rate": 1.02 },
      { "oc_name": "Over 1.5", "oc_size": "1.5", "oc_rate": 1.085 }
    ],
    [
      { "oc_name": "Under 0.5", "oc_size": "0.5", "oc_rate": 22.0 },
      { "oc_name": "Under 1",   "oc_size": "1",   "oc_rate": 16.5 },
      { "oc_name": "Under 1.5", "oc_size": "1.5", "oc_rate": 5.45 }
    ]
  ]
}
```

This example is shortened. In the full response, each column contains more values.

### Player-specific Bets

A player-specific market may have `columns: 1` while containing several nested sets of
selections. In the saved Prematch response, the “Who Will Score Goal” market contained
two such sets. Use `op_id` to distinguish selections belonging to different players or
participants.

The available markets, number of columns, and set of selections depend on the sport and
the specific match. Do not create a fixed schema that supports only `1X2`, totals, or
handicaps.

## Selection Fields

| Field | Type | Description |
|---|---|---|
| `oc_group_name` | string | Betting market name in the requested language |
| `oc_name` | string | Name of the specific selection in the requested language |
| `oc_rate` | number | Current decimal odds |
| `oc_size` | string or number | Total, handicap, or another selection parameter; usually `0` for selections without a separate value |
| `oc_pointer` | string | Primary composite code of the bet or selection. It will be passed to the bet placement and settlement system |
| `oc_block` | boolean | `false` means the selection is available; `true` means it is blocked and unavailable |
| `op_id` | number or `null` | Player or participant ID for a player-specific selection, when applicable |

## `oc_group_name`

`oc_group_name` identifies the market to which the selection belongs:

```json
{
  "oc_group_name": "Total"
}
```

The name depends on the requested language. Do not use it as a permanent technical ID
or link data between languages using the name text.

The technical market ID is available as `group_id` in the parent object.

## `oc_name`

`oc_name` is the ready-to-display selection name:

```json
{
  "oc_name": "W1"
}
```

Other examples:

```text
X
Over 3.5
Under 3.5
1 -3.5
Yes
Erling Braut Haaland - Yes
```

The set of names depends on the sport and betting market:

- in football, the short list often contains `W1`, `X`, and `W2`;
- tennis does not have a draw;
- basketball often uses first-team and second-team wins instead of `1X2`;
- player-specific selection names may include the player's name.

Do not create a fixed schema that requires every match to have `W1`, `X`, and `W2`.
Display the markets and selections actually returned by the API.

## `oc_rate`

`oc_rate` contains the current decimal odds:

```json
{
  "oc_rate": 1.525
}
```

The value is a number, not a string. Live and Prematch odds may change with the next
response update.

When updating the interface:

1. Find the selection by `oc_pointer`.
2. Update the displayed `oc_rate` value.
3. Check the new `oc_block` value.
4. If the selection is no longer returned, do not continue displaying its old odds as
   current.

Do not use `oc_rate` as an identifier. The odds may change while the selection remains
the same.

## `oc_size`

`oc_size` contains a numeric value associated with the selection. For example:

- a total line;
- a handicap value;
- a player-event threshold;
- another value required by the specific bet type.

### Selection Without a Separate Value

```json
{
  "oc_name": "W1",
  "oc_size": 0
}
```

### Total

```json
{
  "oc_group_name": "Total",
  "oc_name": "Over 3.5",
  "oc_rate": 1.23,
  "oc_size": "3.5",
  "oc_pointer": "746146992|17|9|3.5",
  "oc_block": false,
  "op_id": null
}
```

### Negative Handicap

```json
{
  "oc_group_name": "Handicap",
  "oc_name": "1 -3.5",
  "oc_rate": 6.5,
  "oc_size": "-3.5",
  "oc_pointer": "730321837|2|7|-3.5",
  "oc_block": false,
  "op_id": null
}
```

In actual responses, `oc_size` may be either a number or a string. The client must
support both JSON types.

Do not check only the value type. For example, `0` may be a number, while `"3.5"` or
`"-3.5"` may be a string.

You may display the value as text. For calculations, first perform an explicit and safe
numeric conversion.

## `oc_pointer`

`oc_pointer` is the primary technical code of a bet or selection:

```json
{
  "oc_pointer": "730321837|2|7|-3.5"
}
```

The current implementation builds it from four segments:

```text
{game_id}|{group_id}|{outcome_type_id}|{oc_size}
```

For the example above:

| Segment | Value | Purpose |
|---|---|---|
| 1 | `730321837` | Match ID |
| 2 | `2` | Betting market ID |
| 3 | `7` | Internal selection type ID |
| 4 | `-3.5` | `oc_size` value |

In a client integration, treat `oc_pointer` as an opaque string:

- store it in full;
- compare the complete value;
- do not generate it yourself;
- do not replace it with `oc_name`;
- do not use individual segments as a unique ID.

`oc_name` depends on the language, while `oc_rate` may change. Use the complete
`oc_pointer` to match updates for the same selection.

### Use in the Bet Placement System

Sport Line API provides the sports line but does not itself place or settle bets. When
a separate bet placement and settlement service is connected to the project, pass the
complete `oc_pointer` to it as the code of the selected bet.

```text
selected Sport Line API selection
              ↓ oc_pointer
bet placement and settlement system
```

Pass the value exactly as received from Sport Line API. Do not build `oc_pointer`
yourself or replace it with the `oc_name` text.

Continue with the Coupon API documentation:

- [bet pointer format](https://sportapi.net/docs/coupon/bet-placement/bet-pointer.html);
- [coupon placement](https://sportapi.net/docs/coupon/bet-placement/place-coupon.html);
- [odds changes and blocked outcomes](https://sportapi.net/docs/coupon/bet-placement/odds-changes.html).

## `oc_block`

`oc_block` determines whether a selection is available.

Available selection:

```json
{
  "oc_rate": 1.525,
  "oc_block": false
}
```

Blocked selection from an actual Live response:

```json
{
  "oc_group_name": "Double Chance",
  "oc_name": "1X",
  "oc_rate": 1.001,
  "oc_size": 0,
  "oc_pointer": "746266481|8|4|0",
  "oc_block": true,
  "op_id": null
}
```

If `oc_block=true`, do not treat the selection as available merely because `oc_rate`
still contains a numeric value. Disable or hide it in the interface according to the
product's rules.

Check `oc_block` again with every update. Its status may change independently of the
odds value.

## `op_id`

For standard selections, `op_id` is usually `null`:

```json
{
  "op_id": null
}
```

For a player-specific bet, the field may contain a player or participant ID:

```json
{
  "oc_group_name": "Who Will Score Goal",
  "oc_name": "Erling Braut Haaland - Yes",
  "oc_rate": 1.53,
  "oc_size": "0.5",
  "oc_pointer": "730321837|119|5869|0.5",
  "oc_block": false,
  "op_id": 133826394
}
```

Do not require `op_id` for every selection. Use it only when the bet applies to a
specific player or participant.

> **Requires clarification:** in the saved Prematch response, several selections for
> different players in one player-specific market had the same `oc_pointer` but
> different `op_id` values. Until the bet placement system contract is clarified, store
> both `oc_pointer` and `op_id` for player-specific selections.

## Live and Prematch

The odds fields have the same purpose in Live and Prematch, but their data is updated
independently:

- Live and Prematch versions of a match have different `game_id` values;
- `oc_pointer` contains `game_id`, so the pointers will also differ;
- update odds and blocked states from the corresponding sports line type;
- do not carry Prematch odds into Live.

## Practical Integration Rules

1. Retrieve the short odds list from `events`.
2. For the full list, request `event` using the `group` format.
3. Use the complete `oc_pointer` to match standard selections between updates; also
   store `op_id` for player-specific selections.
4. Display the current `oc_rate`, not a previously stored value.
5. Always check `oc_block`.
6. Support both string and number values for `oc_size`.
7. Do not expect the same selection set for different sports.
8. Do not link Live and Prematch odds.
9. Preserve the market, column, and selection order returned in the `group` format.

## Related Documents

- [Match Data Model](match.md)
- [`events` — Match List](../api-reference/events.md)
- [`event` — Specific Match](../api-reference/event.md)
- [Field Reference](field-reference.md)
