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

# 串关 - 多关组合定价

> 使用 OddsPapi 的 oddsIds 为串关和带相关性的同场串关（SGP）定价。并列比较多家博彩公司、读取相关性加成，并正确处理被暂停或缺失的各关。

两个端点可以把一组 `oddsIds` 变成一个多关组合赔率：

| 端点                          | 定价方           | 各关可跨      | 相关性       |
| --------------------------- | ------------- | --------- | --------- |
| `GET /fixtures/odds/parlay` | OddsPapi 本地计算 | 多个赛事与运动项目 | 无——简单相乘   |
| `GET /fixtures/odds/sgp`    | 博彩公司自己        | 必须是同一个赛事  | 有，由博彩公司报价 |

两者使用相同的 `oddsIds` 参数，返回的也是您已经熟悉的赛事视图——只是**新增**了一个部分。

关于围绕这两个端点的工作流——如何在两者之间选择、如何让实时投注单保持报价、
某一关暂停时如何降级——参见[构建投注单](/zh/guides/parlays)。

***

## 选择各关

每一关由它的 `oddsId` 标识：

```
{fixtureId}:{bookmaker}:{outcomeId}:{playerId}
```

这正是您从 `/fixtures/odds` 拿到的那个键。以逗号或空格分隔传入：

```bash theme={null}
curl -H 'X-API-Key: YOUR_KEY' \
  'https://v5.oddspapi.io/zh/fixtures/odds/parlay?oddsIds=id1400003160574217:bet365:141:0,id1400003160574217:bet365:146:0'
```

* 重复项会被去除、列表会被排序，因此同一组选择总是产生同一个请求。
* 每次请求**最多 20 关**。
* 各关会**按博彩公司**分组——在多家博彩公司请求同样的选择即可并列比价。

`parlayId` 即 `parlay` / `sgp` 部分内部的键，就是这个排序后的列表以 `,` 连接。
两个端点的构造方式相同，因此同一组选择的 `/parlay` 与 `/sgp` 条目可以直接比较。

***

## 返回内容

两个端点都返回普通的赛事视图，只是新增了一个部分。

| 部分               | 内容                                     |
| ---------------- | -------------------------------------- |
| 赛事元数据            | 平铺在顶层（`status`、`sport`、`tournament` 等） |
| `odds`           | 所请求的各关，**包括 inactive 的关**              |
| `bookmakers`     | 所请求博彩公司的 `BookmakerFixtureMeta`        |
| `parlay` / `sgp` | 多关组合赔率，先按博彩公司、再按 `parlayId` 索引         |
| `missingOddsIds` | 未能找到的关——仅在确有缺失时出现                      |

`parlay` / `sgp` 部分为空是正常情况，参见[被暂停 vs 无法找到](#被暂停-vs-无法找到)。

`/fixtures/odds/parlay` 返回一个**数组**——每个涉及的赛事一个对象，按 `fixtureId` 排序。
`/fixtures/odds/sgp` 返回**单个对象**，因为它的各关都属于同一个赛事。

***

## 串关：本地定价

新增的那个部分——以 3.8 和 2.1 两关为例：

```json theme={null}
"parlay": {
  "bet365": {
    "id1400003160574217:bet365:141:0,id1400003160574217:bet365:146:0": {
      "bookmaker": "bet365",
      "active": true,
      "price": 7.98,
      "priceFractional": "349/50",
      "priceAmerican": 698,
      "singlesPrice": 7.98,
      "changedAt": 1766667441097
    }
  }
}
```

`price` 即 `3.8 × 2.1`；串关处于 active 状态时 `singlesPrice` 等于它。
跨赛事的串关会在**每一个**涉及的赛事上返回相同的条目，
直接从当前正在查看的那个赛事读取即可。

***

## SGP：由博彩公司定价

所有各关必须属于同一个赛事。请求跨两个赛事的各关会返回 `400`：

```json theme={null}
{ "error": 400, "message": "oddsIds must share one fixture.", "code": "invalid_filters" }
```

`sgp` 部分携带博彩公司自己的报价：

```json theme={null}
"sgp": {
  "bet365": {
    "id1400003160574217:bet365:141:0,id1400003160574217:bet365:146:0": {
      "bookmaker": "bet365",
      "active": true,
      "price": 6.5,
      "priceFractional": "11/2",
      "priceAmerican": 550,
      "limit": 500.0,
      "singlesPrice": 7.98,
      "changedAt": 1766667441097,
      "bookmakerParlayId": "B365-2493810567",
      "bookmakerChangedAt": 1766667440813,
      "betslip": "https://www.bet365.com/dl/sportsbookredirect?bs=..."
    }
  }
}
```

<Info>
  **相关性加成。** `singlesPrice`（7.98）是简单相乘会派彩的赔率；`price`（6.5）
  是博彩公司在考虑各关相关性之后实际提供的赔率。两者之差就是博彩公司为相关性收取的加成—— 这通常正是客户最关心的数字。
</Info>

SGP 独有字段：

* `limit`——该赔率下的最大投注额。可选字段；缺失表示“未声明上限”，而不是 0。
* `bookmakerParlayId`——博彩公司自己为该串关分配的标识。
* `bookmakerChangedAt`——博彩公司的报价时间戳（纪元毫秒）。可选字段；缺失时回退到 `changedAt`。
* `betslip`——可直接打开已加载全部各关的投注单的深度链接。
* `meta.offers`——仅交易所：低于最优档的其余价格阶梯，格式为 `[{ price, limit }, ...]`。

***

## 被暂停 vs 无法找到

这是需要在代码中区分对待的关键点，两个端点的行为一致。

**被暂停的关依然存在**，因此仍会在 `odds` 中以 `active: false` 返回，
串关也会保留一个 inactive 条目并附带原因：

```json theme={null}
"id1400003160574217:bet365:141:0,id1400003160574217:bet365:146:0": {
  "active": false,
  "price": 1,
  "singlesPrice": 7.98,
  "meta": { "inactiveReason": "market_closed" }
}
```

`price: 1` 表示退还本金；`singlesPrice` 仍显示原本可以派彩的赔率，
因此您可以把该选择继续展示在界面上。

**无法找到的关**——未知的博彩公司、未知或已过期的 `oddsId`——
会让该博彩公司完全不出现条目，对应的 `oddsId` 会出现在 `missingOddsIds` 中。

<Warning>
  **`sgp: {}` 但没有 `missingOddsIds`。** 在 `/fixtures/odds/sgp` 上，还有一种情况会丢弃某家博彩公司：
  我们这边各关都在，但该博彩公司自己的定价服务无法处理这个串关——它不认识该赛事、对我们做了限流，或者返回了错误。
  此时各关都会正常出现在 `odds` 中，没有 `missingOddsIds`，而 `sgp` 就是 `{}`。 请把空的 `sgp`
  理解为“当前拿不到相关性赔率”，而不是“您的选择有误”； 可回退到 `/fixtures/odds/parlay` 取一个不含相关性的赔率。
</Warning>

### `meta.inactiveReason` 取值

| 取值                    | 端点     | 含义                                        |
| --------------------- | ------ | ----------------------------------------- |
| `leg_suspended`       | 两者     | 其中某一关当前被暂停                                |
| `market_closed`       | 两者     | 该关所属盘口已关闭                                 |
| `bookmaker_suspended` | 两者     | 该博彩公司在某个涉及的赛事上被暂停                         |
| `not_combinable`      | `/sgp` | 该博彩公司提供 SGP，但不接受**这几个特定的**关的组合——实际中最常见的原因 |
| `sgp_unsupported`     | `/sgp` | 该博彩公司不支持这些关组成的 SGP                        |
| `sgp_fetch_failed`    | `/sgp` | 未能及时从该博彩公司取得报价                            |
| `fixture_ended`       | `/sgp` | 赛事已结束                                     |

***

## 速率限制

| 端点                          | 限制            |
| --------------------------- | ------------- |
| `GET /fixtures/odds/parlay` | **每秒 10 个请求** |
| `GET /fixtures/odds/sgp`    | **每秒 10 个请求** |

两者都与其余赔率端点同属赔率配额。参见[速率限制](/zh/api-reference/rate-limits)。

***

## 错误

| 状态码   | 场景                                                                  |
| ----- | ------------------------------------------------------------------- |
| `400` | `oddsIds` 为空或无效、超过 20 关，或（在 `/sgp` 上）各关跨多个赛事                        |
| `422` | 完全未传 `oddsIds` 参数（`validation_error`）——传了但为空则返回 `400`               |
| `401` | API 密钥缺失或无效                                                         |
| `403` | 密钥无权访问该端点（`channel_not_allowed`），或请求中的某个赛事、运动项目、赛事类别或博彩公司不在密钥的权限范围内 |
| `404` | （`/sgp`）定价服务明确报告该赛事未知                                               |
| `429` | 超出速率限制                                                              |
| `501` | （`/sgp`）您所访问的端点未启用 SGP 定价                                           |
| `502` | （`/sgp`）定价服务无法连接或出错——实际上，凭空编造的 `fixtureId` 会在这里返回，而不是 `404`         |

只要请求中包含一个您无权访问的关，**整个请求**都会以 `403` 失败——它不会被静默丢弃。
请在请求定价之前，先按密钥的权限过滤选择项。

这两个端点都需要按 API 密钥单独开通。若未开通，将返回 `403` `channel_not_allowed`——请联系我们开通。
