pokerai.bet API 文档

两套扑克 API,同一把 key 与配额:GTO 策略 API(/v1/gto/*,preflop/flop/turn/river 全街,返回每个动作的混合频率)+ Poker 引擎 / 语义 API(/v1/pokerkit/*,牌面/评牌/听牌/范围权益/牌局模拟)。两套都在交互式参考里。

GTO 策略 API · 两种获取方式:① 预解方案——preflop / flop 用预先算好的 GTO 数据,毫秒级返回;② 实时求解器计算——flop / turn / river 由求解器实时计算(从 flop 开始耗时较长)。另有 范围转换 沿行动线更新范围。

适用范围:当前仅支持 6 人桌(6-max)100BB 起始有效栈。更多人数与后手深度即将上线

无状态 / 自包含请求:每次请求相互独立,服务端不保存牌局状态,也不要求你先调用前面的街——可直接查询任意局面,只需给齐该局面所需信息。

两套 API,同一把 Key:本页是 GTO 策略 API(/v1/gto/*)。另有 Poker 引擎 / 语义 API(/v1/pokerkit/*:牌面 texture、成手/听牌/补牌/阻断、范围权益、评牌、ICM、牌局模拟…),复用同一 API key 与配额(便宜端点扣通用配额、蒙特卡洛扣 solve 配额)。两套都在交互式参考里(顶栏切换),其文档/规范见 /v1/pokerkit/docs · openapi.json(随代码自动同步)。

牌面 / 手牌格式(统一):所有端点的 boardhole_cards 输入都用无分隔字符串(每张牌 2 字符:点数 AKQJT98765432 + 花色 c d h s),如 "board": "2c2h2s""hole_cards": "AdKd"响应board 统一返回数组,如 ["2c","2h","2s"]

范围记法:oop_range / ip_range 等范围串是逗号分隔的 手牌:权重。手牌用类别记法——对子 AA、同花 AKs、非同花 AKo(大点在前);权重 0~1,省略默认 1。例:"AA:1,KK,AKs:0.5,72o:0.1"。注:range_strategy 的键与 range_*_new_raw具体组合记法(如 "AhKh")。

机器可读规范:OpenAPI 3.0 spec —— 可导入 Postman / Insomnia,或用 openapi-generator 生成各语言客户端。交互式参考:在线 API 参考(可直接试一试) —— 填入 Key 即可逐端点发请求(会消耗真实配额)。

鉴权

所有请求需携带 API Key60 秒拿到 Key:

  1. 打开 控制台,输入邮箱 → 收验证码邮件(免密登录,无需注册)。
  2. 填验证码登录 → 新建 API Key → 复制(仅显示一次,请妥善保存)。
  3. 设为环境变量:export POKERAI_API_KEY=gto_xxxxxxxx,即可跑下方 Quickstart。

之后每个请求都带上同一把 Key(就是上一步复制的 gto_xxx,也就是各处说的 “Bearer token”)放进请求头。下面两种写法二选一即可——不是两把 key:

Authorization: Bearer gto_xxxxxxxx     # 标准写法(推荐;对应 OpenAPI 安全方案 BearerApiKey)
# 或(完全等价,二选一)
X-API-Key: gto_xxxxxxxx                 # 等价(OpenAPI 安全方案 XApiKey);某些网关 / SDK / 快速测试工具更顺手

配额

两类每月配额,分开计量、每月 1 日重置,当前用量见控制台:

错误模型

所有错误返回统一 JSON:{ "error": "<code>", "message": "<说明>" }。少数变体:部分 502 用 reason 代替 message;求解器全忙的 429 形如 { "status": "busy", "message": ... }。各端点的特有 error 取值见对应端点的「可能的错误」小表;通用状态码如下:

HTTPerror / 含义是否重试
400输入不合法或缺字段(具体 error 见各端点小表)。否,改正输入
401missing_api_key / invalid_api_key:缺少或无效 Key。否,检查 Key
403invalid_node_token / invalid_solve:令牌/句柄无效或非本人。否,先重新取树/调度
404no_solution:该局面暂无 GTO 数据。否,换局面
429quota_exceeded(通用)/ solve_quota_exceeded(solve):本月配额用尽。否,下月 1 日重置或升级额度
429status: busy:求解器全忙(仅 /v1/gto/solver),不扣费是,退避后重试
502no_result(reason: timeout / no_worker_available)/ auth_unavailable / solver_unreachable:后端瞬时不可用。是,退避重试 2–3 次

错误响应体示例

400(输入不合法):

{
  "error": "invalid_board",
  "message": "board must be 3 cards, e.g. \"2c2h2s\""
}

401(缺少 Key)/ 404(无数据)/ 429(配额用尽)——单字段或短消息:

{ "error": "missing_api_key" }
{ "error": "no_solution", "message": "no GTO data for this spot/board" }
{ "error": "quota_exceeded" }

502(后端瞬时不可用,可重试):

{
  "error": "no_result",
  "reason": "timeout"
}

重试:只有 429 busy 与 502 值得重试——指数退避(~1s 起翻倍,最多 2–3 次)。其余(400/401/403/404/配额用尽)是终态,重试无用:改正输入 / 换 Key / 重新取树,或等配额每月 1 日重置。无每秒限流,故无 Retry-After 头。

Quickstart

拿到 Key 后(见上),复制这条 curl(把 $POKERAI_API_KEY 换成你的)——最简单的一次成功调用:Hero UTG 开池机会(RFI),preflop 毫秒级返回:

curl -s https://pokerai.bet/v1/gto/preflop \
  -H "Authorization: Bearer $POKERAI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"hole_cards":"AhKh","positions":{"hero":"UTG"},"preflop_actions":[{"position":"SB","action":"small blind","amount":0.5},{"position":"BB","action":"big blind","amount":1}]}'

真实返回(UTG 用 AKs,开池机会 → 100% 开池到 3BB):

{
  "hole_cards": "AhKh",
  "situation": "RFI",
  "strategy": [
    { "action": "raise", "frequency": 1, "amount_bb": 3, "sizing_pot": 0.8 }
  ],
  "quota": { "used": 6, "limit": 100 }
}

面对别人加注?把对方动作加进 preflop_actions 即可(追加 {"position":"UTG","action":"raise","amount":3}、Hero 改 MP → 变成 3bet 局面,situation 返回 Raise)。返回每个动作的混合频率(不返回推荐动作,按 frequency 自行选择)。下面按 预解方案(preflop / flop)、实时求解器计算范围转换 三块展开。

客户端 SDK Python · TypeScript · MCP

不想手写 HTTP?官方客户端从 OpenAPI 规范 自动生成、全类型化,和 API 永远同步。鉴权就是你的 API Key。

Python pip

pip install pokerai-bet   # 分发名 pokerai-bet,导入名 pokerai

同一个 Quickstart 局面(Hero UTG 开池),类型化调用:

from pokerai import AuthenticatedClient
from pokerai.api.lookup import preflop_strategy
from pokerai.models import (
    PreflopRequest, PreflopRequestPositions, PreflopRequestPreflopActionsItem,
)
from pokerai.models.preflop_request_preflop_actions_item_action import (
    PreflopRequestPreflopActionsItemAction as Act,
)
from pokerai.models.position import Position

client = AuthenticatedClient(base_url="https://pokerai.bet", token="gto_...")

resp = preflop_strategy.sync(client=client, body=PreflopRequest(
    hole_cards="AhKh",
    positions=PreflopRequestPositions(hero=Position.UTG),
    preflop_actions=[
        PreflopRequestPreflopActionsItem(position=Position.SB, action=Act.SMALL_BLIND, amount=0.5),
        PreflopRequestPreflopActionsItem(position=Position.BB, action=Act.BIG_BLIND, amount=1.0),
    ],
))
print(resp.to_dict())

真实输出(与 Quickstart 同一局面):

{"hole_cards": "AhKh", "situation": "RFI",
 "strategy": [{"action": "raise", "frequency": 1, "sizing_pot": 0.8, "amount_bb": 3}],
 "quota": {"used": 2702, "limit": 100000}}

TypeScript / JavaScript npm

npm install @pokerai/client
import { createPokeraiClient } from "@pokerai/client";

const client = createPokeraiClient({ apiKey: "gto_..." });

const { data, error } = await client.POST("/v1/gto/preflop", {
  body: {
    hole_cards: "AhKh",
    positions: { hero: "UTG" },
    preflop_actions: [
      { position: "SB", action: "small blind", amount: 0.5 },
      { position: "BB", action: "big blind", amount: 1 },
    ],
  },
});
if (error) throw new Error(JSON.stringify(error));
console.log(data.situation, data.strategy);
// "RFI" [{ action: "raise", frequency: 1, amount_bb: 3, sizing_pot: 0.8 }]

路径、请求体、响应字段全程类型检查——编辑器自动补全整套 API。类型由 openapi-typescript 从规范生成,运行时是 openapi-fetch

MCP(给 LLM agent)npm

让 Claude / Cursor 等把 Pokerai API 直接当工具调用(@pokerai/mcp):

// mcp.json
{ "mcpServers": { "pokerai": {
  "command": "npx", "args": ["-y", "@pokerai/mcp"],
  "env": { "POKERAI_API_KEY": "gto_..." }
}}}

默认 5 个预解查询工具;加 "POKERAI_ENABLE_SOLVE": "1" 开启实时求解工具(消耗 solve 配额)。

概念

整体心智模型——先看这一遍,往下读各端点会更顺。

两种获取方式

不返回推荐动作

所有策略都返回每个动作的混合频率(0~1),不替你选动作——你按 frequency 自行实现(取最大概率,或按频率随机)。

下注尺度:amount_bb 与 sizing_pot

每个 bet/raise 带两个尺度字段:amount_bb(绝对额,加注多少 BB)与 sizing_pot(相对底池的数值,标准 % 底池口径):

例:3bet 到 9 面对 open 3 —— 底池 4.5,跟注后 4.5+3=7.5,加注超出 9−3=6,sizing_pot = 6/7.5 = 0.8。全下时另带 allin: true

tree → node(取树 → 取节点)

flop 决策树和实时求解器都是两步:先取整棵树(每个决策节点带一个 token),再用 Hero 节点的 token 取该步策略。

取树   /flop/tree   或   /solver(+ 轮询 /solver/tree)
         └─→ nodes[]:每个节点带 is_hero + token
               └─→ 选 is_hero:true 的节点
取节点  /flop/node  或   /solver/node    (body 带该节点 token)
               └─→ 这一步的混合策略

节点路径记法(两套,取决于树的来源):flop 决策树BET_8(下划线、整数 BB);solver 树BET 8.000000(空格、6 位小数)。传 node_id / 导航时要和那棵树(或 solver_results)里的标签逐字一致

节点状态机(实时求解)

调度后轮询 /solver/treespot_status:available(未调度)→ computing(求解中,继续轮询)→ queryable(可取节点)→ expired(缓存按 TTL 回收,需重新调度)。无需也无法手动释放计算资源。

预解方案 presolved · 毫秒级

preflop 与 flop 使用预先算好的 GTO 策略,毫秒即返回,适合需要短时返回的业务场景。有效栈固定 100BB(已预先算好,非输入项)。想对 flop 用真实求解器实时算,见下一节。

Preflop

POST https://pokerai.bet/v1/gto/preflop 扣 1 通用

完整参数 / 响应 schema、在线试一试 → 交互式参考

不需要你判断“几bet池”——按顺序给出 Hero 之前的翻前动作,服务端自动推导局面(未开池/面对加注/3bet/4bet…)。

说明 · 请求体

字段类型说明
hole_cardsstringHero 的 2 张手牌,如 "AdKd"
positions.herostringHero 位置,SB BB UTG MP CO BTN 之一(放在 positions 下)。
preflop_actionsarray从小盲到 Hero 之前一位玩家的完整、显式动作序列(Hero 在序列中,其位置由 positions.hero 给出,序列止于 Hero 前一位)。每项 { position, action, amount, allin? },见下表。
preflop_versionstring可选。选用哪套 6max 翻前策略图表:6max(默认)/ 6max_RC_100bb_200NL / 6max_RC_100bb_100NL / 6max_RC_40bb。省略=平台默认(6max);未知值 → 400 unsupported_preflop_version。同一局面不同版本频率不同。

preflop_actions 每项字段

字段类型说明
positionstring该动作所在位置,SB BB UTG MP CO BTN 之一。
actionstring"small blind" / "big blind" / "raise" / "call" / "fold"(注意盲注是两个单词的字符串)。
amountnumber该动作新投入的增量(BB,不是累计总额)。例:小盲 0.5;大盲 1;开池加注到 3 → amount 3(从 0 起);已投入 1 的玩家再加注到 9 → amount 8。fold 省略(记 0)。底池 = 所有 amount 之和
allinboolean可选。标记一次短码全下(下注/跟注额低于最小加注额);为 true 时跳过最小加注校验。

校验(违反 → 400 invalid_actions):序列必须以 small blind(0.5)、再 big blind(1)开头;每个 raise/call 需正的 amount;raise累计总额须大于当前注额且满足最小加注额(= 当前注额 + 上一次加注的幅度;故开池 ≥ 2BB,对 open-to-3 的 3bet 须 ≥ 5BB)——除非 allin:true;call 的累计总额须恰好等于当前注额——除非 allin:true

精确金额只影响底池 / sizing_pot,不影响频率:给出精确的 amount 只是让 底池 / sizing_pot 精确;GTO 频率是按局面(RFI / 3bet / 4bet + 位置)决定的,不随下注尺寸变化

调用示例

curl -s https://pokerai.bet/v1/gto/preflop \
  -H "Authorization: Bearer $POKERAI_API_KEY" -H "Content-Type: application/json" \
  -d '{"hole_cards":"AhKh","positions":{"hero":"MP"},"preflop_version":"6max_RC_100bb_200NL",
       "preflop_actions":[{"position":"SB","action":"small blind","amount":0.5},
                          {"position":"BB","action":"big blind","amount":1},
                          {"position":"UTG","action":"raise","amount":3}]}'

省略 preflop_version = 默认 6max。可选版本见下,或用 GET /v1/gto/preflop/versions 实时查询。

响应(200)

{"hole_cards": "AhKh", "situation": "Raise", "strategy": [{"action": "raise", "frequency": 1, "amount_bb": 9, "sizing_pot": 0.8}], "quota": {"used": 7, "limit": 100}}
字段说明
hole_cards回显的手牌。
situation轮到 Hero 时牌桌面对的状态:RFI(无人入池,Hero 开池机会)/ Limp(有人 limp、无加注)/ Raise(面对 1 个开池加注)/ 3-Bet / 4-Bet / 5-Bet。注:BB 永远落 Limp(它行动前必有人入池)。
strategy[]每个动作的混合策略。raiseamount_bb(加注的绝对额,BB)与 sizing_pot(见 下注尺度)。preflop 的 amount_bb 按 Hero 之前的加注次数给定,见下表。不返回推荐动作,按 frequency 自行选择。
quota本月通用配额用量(used / limit)。

preflop amount_bb(由 flop 预解方案底池推导,加注):

Hero 之前加注次数局面amount_bb
0开池3
13bet9
24bet25
≥35bet+全下 100(allin: true)

可选策略版本 preflop_version

同样 6max,可选不同策略图表(同一局面频率不同);省略=默认 6max权威列表用发现端点 GET /v1/gto/preflop/versions 实时查(免费,返回 id+标签+默认):

id(传给 preflop_version)说明
6max(默认)6 max 100bb Deepsolver
6max_RC_100bb_200NL6 max 100bb GG 200NL 3b/f 2.2x - 2.5x
6max_RC_100bb_100NL6 max 100bb GG 100NL 3b/f
6max_RC_40bb6 max 40bb GG 100NL
curl -s https://pokerai.bet/v1/gto/preflop/versions -H "Authorization: Bearer $POKERAI_API_KEY"
// {"versions": [{"id": "6max", "label": "6 max 100bb Deepsolver", "default": true}, …], "default": "6max"}

可能的错误

HTTPerror触发 / 怎么修
400invalid_hole_cardshole_cards 不是 2 张牌(如 "AdKd")。
400unsupported_table_size / invalid_positions / invalid_actions桌型(当前仅 6max)、hero/位置、或 preflop_actions 不合法。
400unsupported_preflop_versionpreflop_version 不在允许集合(6max / 6max_RC_100bb_200NL / 6max_RC_100bb_100NL / 6max_RC_40bb)。
404no_solution该 preflop 局面无预求解数据,换局面。

下载:request.jsonresponse.json

整段范围(13×13) 扣 1 通用

POST https://pokerai.bet/v1/gto/preflop/range · 同一局面(位置 + 行动线)的**整张 13×13 范围**,一次返回 169 个手型的 fold/call/raise。不传 hole_cards(局面由 positions + preflop_actions 决定)。计费 1 次通用(一次返回,不是 169 次)。用于画范围表。

// 请求(不传 hole_cards)
{"table_size": "6max", "positions": {"hero": "BTN"}, "preflop_actions": [{"position": "SB", "action": "small blind", "amount": 0.5}, {"position": "BB", "action": "big blind", "amount": 1}, {"position": "UTG", "action": "fold"}, {"position": "MP", "action": "fold"}, {"position": "CO", "action": "fold"}]}

// 响应
{ "range": {"22": {"fold": 0.69, "call": 0, "raise": 0.31}, "33": {"fold": 0, "call": 0, "raise": 1}, "ATs": {"fold": 0, "call": 0, "raise": 1}, "AKs": {"fold": 0, "call": 0, "raise": 1}, "AKo": {"fold": 0, "call": 0, "raise": 1}, "KQs": {"fold": 0, "call": 0, "raise": 1}, "72o": {"fold": 1, "call": 0, "raise": 0}, "JJ": {"fold": 0, "call": 0, "raise": 1} , …共 169 项… },
  "quota": {"used": 7, "limit": 100} }

手型记法:对子 AA、同花 AKs、杂色 AKo(高牌在前);每项 fold+call+raise≈1。完整 schema / 在线试一试 → 交互式参考

Flop 决策树

flop 策略是一棵决策树:先取树(拿到全部决策节点 + 每个节点的 token),业务系统按实际下注在树上走路径,到 Hero 的节点再取该步策略。两步,和 solver 一致(treenode)。

① 取决策树 扣 1 通用

POST https://pokerai.bet/v1/gto/flop/tree · 输入 board + pot_type + positions(不需要 hole_cards,树与手牌无关)。

完整参数 / 响应 schema、在线试一试 → 交互式参考

board翻牌 3 张,如 "2c2h2s"
pot_type"SRP" 单加注 / "3BET" / "4BET" / "LIMP" 跛入。
positions各角色位置(SB BB UTG MP CO BTN),按 pot_type 要求见下。
flop_version可选。选用哪套 flop 数据集(每个 preflop 版本各解一套):6max(默认)/ 6max_RC_100bb_200NL / 6max_RC_100bb_100NL / 6max_RC_40bb。省略=默认 6max;该版本无此 spot 数据时优雅降级6max;未知值 → 400 unsupported_flop_version。与 preflop_version 相互独立。node token 会带上此版本,故 /v1/gto/flop/node 保持同一数据集。
pot_type必填位置Hero 取值
SRPhero, raiser, callerraiser 或 caller
3BET / 4BEThero, raiser, three_bettorraiser 或 three_bettor
LIMPhero, limperHero 或 limper 必有一个是 BB
// 请求(不需要 hole_cards)
{"board": "2c2h2s", "pot_type": "SRP", "positions": {"hero": "UTG", "raiser": "UTG", "caller": "BTN"}}

// 响应(nodes 共 36 个,此处示前 5)
{"board": ["2c", "2h", "2s"], "pot_type": "SRP", "pot": 7.5, "effective_stack": 97, "oop_range": "AA:1,AKs:1,AQs:1,AJs:1,ATs:1,A9s:1,A8s:1,A7s:1,A6s:1,A5s:1,…", "ip_range": "AQs:0.05,AJs:0.86,ATs:0.436,A9s:0.356,A8s:0.36,A7s:0.196,…", "node_count": 36, "nodes": [{"node": "root", "is_hero": true, "token": "eyJ…"}, {"node": "root/BET_4", "is_hero": false, "token": "eyJ…"}, {"node": "root/BET_8", "is_hero": false, "token": "eyJ…"}, {"node": "root/BET_97", "is_hero": false, "token": "eyJ…"}, {"node": "root/CHECK", "is_hero": false, "token": "eyJ…"}, …], "quota": {"used": 7, "limit": 100}}
字段说明
oop_range / ip_range本局面的起手范围(加权组合串),用于范围转换的起手权重。
nodes[]全部决策节点:node(动作路径,如 "root/CHECK/BET_8")、is_hero(是否 Hero 决策点)、token(取该节点策略的凭证,传给 ②;绑定账号+局面,不可伪造)。
pot / effective_stack / node_count底池、有效栈(BB)与决策节点总数。
quota本月通用配额用量(used / limit)。

② 取某节点策略 免费

POST https://pokerai.bet/v1/gto/flop/node · 带 node(①里某节点的 token)。hole_cards → 这手牌的混合策略;不传 hole_cards → 整段范围策略。

完整参数 / 响应 schema、在线试一试 → 交互式参考

Hero 的每个决策都走这里:Hero 第一个行动 = root 节点(OOP 先手 check/bet);Hero 在 IP、面对下注、或第二次行动,就选对应的 is_hero:true 节点(如 root/CHECK/BET_8 = 我过牌后面对下注 → fold/call/raise)。翻牌树是单街;跨街(转牌/河牌)走 /v1/gto/solver/*

// 请求(Hero 节点,带手牌) -> Hero 策略
{"node": "eyJ…", "hole_cards": "AdKd"}
{"hole_cards": "AdKd", "node": "root", "is_hero": true, "strategy": [{"action": "check", "frequency": 0.7208}, {"action": "bet", "amount_bb": 4, "sizing_pot": 0.5333, "frequency": 0.0533}, {"action": "bet", "amount_bb": 8, "sizing_pot": 1.0667, "frequency": 0.2259}, {"action": "bet", "amount_bb": 97, "sizing_pot": 12.9333, "allin": true, "frequency": 0}]}

// 请求(不传 hole_cards) -> 整段范围策略
{"node": "eyJ…"}
{"node": "root/BET_4", "is_hero": false, "actions": [{"action": "call"}, {"action": "raise", "amount_bb": 12, "sizing_pot": 0.5161}, {"action": "raise", "amount_bb": 20, "sizing_pot": 1.0323}, {"action": "raise", "amount_bb": 97, "sizing_pot": 6, "allin": true}, {"action": "fold"}], "range_strategy": {"3d3c": [0.93067, 4e-05, 0.06922, 1e-05, 7e-05], "3h3c": [0.92172, 6e-05, 0.07812, 1e-05, 0.0001], "3h3d": [0.94334, 4e-05, 0.05655, 1e-05, 7e-05], "…": "共 188 手"}, "range_hand_count": 188}
字段说明
is_hero该节点是否轮到 Hero 决策。
strategy[]Hero 节点(带 hole_cards):这手牌的混合策略。actioncheck / bet / call / raise / fold(bet=首注、raise=面对下注的加注);bet/raiseamount_bbsizing_pot(见 下注尺度),全下另带 allin: true;frequency 为 0~1 概率。不返回推荐动作,按 frequency 自行选择。
actions[] + range_strategy对手节点(或不传 hole_cards):整段范围策略,每手牌频率对齐 actions;含 range_hand_count。可用于「范围转换」组装 solver_results

节点 id 里的 BET_8 / RAISE_20 即绝对下注额(BB)。

可能的错误

HTTPerror触发 / 怎么修
400invalid_board / invalid_positions;② missing_node / invalid_hole_cards① board 非 3 张、或 hero/位置不合法;② 缺 node 或 hole_cards 非 2 张。
403invalid_node_token(②)node 令牌无效或非本人——先调 /v1/gto/flop/tree 取树。
404no_solution该局面/棋盘无预求解数据。

下载:tree_request.jsontree_response.jsonnode_request.jsonnode_response.json

实时求解器计算 solver

POST https://pokerai.bet/v1/gto/solver 系列。纯 postflop 求解器实时计算,走独立的 solve 配额。输入即解的本质:牌面 + oop/ip 范围 + 底池 + 后手筹码 + 谁是 Hero,不需要历史,可从任意局面切入。三步:调度 → 轮询取树 → 取节点策略

完整参数 / 响应 schema、在线试一试 → 交互式参考(含 /solver/tree/solver/node)。

board 长度定街:3=flop / 4=turn / 5=river。⚠ 从 flop 开始实时求解耗时较长(实测 SRP 翻牌约 70 秒),不适合需要短时返回的业务场景——flop 想要快,请用上面的「预解方案」(毫秒级);turn / river 较快(数秒到数十秒)。

调用示例(三步:① 调度 → ② 轮询取树 → ③ 取节点)

# ① 调度(board 3/4/5 = flop/turn/river;flop 约 70 秒)
curl -s https://pokerai.bet/v1/gto/solver -H "Authorization: Bearer $POKERAI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"board":"2c2h2s","oop_range":"AA,KK,QQ,AKs","ip_range":"JJ,TT,AQs,KQs","pot":7.5,"effective_stack":97,"hero":"OOP"}'
# ② 轮询取树,直到 spot_status=queryable;③ 取节点
curl -s https://pokerai.bet/v1/gto/solver/tree -H "Authorization: Bearer $POKERAI_API_KEY" \
  -H "Content-Type: application/json" -d '{"solve":"eyJ...<上一步 solve>"}'

① 调度求解 扣 1 solve

board3=flop / 4=turn / 5=river,如 "2c2h2s9d"(turn)。
oop_range / ip_range必填。进入该街的双方范围,加权组合串,如 "AsKs:1,QQ:0.75,..."
pot / effective_stack必填。进入该街的底池与后手有效栈(BB),决定下注档,必须真实。
hero必填:"OOP""IP"
bet_sizes可选:覆盖各街下注档,如 {"flop":[33,75],"turn":[33,67,120],"river":[33,75,150]}(底池%);flop 不传默认 50%。
// 请求(flop 例)
{"board": "2c2h2s", "oop_range": "AA,KK,QQ,AKs", "ip_range": "JJ,TT,AQs,KQs", "pot": 7.5, "effective_stack": 97, "hero": "OOP"}

// 响应(首次触发求解,扣 1 solve)
{"status": "computing", "solve": "eyJ0Ijoic2x2XzJmMjk2MWU3Y2Y0ODU3OGUiLCJ1IjoiYjk4Y2Y5NzAtZTVjMS00Njk5LTg4ODQtOGQzYzcwMGIyNGJlIiwidHMiOjE3ODIxMjkxMTUyMDJ9.rtmeNcZoBNWJRhkAGkTSSR58ZafpAh35mi510bgPU6c", "solve_quota": {"used": 1, "limit": 100}}
字段 / 情况说明
solvesolve 句柄,后续 /tree/node 用它;范围只在这一步给一次。
status = computing已触发新求解,扣 1 solve 配额(见 solve_quota)。
status = queryable同一局面已有缓存解,不扣费(无 solve_quota 字段),直接轮询 /tree
429 status = busy所有求解器都在忙,不扣费,稍后重试。

缓存命中(不重复扣费):同一局面(相同 board/范围/底池/筹码/hero/bet_sizes)重复调度不再扣 solve 配额——响应里没有 solve_quota 字段即表示未扣费,直接用返回的 solve 句柄轮询 /tree。示例:request / response

② 取决策树 + 节点状态 免费

POST https://pokerai.bet/v1/gto/solver/tree · 带 solve 句柄,轮询直到 spot_status = queryable取后续街道的策略:带上已发的牌 —— turn_card(flop 解 → 指定 turn)和/或 river_cardflop 解取 river 需同时传 turn_card + river_card(光给 river 不唯一);turn 解只需 river_card;都不传则取本街。

// ② 取本街(flop)树
{"solve": "eyJ…"}
{"street": "flop", "spot_status": "queryable", "pot": 7.5, "effective_stack": 97, "solve_seconds": 25.84, "node_count": 14, "nodes": [{"node": "root", "is_hero": true, "status": "queryable", "token": "eyJ…"}, {"node": "root/BET 4.000000", "is_hero": false, "status": "queryable", "token": "eyJ…"}, {"node": "root/BET 4.000000/RAISE 12.000000", "is_hero": true, "status": "queryable", "token": "eyJ…"}, …]}

// 取 turn 树(flop 解 + turn_card)
{"solve": "eyJ…", "turn_card": "9d"}
{"street": "turn", "spot_status": "queryable", "pot": 7.5, "effective_stack": 97, "solve_seconds": 25.84, "node_count": 104, "nodes": [{"node": "root/BET 4.000000/CALL/9d", "is_hero": false, "status": "queryable", "token": "eyJ…"}, {"node": "root/BET 4.000000/CALL/9d/BET 10.000000", "is_hero": true, "status": "queryable", "token": "eyJ…"}, {"node": "root/BET 4.000000/CALL/9d/BET 10.000000/RAISE 34.000000", "is_hero": false, "status": "queryable", "token": "eyJ…"}, …]}

// 取 river 树(flop 解 + turn_card + river_card)
{"solve": "eyJ…", "turn_card": "9d", "river_card": "Qh"}
{"street": "river", "spot_status": "queryable", "pot": 7.5, "effective_stack": 97, "solve_seconds": 25.84, "node_count": 308, "nodes": [{"node": "root/BET 4.000000/CALL/9d/BET 10.000000/CALL/Qh", "is_hero": true, "status": "queryable", "token": "eyJ…"}, {"node": "root/BET 4.000000/CALL/9d/BET 10.000000/CALL/Qh/BET 27.000000", "is_hero": false, "status": "queryable", "token": "eyJ…"}, {"node": "root/BET 4.000000/CALL/9d/BET 10.000000/CALL/Qh/BET 27.000000/RAISE 83.000000", "is_hero": true, "status": "queryable", "token": "eyJ…"}, …]}
字段说明
spot_status求解状态:available(未调度)/ computing(求解中,继续轮询)/ queryable(可取)/ expired(缓存已回收,需重新调度 ①)/ no_nodes(解已收敛,但该 runout/街在树中无决策节点 —— 终态,停止轮询)。
nodes[]各决策节点:node(动作路径)、is_herostatustoken(取该节点策略的凭证,传给 ③)。河牌 runout 为 chance 节点,按河牌导航。
street / pot / effective_stack / node_count街道、底池、有效栈与决策节点总数。
solve_seconds本次实时求解从调度到收敛的耗时(秒)。仅在 queryable 返回;同一个解的各 runout 共享该值。

③ 取某节点策略 免费

POST https://pokerai.bet/v1/gto/solver/node · 带 node token。Hero 节点返回 Hero 策略;对手节点(或不传 hole_cards)返回范围策略。

{"node": "eyJ…", "hole_cards": "AhKh"}
{"hole_cards": "AhKh", "node": "root", "is_hero": true, "strategy": [{"action": "check", "frequency": 0}, {"action": "bet", "amount_bb": 4, "sizing_pot": 0.5333, "frequency": 0.2666}, {"action": "bet", "amount_bb": 97, "sizing_pot": 12.9333, "allin": true, "frequency": 0.7334}]}
字段说明
is_hero该节点是否轮到 Hero 决策。
strategy[]Hero 节点:Hero 这手牌的混合策略(action / amount_bb / sizing_pot(见 下注尺度)/ frequency),全下时另带 allin: true
actions[] + range_strategy对手节点(或不传 hole_cards):range_strategy 每手牌的频率对齐 actions 顺序;另含 range_hand_count

无需也无法手动释放计算资源,系统按 TTL 自动回收;缓存已回收或被新解替换时该步返回 { "node_status": "expired" },重新调度 ① 即可。节点级求解错误返回 { "node_status": "error", "message": … }(终态,别再轮询)。

可能的错误

HTTPerror触发 / 怎么修
400invalid_board / missing_range / invalid_pot / invalid_effective_stack / invalid_hero(调度)board、oop/ip 范围、底池、后手筹码或 Hero 等求解输入不合法。
400missing_solve / missing_node(取树/取节点)缺 solve 句柄或 node 令牌。
403invalid_solve / invalid_node_token句柄/令牌无效或非本人——重新调度 / 取树。
429status: busy所有求解器都在忙,不扣费,退避后重试。
502solve_failed求解触发失败,稍后重试。
503upstream_unavailable(取树/取节点)求解器多次重试后仍不可达(传输错 / 5xx)——可重试。

下载(turn,board=4):schedule_reqschedule_restree_reqtree_resnode_reqnode_res

下载(flop,board=3):schedule_reqschedule_restree_reqtree_resnode_reqnode_res

下载(river,board=5):schedule_reqschedule_restree_reqtree_resnode_reqnode_res

端到端轮询示例(Python)

import requests, time
H = {"Authorization": "Bearer $POKERAI_API_KEY"}
BASE = "https://pokerai.bet/v1/gto"

# ① 调度(扣 1 solve 配额;命中缓存则不扣)
solve = requests.post(f"{BASE}/solver", headers=H, json={
    "board": "2c2h2s9d", "oop_range": "AA,KK,QQ,AKs", "ip_range": "JJ,TT,AQs,KQs",
    "pot": 20, "effective_stack": 90, "hero": "OOP"}).json()["solve"]

# ② 轮询取树,直到 queryable
while True:
    tree = requests.post(f"{BASE}/solver/tree", headers=H, json={"solve": solve}).json()
    if tree["spot_status"] == "queryable": break
    time.sleep(2)                       # computing -> 退避后再查

# ③ 取 Hero 根节点策略
root = next(n for n in tree["nodes"] if n["node"] == "root")
strat = requests.post(f"{BASE}/solver/node", headers=H,
                      json={"node": root["token"], "hole_cards": "AhKh"}).json()
print(strat["strategy"])

范围转换 range

POST https://pokerai.bet/v1/gto/range 扣 1 通用

完整参数 / 响应 schema、在线试一试 → 交互式参考

沿一条行动线更新 OOP/IP 范围。主要用于得到进入下一条街的范围(flop→turn / turn→river),再喂给 /v1/gto/solver。范围更新的全部计算(含 normalize、诈唬折扣)由求解器完成,与求解器结果一致。

这是一个透传代理:你自己组织全部输入,包括 solver_results(决策树)。决策树可用你已有的解,或用本平台 flop 决策树拼出:走 /v1/gto/flop/tree 拿起手范围,逐节点 /v1/gto/flop/node不传 hole_cards 取整段 range_strategy,按 {node_type,player,strategy,childrens} 嵌套。见文末脚本。

请求体

{
  "range_oop": "AQs:1,AJs:0.48,...",            // 必填,起手 OOP 范围
  "range_ip":  "AA:1,AKs:1,...",                 // 必填,起手 IP 范围
  "solver_results": { /* 必填:决策树 */ },
  "node_id": "root/CHECK",          // 必填,行动线
  "board": "2c2h2s",                             // 可选,无分隔串(同其它端点)
  "normalize": true,                             // 可选,默认 true
  "explain": false,                              // 可选,逐手牌变化解释
  "track_hands": ["AA"],                         // 可选,只追踪这些手牌
  "bluff_discount_ratio": 0.8,                   // 可选,诈唬折扣
  "hero_position": "oop",                        // 可选,"oop"/"ip" — Hero 是哪一方(牌阻挡用)
  "hero_hand": "AsKs"                            // 可选,从范围里移除含 Hero 底牌的组合(牌阻挡);会回显
}

牌阻挡(可选):设 hero_position("oop"/"ip")+ hero_hand,可把范围里所有含 Hero 底牌的组合移除——适合「范围 vs 具体手牌」分析。两者都会在响应里回显。(投影范围两个包装端点同样支持;/v1/gto/flop/projected-range 另支持 partner_hands。)

调用示例

# solver_results 较大,放文件用 -d @
curl -s https://pokerai.bet/v1/gto/range -H "Authorization: Bearer $POKERAI_API_KEY" \
  -H "Content-Type: application/json" -d @range_request.json

响应(200,真实返回,长串截断)

{"bluff_combos_ratio": 0.33, "bluff_discount_ratio": 1, "board": ["2c", "2h", "2s"], "hand_bottom_ranks_ip": "KsQh:2413,KsQd:2413,KsQc:2413,KhQs:2413,…", "hand_bottom_ranks_oop": "KcJh:2414,KcTs:2415,KsTs:2415,KsTh:2415,…", "hand_ranks_ip": "QcQh:313,QdQh:313,QdQs:313,QhQs:313,QcQs:313,…", "hand_ranks_oop": "Ad2d:155,AdAc:311,AhAc:311,AhAd:311,AsAc:311,…", "node_id": "root/CHECK", "path_length": 1, "range_ip_new": "33:0.193023,44:0.216279,54s:0.65814,…", "range_ip_new_raw": "3c3d:0.193023,3c3h:0.193023,3c3s:0.193023,…", "range_ip_new_raw_before_normalization": "3c3d:0.166,3c3h:0.166,3c3s:0.166,3d3h:0.166,…", "range_oop_new": "33:0.245724,44:0.272975,54s:0.420008,…", "range_oop_new_raw": "3d3c:0.245857,3h3c:0.245847,3h3d:0.24586,…", "range_oop_new_raw_before_normalization": "3d3c:0.245843,3h3c:0.245833,3h3d:0.245845,…", "quota": {"used": 7, "limit": 100}}
字段说明
range_oop_new / range_ip_new归一后的更新范围(类记法),直接当下一条街的 oop_range / ip_range 喂给 solver。
range_oop_new_raw / range_ip_new_raw诈唬折扣后、未归一的中间值(组合记法),按需取用。
range_oop_new_raw_before_normalization / range_ip_new_raw_before_normalization仅沿行动线收窄,未做诈唬折扣与归一的原始值。
hand_ranks_oop / hand_ranks_ip手牌强度排名(组合:rank,数值越小越强)。
hand_bottom_ranks_oop / hand_bottom_ranks_ip底部范围排名。
node_id / board / path_length回显的行动线、公共牌与行动线步数。
bluff_discount_ratio / bluff_combos_ratio本次实际使用的诈唬折扣参数。
quota本月通用配额用量(used / limit)。

可能的错误

HTTPerror触发 / 怎么修
400missing_fieldrange_oop / range_ip / solver_results / node_id 之一。
400bad_requestsolver 端拒绝(如 node_id 在你给的决策树里走不通)。

下载:request.json(~1MB,含决策树)response.jsonassemble_solver_results.py(端到端脚本)

flop 投影范围 projected-range

POST https://pokerai.bet/v1/gto/flop/projected-range 扣 1 通用

完整参数 / 响应 schema、在线试一试 → 交互式参考

沿一条 flop 行动线收窄 OOP/IP 范围,直接得到进入 turn 的起手范围。给齐局面(board/pot_type/positions)与一条行动线(node_id),平台在服务端自动拼出决策树并完成范围更新,返回与 范围转换 基本相同的字段(另多一个回显的 pot_type)。

这是 /v1/gto/range便捷封装:省去你手动走 /v1/gto/flop/tree + 逐节点 /v1/gto/flop/nodesolver_results 的步骤。只适用于 flop 行动线(决策树由本平台 flop 预求解结果拼出);turn→river 等需自带决策树时仍用 /v1/gto/range

请求体

{
  "board": "2c2h2s",
  "pot_type": "SRP",
  "positions": { "hero": "UTG", "raiser": "UTG", "caller": "BTN" },
  "node_id": "root/BET_4",
  "normalize": true,
  "bluff_discount_ratio": 0.8,
  "bluff_combos_ratio": 0.5,
  "hero_position": "oop",
  "hero_hand": "AsKs",
  "partner_hands": ["Ac9c"]
}
字段说明
board必填。翻牌(无分隔串,同其它端点),如 "2c2h2s"
pot_type必填。底池类型,如 "SRP"
positions必填。{ hero, raiser, caller }(同 flop);3bet/limp 局面可带 three_bettor / limper
node_id可选,默认 "root"。flop 行动线,用 /v1/gto/flop/tree 返回的 api 记法(下注额绝对 BB),如 "root/BET_4""root/CHECK/BET_8/CALL"
normalize可选,默认 true。是否对更新后范围归一。
bluff_discount_ratio / bluff_combos_ratio可选,各自取值 [0,1](越界 → 400)。bluff_discount_ratio 是对底部诈唬组合的折扣系数;bluff_combos_ratio 是范围里被当作诈唬的比例。省略则用服务端 turn/river 默认值。两者都会回显。
hero_position / hero_hand可选牌阻挡。设 hero_position("oop"/"ip")+ hero_hand(如 "AsKs"):把对手更新后范围(range_*_new_raw)里所有含 Hero 底牌的组合移除,并确保 Hero 自己底牌出现在自己范围里。两者都会回显。仅作用于 range_*_new_raw——hand_ranks / hand_bottom_ranks 只按 board 过滤。
partner_hands可选,4 字符组合数组(如 ["Ac9c"]),需配合 hero_position。把对手 range_*_new_raw 里所有含这些牌的组合移除——用于建模已知死牌(弃掉的手牌、暴露的牌)。仅输入不回显。/v1/gto/turn/projected-range 也支持。
flop_version可选。选用哪套 flop 数据集(每个 preflop 版本各解一套):6max(默认)/ 6max_RC_100bb_200NL / 6max_RC_100bb_100NL / 6max_RC_40bb。省略=默认 6max;该版本无此 spot 数据时优雅降级6max;未知值 → 400 unsupported_flop_version。与 preflop_version 相互独立。

调用示例

curl -s https://pokerai.bet/v1/gto/flop/projected-range -H "Authorization: Bearer $POKERAI_API_KEY" \
  -H "Content-Type: application/json" -d @projected_range_request.json

响应(200,字段全列,长串截断)

{"board": ["2c", "2h", "2s"], "pot_type": "SRP", "node_id": "root/BET_4", "bluff_combos_ratio": 0.5, "bluff_discount_ratio": 0.8, "hero_position": "oop", "hero_hand": "AsKs", "hand_bottom_ranks_ip": "AhTh:2405,AsTs:2405,Ac9c:2406,Ad9d:2406,…", "hand_bottom_ranks_oop": "AhJs:2404,AsJc:2404,AsJd:2404,AsJh:2404,…", "hand_ranks_ip": "QcQd:313,QcQh:313,QcQs:313,QdQh:313,…", "hand_ranks_oop": "Ad2d:155,AdAc:311,AdAh:311,AdAs:311,…", "path_length": 1, "range_ip_new": "33:0.193023,44:0.216279,54s:0.65814,55:0.344186,…", "range_ip_new_raw": "3c3d:0.193023,3c3h:0.193023,3c3s:0.193023,3d3h:0.193023,…", "range_ip_new_raw_before_normalization": "3c3d:0.166,3c3h:0.166,3c3s:0.166,3d3h:0.166,…", "range_oop_new": "44:0.0120032,55:0.0422217,66:0.181192,77:0.327323,…", "range_oop_new_raw": "4s4c:0.0666554,4s4h:0.0666554,5d5c:0.005,5d5h:0.005,…", "range_oop_new_raw_before_normalization": "4s4c:0.011816,4s4h:0.011816,5s5c:0.0392591,5s5h:0.0392591,…", "quota": {"used": 7, "limit": 100}}
字段说明
range_oop_new / range_ip_new归一后的更新范围(类记法),直接当下一条街的 oop_range / ip_range 喂给 solver。
range_oop_new_raw / range_ip_new_raw诈唬折扣后、未归一的中间值(组合记法),按需取用。
range_oop_new_raw_before_normalization / range_ip_new_raw_before_normalization仅沿行动线收窄,未做诈唬折扣与归一的原始值。
hand_ranks_oop / hand_ranks_ip手牌强度排名(组合:rank,数值越小越强)。
hand_bottom_ranks_oop / hand_bottom_ranks_ip底部范围排名。
node_id / board / pot_type / path_length回显的行动线、公共牌、底池类型与行动线步数。
bluff_discount_ratio / bluff_combos_ratio本次实际使用的诈唬折扣参数。
quota本月通用配额用量(used / limit)。

可能的错误

HTTPerror触发 / 怎么修
400invalid_board / invalid_positionsboard 非 3 张,或 positions.hero 不合法。
404no_solution(及服务端 errorTypeno_ranges / no_root_node)该局面无 flop 预求解树,或 node_id 行动线走不通。

下载:request.jsonresponse.json

turn 投影范围(turn→river)projected-range

POST https://pokerai.bet/v1/gto/turn/projected-range 免费

完整参数 / 响应 schema、在线试一试 → 交互式参考

flop 投影范围的 turn→river 版,用于一个实时 turn 求解。沿一条 turn 行动线(含收尾的 CALL/CHECK)收窄范围,直接得到进入 river 的起手范围。进入 turn 的 OOP/IP 范围直接取自该求解自己的配置(即 /v1/gto/solver 调度时用的范围),你只需给求解句柄 solve + 一条 node_id免费(该求解已在 /v1/gto/solver 计费),与 /v1/gto/solver/tree 一样;返回字段与 范围转换 相同。

先轮询 /v1/gto/solver/treespot_status = queryable 再调用。turn→river 不必手动拼 solver_results(平台从该求解读取并拼出)。

请求体

{
  "solve": "eyJ…",
  "node_id": "root/CHECK/BET 6.000000/CALL",
  "normalize": true,
  "bluff_discount_ratio": 0.8,
  "hero_position": "oop",
  "hero_hand": "AsKs"
}
字段说明
solve必填。/v1/gto/solver 返回的求解句柄(一个 turn 求解)。
node_id必填。turn 行动线,可以以收尾的 CALL/CHECK 结尾。求解器节点记法(带空格),如 "root/CHECK/BET 6.000000/CALL"(取自 /v1/gto/solver/tree 的节点)。
normalize可选,默认 true。是否对更新后范围归一。
bluff_discount_ratio可选。诈唬折扣(0..1)。
hero_position / hero_hand可选牌阻挡:hero_position("oop"/"ip")+ hero_hand(如 "AsKs"),把对手更新后范围里含 Hero 底牌的组合移除。
partner_hands可选,4 字符组合数组(如 ["Ac9c"]),需配合 hero_position。把对手 range_*_new_raw 里所有含这些牌的组合移除。仅输入。

调用示例

curl -s https://pokerai.bet/v1/gto/turn/projected-range -H "Authorization: Bearer $POKERAI_API_KEY" \
  -H "Content-Type: application/json" -d @turn_projected_range_request.json

响应(200,字段全列,长串截断)

{"bluff_combos_ratio": 0.33, "bluff_discount_ratio": 1, "board": ["8d", "4h", "8s", "Qc"], "hand_bottom_ranks_ip": "TcTs:2943,TdTh:2943,TdTs:2943,9h9s:3020,…", "hand_bottom_ranks_oop": "AhKd:4646,AhKc:4646,AcKh:4646,JsTs:4746,…", "hand_ranks_ip": "Ac8c:2007,Ah8h:2007,KcKs:2645,KhKs:2645,…", "hand_ranks_oop": "8c8h:85,Ah8h:2007,Ac8c:2007,9c8c:2029,…", "node_id": "root/CHECK/BET 6.000000/CALL", "path_length": 3, "range_ip_new": "99:0.603064,A8s:0.5,AKs:0.70197,AQs:0.453337,…", "range_ip_new_raw": "9c9d:0.71487,9c9h:0.394271,9c9s:0.71487,…", "range_ip_new_raw_before_normalization": "9c9d:0.714699,9c9h:0.394176,9c9s:0.714699,…", "range_oop_new": "88:0.131148,98s:0.0902494,A8s:0.0344418,…", "range_oop_new_raw": "8c8h:0.777777,9c8c:0.185048,9h8h:0.17177,…", "range_oop_new_raw_before_normalization": "8c8h:0.418968,9c8c:0.0996806,9h8h:0.0925282,…"}
字段说明
range_oop_new / range_ip_new归一后的进入 river 范围(类记法),直接当 river 求解的 oop_range / ip_range
range_*_raw / range_*_raw_before_normalization未归一 / 未折扣的中间值(组合记法)。
hand_ranks_* / hand_bottom_ranks_*手牌强度排名 / 底部范围排名(组合:rank,越小越强)。
node_id / board / path_length回显的行动线、公共牌(4 张)、行动线步数。
bluff_discount_ratio / bluff_combos_ratio本次实际使用的诈唬折扣参数。

注:免费,故响应quota 字段;也无 pot_type(那是 flop 专有)。

可能的错误 / 状态

HTTPerror / 状态触发 / 怎么修
200{ "spot_status": "computing" }求解尚未收敛 —— 继续轮询 /v1/gto/solver/tree 到 queryable。
400missing_solve / missing_node_idsolve 句柄或 node_id
410expired求解已过期(TTL)—— 用 /v1/gto/solver 重新调度。
502no_solution上游求解出错。

下载:request.jsonresponse.json

节点 EV solver

POST https://pokerai.bet/v1/gto/evs 免费

完整参数 / 响应 schema、在线试一试 → 交互式参考

已完成解在某个节点的逐手、逐动作期望值(EV)。给 solve 句柄 solve(来自 /v1/gto/solver)+ 一个 node_id(来自 /v1/gto/solver/tree);可选 hand 只取一手。免费(解已在 /v1/gto/solver 计过费)。先轮询 /v1/gto/solver/treespot_status = queryable

请求体

{
  "solve": "eyJ0Ijoic2x2X3h4eXoi...(来自 /v1/gto/solver 的句柄)",
  "node_id": "root",
  "hand": "2c2d"
}
字段说明
solve必填。/v1/gto/solver 返回的 solve 句柄。
node_id必填。/v1/gto/solver/tree 里的节点(solver 记法,如 "root""root/CHECK/BET 6.000000")。
hand可选。只取一手的 EV(如 "2c2d");省略=全部手牌。

响应(200,每手的 EV 数组与 actions 对齐)

{"node_id": "root", "task_id": "slv_srp_…", "player": 1, "round": "FLOP", "actions": ["CHECK", "BET 4.000000", "BET 97.000000"], "evs": {"2c2d": [-0.826359, -0.792223, -1.643411], "2c2h": […], …}}
字段说明
actions节点的动作(有序);每手的 EV 数组与之对齐。
evs每手 → 各动作的 EV 数组(bb)。传了 hand 时,evs 是该手的单个数组。
player / round / node_id / task_id谁行动、街、回显的节点 / 解 id。

注:免费,无 quota 字段。解未收敛时返回 { "spot_status": "computing" }(轮询 /v1/gto/solver/tree)。

打一手牌(全流程)

把各端点串起来:一手 SRP(UTG 开池、BTN 跟注),Hero = UTG。下面 curl 省略了鉴权头(同 Quickstart)。

① Preflop — 该不该开池

POST /v1/gto/preflop
{ "hole_cards": "AdKd", "positions": { "hero": "UTG" },
  "preflop_actions": [ { "position": "SB", "action": "small blind", "amount": 0.5 }, { "position": "BB", "action": "big blind", "amount": 1 } ] }

preflop_actions 只含 SB+BB 盲注(无加注)= Hero 是首个行动者(开池局面)。返回开池频率与尺度。

② Flop — 翻牌策略

翻牌 2c2h2s。flop 是决策树,两步:先取树,再用 Hero 节点的 token 取策略。

// 1) 取树(不需要 hole_cards)
POST /v1/gto/flop/tree
{ "board": "2c2h2s", "pot_type": "SRP",
  "positions": { "hero": "UTG", "raiser": "UTG", "caller": "BTN" } }
// → nodes[] 中 root (is_hero:true) 带一个 token

// 2) 用 root 的 token 取 Hero 策略
POST /v1/gto/flop/node
{ "node": "<root 的 token>", "hole_cards": "AdKd" }

Hero 第一个决策 = root;面对下注 / 第二次行动 → 选对应的 is_hero:true 节点。详见 Flop 决策树

③ Turn — 实时求解,范围从 flop 推导

转牌(如 9d)要实时解,需要进入 turn 的双方范围。用 flop 决策树 + 范围转换得到:

  1. /v1/gto/flop/tree 拿起手 oop_range / ip_range 与决策节点。
  2. 沿一条 flop 行动线(如 root/CHECK/BET_8/CALL)逐节点 /v1/gto/flop/node(不传 hole_cards)取 range_strategy,拼出 solver_results——脚本见 范围转换 节的 assemble_solver_results.py
  3. /v1/gto/range 沿该线更新 → range_oop_new / range_ip_new进入 turn 的范围
  4. 喂给 solver(board 给 4 张),轮询取树到 queryable 后取节点:
    POST /v1/gto/solver
    { "board": "2c2h2s9d", "oop_range": "<进turn的OOP范围>", "ip_range": "<进turn的IP范围>",
      "pot": <进turn底池>, "effective_stack": <进turn有效栈>, "hero": "OOP" }

已有自己的范围/局面?第 3 步直接给 oop_range / ip_range,跳过推导。

④ River — 实时求解

河牌(board 给 5 张)同 turn:可单独求解(直接给进入 river 的范围),或用范围转换把 turn 行动线再更新一次,得到进入 river 的范围再喂给 solver。

Poker 引擎 / 语义 API

包住 pokerkit-plus(pokerkit 0.7.3 超集):牌面/手牌语义、范围与权益、评牌/equity/ICM/记法、牌局模拟。复用同一 API key 与配额——便宜端点扣通用配额,蒙特卡洛端点扣 solve 配额。基址 /v1/pokerkit/*;牌输入用无分隔串(AsKsQs),枚举返回 {name,value}完整 per-endpoint 参数/schema、在线试一试交互式参考;机读 spec → /v1/pokerkit/openapi.json(随代码自动同步)。

牌面 / 手牌语义 扣 1 通用

牌面无关:/texture(湿度/连接性/听牌面)、/nuts(坚果 + 并列 combo)、/category-combos/board-report(texture+nuts)。Hero 相关:/hand-tier(成手档位)、/draws(听牌)、/outs(补牌)、/blockers(阻断坚果)、/hand-report(texture+tier+draws+outs 一次拿全)。

请求字段(本组通用)

字段说明
board必填。3/4/5 张公共牌,无分隔串,如 "AsKsQs"
holeHero 端点(hand-tier / draws / outs / blockers / hand-report)必填。2 张底牌,如 "JhTh";牌面端点(texture / nuts / category-combos / board-report)不需要。
hand_type可选,默认 StandardHighHand(v1 仅此,其它牌型返回 400)。
dead可选。死牌 / 已移除的牌(本组所有端点适用)。

Texture 扣 1 通用

POST https://pokerai.bet/v1/pokerkit/texture — 牌面结构:湿度/连接性/rank band/听牌可成性/花色形态。 完整参数 / 在线试一试 →

{"board": "AsKsQs"}

{"result": {"cards": ["As", "Ks", "Qs"], "wetness": {"name": "WET", "value": "Wet"}, "connectivity": {"name": "HIGH", "value": "High"}, "rank_band": {"name": "HIGH", "value": "High"}, "straight_draw": {"name": "OPEN_ENDED", "value": "Open-ended"}, "flush_draw": {"name": "LIVE", "value": "Live"}, "are_two_tone": false, "are_monotone": true, "are_rainbow": false}}

Nuts 扣 1 通用

POST https://pokerai.bet/v1/pokerkit/nuts — 该牌面的最强成手 + 所有并列的两张组合(含 is_royal / board_is_nuts)。 完整参数 / 在线试一试 →

{"board": "AsKsQs"}

{"result": {"hand": "TsJsKsQsAs", "combos": [{"cards": ["Ts", "Js"], "hand": "TsJsKsQsAs", "as_frozenset": ["Js", "Ts"]}], "candidate_count": 1176, "board_is_nuts": false, "is_royal": true, "label": {"name": "STRAIGHT_FLUSH", "value": "Straight flush"}}}

Category combos 扣 1 通用

POST https://pokerai.bet/v1/pokerkit/category-combos — 所有活的两张组合按成手类别分组。 完整参数 / 在线试一试 →

{"board": "AsKsQs"}

{"result": {"by_category": {"HIGH_CARD": [{"cards": ["2c", "3c"], "hand": "2c3cKsQsAs", "as_frozenset": ["2c", "3c"]}, {"cards": ["2c", "3d"], "hand": "2c3dKsQsAs", "as_frozenset": ["2c", "3d"]}], …}}}

Board report 扣 1 通用

POST https://pokerai.bet/v1/pokerkit/board-report — texture + nuts 一次返回(牌面综合)。 完整参数 / 在线试一试 →

{"board": "AsKsQs"}

{"result": {"texture": {"cards": ["As", "Ks", "Qs"], "wetness": {"name": "WET", "value": "Wet"}, "connectivity": {"name": "HIGH", "value": "High"}, "rank_band": {"name": "HIGH", "value": "High"}, "straight_draw": {"name": "OPEN_ENDED", "value": "Open-ended"}, "flush_draw": {"name": "LIVE", "value": "Live"}, "are_two_tone": false, "are_monotone": true, "are_rainbow": false}, "nuts": {"hand": "TsJsKsQsAs", "combos": [{"cards": ["Ts", "Js"], "hand": "TsJsKsQsAs", "as_frozenset": ["Js", "Ts"]}], "candidate_count": 1176, "board_is_nuts": false, "is_royal": true, "label": {"name": "STRAIGHT_FLUSH", "value": "Straight flush"}}}}

Hand tier 扣 1 通用

POST https://pokerai.bet/v1/pokerkit/hand-tier — Hero 成手档位(对子 / 两对 / 三条 / 踢脚档,是否坚果)。 完整参数 / 在线试一试 →

{"hole": "JhTh", "board": "AsKsQs"}

{"result": {"category": {"name": "STRAIGHT", "value": "Straight"}, "is_nut": false, "pair_tier": null, "two_pair_tier": null, "three_of_a_kind_tier": null, "kicker_tier": null, "nut_rank": {"name": "NON_NUT", "value": "Non-nut"}}}

Draws 扣 1 通用

POST https://pokerai.bet/v1/pokerkit/draws — Hero 的听牌(顺子听牌 / 同花听牌 / 听牌坚果度)。 完整参数 / 在线试一试 →

{"hole": "Ah5h", "board": "Kh7h2c"}

{"result": {"straight_draw": null, "flush_draw": {"name": "LIVE", "value": "Live"}, "nut_rank": {"name": "NUT", "value": "Nut"}}}

Outs 扣 1 通用

POST https://pokerai.bet/v1/pokerkit/outs — Hero 能升级成手类别的补牌,按结果类别分组 + 总数。 完整参数 / 在线试一试 →

{"hole": "Ah5h", "board": "Kh7h2c"}

{"result": {"by_category": {"ONE_PAIR": ["2d", "2s", "5c", "5d", "5s", "7c", "7d", "7s", "Kc", "Kd", "Ks", "Ac", "Ad", "As"], "FLUSH": ["2h", "3h", "4h", "6h", "8h", "9h", "Th", "Jh", "Qh"]}, "count": 23}}

Blockers 扣 1 通用

POST https://pokerai.bet/v1/pokerkit/blockers — Hero 的牌阻断了多少坚果组合(阻断牌 / 阻断比例)。 完整参数 / 在线试一试 →

{"hole": "AhAd", "board": "AsKsQs"}

{"result": {"nut_combos_total": 1, "nut_combos_blocked": 0, "blocker_cards": [], "block_fraction": 0.0, "blocks_nuts": false}}

Hand report 扣 1 通用

POST https://pokerai.bet/v1/pokerkit/hand-report — texture + tier + draws + outs 一次返回(Hero 综合,旗舰)。 完整参数 / 在线试一试 →

{"hole": "JhTh", "board": "AsKsQs"}

{"result": {"texture": {"cards": ["As", "Ks", "Qs"], "wetness": {"name": "WET", "value": "Wet"}, "connectivity": {"name": "HIGH", "value": "High"}, "rank_band": {"name": "HIGH", "value": "High"}, "straight_draw": {"name": "OPEN_ENDED", "value": "Open-ended"}, "flush_draw": {"name": "LIVE", "value": "Live"}, "are_two_tone": false, "are_monotone": true, "are_rainbow": false}, "tier": {"category": {"name": "STRAIGHT", "value": "Straight"}, "is_nut": false, "pair_tier": null, "two_pair_tier": null, "three_of_a_kind_tier": null, "kicker_tier": null, "nut_rank": {"name": "NON_NUT", "value": "Non-nut"}}, "draws": {"straight_draw": null, "flush_draw": null, "nut_rank": null}, "outs": {"by_category": {}, "count": 0}}}

范围 / 权益 扣 1 通用

/range/expand(notation→具体 combo)、/range/value(按成手类别阈值建价值范围,aggression ∈ NO_BET/SINGLE_BET/RAISED)、/range/nut-advantage(精确坚果占比,无抽样)。

请求字段(本组通用)

字段说明
notation(expand)范围记法数组,如 ["AA","KQs","QQ+"]
hero / villain(nut-advantage)各一个范围记法数组。
board公共牌(value / nut-advantage 必填;expand 不需要)。
aggression(value)NO_BET / SINGLE_BET / RAISED 定成手类别阈值;或用 floor 显式指定类别。

Range expand 扣 1 通用

POST https://pokerai.bet/v1/pokerkit/range/expand — 把范围记法展开成具体两张组合。 完整参数 / 在线试一试 →

{"notation": ["AA", "KQs"]}

{"result": [["Ac", "Ad"], ["Ac", "Ah"], ["Ac", "As"], ["Ad", "Ah"], ["Ad", "As"], ["Ah", "As"], ["Kc", "Qc"], ["Kd", "Qd"], ["Kh", "Qh"], ["Ks", "Qs"]]}

Range value 扣 1 通用

POST https://pokerai.bet/v1/pokerkit/range/value — 按成手类别阈值(aggression)构造价值范围。 完整参数 / 在线试一试 →

{"board": "AsKsQs", "aggression": "SINGLE_BET"}

{"result": [["2s", "3s"], ["2s", "4s"], ["2s", "5s"], ["2s", "6s"], ["2s", "7s"], ["2s", "8s"], …]}

Range nut-advantage 扣 1 通用

POST https://pokerai.bet/v1/pokerkit/range/nut-advantage — 精确按坚果类别计数的双方占比(无抽样,确定性)。 完整参数 / 在线试一试 →

{"hero": ["AA", "KK"], "villain": ["QQ", "JJ"], "board": "AsKsQs"}

{"result": {"hero_share": 0.5, "villain_share": 0.5, "basis": {"name": "NUT_SHARE", "value": "Nut share"}}}

评牌 · equity · ICM · 记法

扣 1 通用 /eval/hand/eval/compare(多手排名+并列)、/icm(确定性)、/notation/parse(.phh→结构化)。扣 1 solve 蒙特卡洛:/equity/hand-strength/range/equity-advantage——带 sample_count(有上限)与 seed(可复现,~2s@10k)。

请求字段(本组通用)

蒙特卡洛端点(equity / hand-strength / range/equity-advantage)扣 solve 配额,其余扣通用配额。

字段说明
hole / holdings / board评牌输入:单手 hole + board(eval/hand),或多手 holdings(2+)+ board(eval/compare)。
ranges / hole_range / hero / villain范围记法(equity / hand-strength / range/equity-advantage)。
sample_count / seed蒙特卡洛:样本数(有上限,超出按上限)/ 随机种子(可复现)。
payouts / chips(icm)奖励结构 / 各方筹码。
text(notation/parse).phh 手牌历史字符串。

Eval hand 扣 1 通用

POST https://pokerai.bet/v1/pokerkit/eval/hand — 评出 hole + board 的成手(5 张 + 类别 label)。 完整参数 / 在线试一试 →

{"hole": "JhTh", "board": "AsKsQs"}

{"result": {"hand": "JhThAsKsQs", "label": {"name": "STRAIGHT", "value": "Straight"}}}

Eval compare 扣 1 通用

POST https://pokerai.bet/v1/pokerkit/eval/compare — 多手在同一牌面排名(含并列 rank)。 完整参数 / 在线试一试 →

{"holdings": ["AcAd", "KsKh", "JhTh"], "board": "AsKsQs"}

{"result": [{"index": 2, "hole": "JhTh", "hand": "JhThAsKsQs", "rank": 1, "label": {"name": "STRAIGHT", "value": "Straight"}}, {"index": 0, "hole": "AcAd", "hand": "AcAdAsKsQs", "rank": 2, "label": {"name": "THREE_OF_A_KIND", "value": "Three of a kind"}}, {"index": 1, "hole": "KsKh", "hand": "KsKhAsKsQs", "rank": 3, "label": {"name": "THREE_OF_A_KIND", "value": "Three of a kind"}}]}

Equity 扣 1 solve

POST https://pokerai.bet/v1/pokerkit/equity — 多个范围在牌面上的权益(蒙特卡洛)。 完整参数 / 在线试一试 →

{"ranges": [["AA"], ["KK"]], "board": "", "sample_count": 2000, "seed": 7}

{"result": {"equities": [0.8235, 0.1765], "sample_count": 2000}}

Hand strength 扣 1 solve

POST https://pokerai.bet/v1/pokerkit/hand-strength — Hero 一手 / 范围对 N 人的胜率(蒙特卡洛)。 完整参数 / 在线试一试 →

{"hole_range": ["AhKh"], "board": "Qh7c2d", "player_count": 3, "sample_count": 2000, "seed": 7}

{"result": {"hand_strength": 0.3945, "player_count": 3, "sample_count": 2000}}

ICM 扣 1 通用

POST https://pokerai.bet/v1/pokerkit/icm — 按筹码 / 奖励结构算 ICM 权益分配(确定性)。 完整参数 / 在线试一试 →

{"payouts": [50, 30, 20], "chips": [5000, 3000, 2000]}

{"result": {"icm": [38.392857142857146, 32.75, 28.857142857142854]}}

Range equity-advantage 扣 1 solve

POST https://pokerai.bet/v1/pokerkit/range/equity-advantage — 双方范围的权益占比(蒙特卡洛)。 完整参数 / 在线试一试 →

{"hero": ["AA", "KK"], "villain": ["QQ", "JJ"], "board": "AsKsQs", "sample_count": 2000, "seed": 7}

{"result": {"hero_share": 0.80325, "villain_share": 0.19675, "basis": {"name": "EQUITY", "value": "Equity"}, "sample_count": 2000}}

Notation parse 扣 1 通用

POST https://pokerai.bet/v1/pokerkit/notation/parse — 解析 .phh 手牌历史为结构化配置(variant / 盲注 / 筹码 / 动作…)。 完整参数 / 在线试一试 →

{"text": "variant = \"NT\"\nante_trimming_status = true\nantes = [0, 0]\nblinds_or_straddles = [1, 2]\nmin_bet = 2\nstarting_stacks = [200, 200]\nactions = [\"d dh p1 AhKh\", \"d dh p2 QsQd\", \"p1 cbr 6\", \"p2 cc\", \"d db Qh7c2d\"]\n"}

{"result": {"variant": "NT", "ante_trimming_status": true, "antes": [0, 0], "blinds_or_straddles": [1, 2], "bring_in": null, "small_bet": null, "big_bet": null, "min_bet": 2, "starting_stacks": [200, 200], "actions": ["d dh p1 AhKh", "d dh p2 QsQd", "p1 cbr 6", "p2 cc", "d db Qh7c2d"], "automations": [{"name": "ANTE_POSTING", "value": "Ante posting"}, …], "author": null, "event": null, "day": null, "month": null, "year": null, "hand": null, "currency": null}}

牌局模拟 扣 1 通用

完全信息、无状态回放:客户端携带动作列表,服务端用 pokerkit 重建局面(零服务端状态)。/games/state(当前局面快照)、/games/step(追加 next_action;带 expected_action_count 作并发令牌,不符回 409)、/notation/replay(config 或 .phh 文本 → 逐步快照)、/cards/normalize(校验/规整牌串)。

请求字段(本组通用)

字段说明
variant必填。变体码,如 "NT"(无限德州);全部见 /v1/pokerkit/meta
antes / starting_stacks必填。每位玩家前注 / 起始筹码(整数数组)。
blinds_or_straddles / min_bet盲注 / 跨注;无限 / 底池限注必填 min_bet(固定限注 / 梭哈用 small_bet / big_bet / bring_in)。
actions到目前为止的动作列表(pokerkit 记法:d dh p1 AhKh 发底牌、p2 cbr 6 加注到 6、p1 cc 过牌/跟注、p1 f 弃牌)。
next_action / expected_action_count(step)要应用的动作 / 并发令牌(= 你扩展的列表长度,不符回 409)。
text / index(replay)可改用 .phh 文本;index 只取某一步快照。cards/normalize 仅需 cards(牌串)。

Games state 扣 1 通用

POST https://pokerai.bet/v1/pokerkit/games/state — 给动作列表 → 重建并返回当前局面快照(亮所有底牌)。 完整参数 / 在线试一试 →

{"variant": "NT", "antes": [0, 0], "blinds_or_straddles": [1, 2], "min_bet": 2, "starting_stacks": [200, 200], "actions": ["d dh p1 AhKh", "d dh p2 QsQd", "p2 cbr 6"]}

{"result": {"snapshot": {"terminal": false, "street_index": 0, "actor_index": 0, "pot": 8, "bets": [2, 6], "stacks": [198, 194], "board": [], "hole_cards": [{"player": 0, "cards": ["Ah", "Kh"]}, {"player": 1, "cards": ["Qs", "Qd"]}], "legal_actions": [{"action": "fold"}, {"action": "check_or_call", "amount": 4}, {"action": "complete_bet_or_raise_to", "min": 10, "max": 200}]}, "actions": ["d dh p1 AhKh", "d dh p2 QsQd", "p2 cbr 6"]}}

Games step 扣 1 通用

POST https://pokerai.bet/v1/pokerkit/games/step — 追加 next_action → 新快照(带并发令牌,不符回 409)。 完整参数 / 在线试一试 →

{"variant": "NT", "antes": [0, 0], "blinds_or_straddles": [1, 2], "min_bet": 2, "starting_stacks": [200, 200], "actions": ["d dh p1 AhKh", "d dh p2 QsQd", "p2 cbr 6"], "next_action": "p1 cc", "expected_action_count": 3}

{"result": {"snapshot": {"terminal": false, "street_index": 1, "actor_index": null, "pot": 12, "bets": [0, 0], "stacks": [194, 194], "board": [], "hole_cards": [{"player": 0, "cards": ["Ah", "Kh"]}, {"player": 1, "cards": ["Qs", "Qd"]}], "legal_actions": [{"action": "deal_board"}]}, "actions": ["d dh p1 AhKh", "d dh p2 QsQd", "p2 cbr 6", "p1 cc"]}}

Notation replay 扣 1 通用

POST https://pokerai.bet/v1/pokerkit/notation/replay — config 或 .phh → 每步局面快照(可选 index 取单步)。 完整参数 / 在线试一试 →

{"variant": "NT", "antes": [0, 0], "blinds_or_straddles": [1, 2], "min_bet": 2, "starting_stacks": [200, 200], "actions": ["d dh p1 AhKh", "d dh p2 QsQd", "p2 cbr 6", "p1 f"], "index": 2}

{"result": {"snapshot": {"terminal": false, "street_index": 0, "actor_index": 1, "pot": 3, "bets": [2, 1], "stacks": [198, 199], "board": [], "hole_cards": [{"player": 0, "cards": ["Ah", "Kh"]}, {"player": 1, "cards": ["Qs", "Qd"]}], "legal_actions": [{"action": "fold"}, {"action": "check_or_call", "amount": 1}, {"action": "complete_bet_or_raise_to", "min": 4, "max": 200}]}, "step_count": 5}}

Cards normalize 扣 1 通用

POST https://pokerai.bet/v1/pokerkit/cards/normalize — 校验 / 规整牌串为标准两字符牌。 完整参数 / 在线试一试 →

{"cards": "Ah Ks Qs"}

{"result": ["Ah", "Ks", "Qs"]}

另有 GET /v1/pokerkit/meta(版本 / 变体码 / 牌型 / 枚举词表)。每个端点的完整参数 schema 与在线试一试,见交互式参考(顶部可在两套 API 间浏览)。

全部示例下载

每个端点的完整请求/响应(真实数据、字段不省略)汇总,见上面各节的「下载」,或: preflop · flop tree · flop node · solver · range · projected range · projected range 响应 · assemble 脚本

版本与变更

版本策略

Changelog

日期变更
2026-07-04新增 /v1/gto/evs(已完成解在某节点的逐手逐动作 EV);/v1/gto/turn/projected-range 现在也支持 partner_hands
2026-07-04/v1/gto/flop/projected-range 现在会读取请求里的 bluff_discount_ratio / bluff_combos_ratio、把 Hero 自己底牌注入 Hero 范围,并新增 partner_hands(把队友死牌从对手范围里阻挡)。
2026-06-22新增 Poker 引擎 / 语义 API /v1/pokerkit/*(牌面/手牌语义、范围/权益、评牌/equity/ICM/记法、牌局模拟;复用同一 key 与配额)。见 下方
2026-06-22新增 /v1/gto/preflop/range(整段 13×13 翻前范围,一次返回 169 手型)。
2026-06-18拆分 flop:删除单点查询 /v1/gto/flop,改为 /v1/gto/flop/tree(取树)+ /v1/gto/flop/node(取节点,免费),与 solver 的 tree/node 对齐。
2026-06-17新增 /v1/gto/flop/projected-range(/v1/gto/range 的便捷封装:给齐 flop 局面 + 行动线,服务端自动拼决策树,直接返回进 turn 范围)。
2026-06-17牌面 / 手牌格式统一:输入无分隔串,响应 board 统一返回数组。
2026-06-17新增 OpenAPI 3.0 规范;新增范围记法 / 概念 / 全流程教程文档。
2026-06-17/v1/gto/solver 支持 flop 实时求解(board=3);bet_sizes.flop 可自定义 flop 下注档。
2026-06-17修复:flop 查询 raise 的 amount_bb 误为 0(现返回正确绝对 BB)。
2026-06-17新增 范围转换 /v1/gto/range;/flop/tree 暴露起手 oop_range / ip_range;/flop/solver/node 不传 hole_cards 时返回整段范围策略。
2026-06-16新增 turn / river 实时求解器 /v1/gto/solver 系列(独立 solve 配额);新增 flop 决策树 /v1/gto/flop/tree
2026-06-15移除所有响应里的 recommendation 字段——请按 frequency 自行选择动作。

pokerai.bet · GTO API v1