Logo
Hold'em API · v1

Developer Documentation

This page documents two core endpoints for probabilistic action modeling and Bayesian range updates. All examples use a black theme and reference local development at https://www.holdemapi.com.

Base URL: https://www.holdemapi.comRoute #1: /api/p_actionsRoute #2: /api/update_range

Introduction

Hold’em API’s decision models are trained on millions of real, anonymized hand histories from online cash games and tournaments. We combine population behavior with solver-guided augmentation to learn action probabilities, range transitions, and equity patterns that reflect how people actually play—across positions, stack depths, and streets.

Data hygiene & privacy

Histories are aggregated, de-duplicated, and stripped of personal identifiers; we retain only event sequences (positions, actions, sizes, stacks, board/hand cards). Data is used in aggregate to train models—never to target individual players.

Coverage

2–9 handed, preflop → river, common bet-sizing ladders, and typical formations (single-raised, 3-bet, multi-way where available).

Learning targets

Three heads: (1) fold/continue, (2) call vs. raise (conditional on continuing), and (3) aggressive size mix when betting/raising is selected.

Calibration

Models are calibrated on held-out pools and checked with reliability curves. We ship versioned model IDsin responses where applicable.

What this means for you
  • Plug-and-play probabilities that mirror population tendencies by street and position.
  • Range updates that trend toward frequencies observed in large samples.
  • Sensible defaults when no villain range is provided (population priors), with the option to condition on your own ranges for finer control.

Intended use: off-table analysis, training, and analytics—not real-time assistance during live play.

Conventions
  • Units: sizes reported as sizeBB (big blinds). Fractions in sizeFractions are pot fractions.
  • Seat labels: UTG, HJ, CO, BTN, SB, BB.
  • Cards: two-char per card (rank+suit), e.g. Qd. Combos are concatenations like KdAs.
  • Probabilities: well-formed outputs sum to 1 (up to numerical precision). Extremely small weights are clipped to zero in responses.

Shared Schema & Validation

Combo Rules

FieldTypeReq?Description
FormatstringYesTwo-card combo like 'KdAs' or suited 'KhQh'. Use ranks [2–9,T,J,Q,K,A] and suits [c,d,h,s].
CasestringNoUppercase recommended for clarity (e.g., As, Kd).
UniquenessbooleanNoNo duplicates in a single range payload.
ValiditystringNoCards must be distinct; suit-sensitive for suited/offsuit combos.

Board

FieldTypeReq?Description
street"preflop" | "flop" | "turn" | "river"YesDetermines required card count.
cardsstring[]YesFlop: 3, Turn: 4, River: 5. Preflop: [].
potBBnumberYesCurrent pot expressed in big blinds at decision time.

History Events

FieldTypeReq?Description
actorstringYesSeat label like UTG/HJ/CO/BTN/SB/BB.
type"fold"|"check"|"call"|"bet"|"raise"YesAction kind.
sizeBBnumberNoRequired for bet/raise; total size in BB, not increment.
street"preflop"|"flop"|"turn"|"river"YesStreet on which action occurred.

POST /api/p_actions — Predict Actions

Returns model fold/call/raise outputs, continuation probabilities, and (if facing a bet) a distribution over raise/bet size buckets. Includes per-model diagnostics and a sampled action with RNG metadata.

Example Request (curl)

curl
curl -X POST \
  'https://www.holdemapi.com/api/p_actions' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer $API_KEY' \
  -d '{
  "hand": "KdAs",
  "range": "TT+,AQs+",
  "tau": 0.1,
  "action": {
    "type": "raise",
    "actor": "CO"
  },
  "context": {
    "board": {
      "street": "turn",
      "cards": [
        "8h",
        "5d",
        "2s",
        "Qd"
      ],
      "potBB": 14.5
    },
    "history": [
      {
        "actor": "HJ",
        "type": "raise",
        "sizeBB": 2.5,
        "street": "preflop"
      },
      {
        "actor": "CO",
        "type": "call",
        "sizeBB": 2.5,
        "street": "preflop"
      },
      {
        "actor": "HJ",
        "type": "bet",
        "sizeBB": 5,
        "street": "flop"
      },
      {
        "actor": "CO",
        "type": "call",
        "sizeBB": 5,
        "street": "flop"
      },
      {
        "actor": "HJ",
        "type": "bet",
        "sizeBB": 10.5,
        "street": "turn"
      }
    ],
    "effectiveStackBB": 80,
    "numPlayers": 6,
    "subjectPosition": "CO"
  },
  "options": {
    "smoothingEps": 0.000001,
    "aggressiveTemp": 1.2,
    "returnDiagnostics": true
  }
}'

Sample Response (200 OK)

response.json
{
    "ok": true,
    "hand": "KdAs",
    "equity": 0.54,
    "rank": 72,
    "length_range": 34,
    "board": { "cards": ["8h", "5d", "2s", "Qd"], "potBB": 14.5 },
    "facingBet": true,
    "output": { "fold": 0.49, "call": 0.2927041660265801, "raise": 0.2172958339734199 },
    "givenContinue": { "call": 0.5739297373070198, "raise": 0.4260702626929802, "sum": 1 },
    "sizes": ["22%","34%","47%","63%","85%","112%","147%","200%","shove"],
    "weights": [0.02,0.04,0.08,0.15,0.26,0.25,0.12,0.06,0.02],
    "debug": {
        "models": {
            "FC": 0.48897695541381836,
            "CR": 0.4260702626929802,
            "RS": [0.02,0.04,0.08,0.15,0.26,0.25,0.12,0.06,0.02]
        }
    },
    "selected": {
        "action": "raise",
        "amountBB": 16.24,
        "bucket": "112%",
        "rng": { "fc": 0.13, "cr": 0.42, "rs": 0.37 }
    }
}

Type Definitions

types.ts
// ----- Request
export interface PAction {
    type: 'fold' | 'check' | 'call' | 'bet' | 'raise';
    actor: string;          // e.g., 'CO', 'HJ', 'SB'
    sizeBB?: number;        // optional when type is bet/raise
    street?: 'preflop' | 'flop' | 'turn' | 'river';
}

export interface HistoryEvent extends PAction {
    // Required: each event should include street to anchor order
    street: 'preflop' | 'flop' | 'turn' | 'river';
}

export interface BoardContext {
    street: 'preflop' | 'flop' | 'turn' | 'river';
    cards: string[];       // e.g., ['8h','5d','2s','Qd'] (empty on preflop)
    potBB: number;         // pot size in big blinds at this decision
}

export interface DecisionContext {
    board: BoardContext;
    history: HistoryEvent[];
    effectiveStackBB: number;
    numPlayers: number;
    subjectPosition: string;   // hero seat label, e.g. 'CO'
}

export interface PActionOptions {
    smoothingEps?: number;       // probability floor; avoids zeros
    aggressiveTemp?: number;     // temperature for raise/bet softness
    returnDiagnostics?: boolean;
}

export interface PActionRequest {
    hand: string;               // 'KdAs'
    range?: WeightedRange;      // opponent prior (defaults to uniform)
    tau?: number;               // temperature for fold gate
    action: PAction;            // current contemplated action (used for conditioning/labels)
    context: DecisionContext;   // full state
    options?: PActionOptions;
}

// ----- Response
export interface PActionResponse {
    ok: boolean;
    facingBet: boolean;
    hand: string;
    rank: number;
    length_range: number;   // combos in hero range after blockers (max 1326)
    board: { cards: string[]; potBB: number };
    output: { fold: number; call: number; raise: number } | { check: number; bet: number };
    givenContinue: { call: number; raise: number; sum: 1 };
    sizes: string[];
    weights: number[];
    debug?: { models: { FC: number; CR: number; RS: number[] } };
    selected: {
        action: 'fold' | 'check' | 'call' | 'raise';
        amountBB: number | null;
        bucket: string | null;
        rng: { fc: number; cr: number; rs: number };
    };
}
Notes
  • output reports the raw model probabilities. When facing a bet it includes fold, call, and raise; otherwise it exposes check vs. bet from the CR model (call/check is the complement of the reported bet probability).
  • rank is 1-indexed by best hand (1 is strongest). length_range counts the combos in your supplied range after blockers, capped at 1,326.
  • weights is conditioned on continuing and aligns withsizes. Multiply the weights by 1 - output.fold (when facing a bet) if you need absolute mass per bucket.
  • aggressiveTemp softens/sharpens the raise probability; higher values → more uniform.

POST /api/update_range — Update Opponent Range

Performs a Bayesian-style update of an opponent’s weighted range after observing an action at the current node. Supports temperature/likelihood shaping, target-KL constraints, and an optional equity gate.

Example Request (curl)

curl
curl -X POST \
  'https://www.holdemapi.com/api/update_range' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer $API_KEY' \
  -d '{
  "range": {
    "AsKd": 0.3,
    "AsKs": 0.22,
    "KhQh": 0.18,
    "7c7d": 0.15,
    "JcTc": 0.15
  },
  "action": {
    "type": "raise",
    "sizeBB": 95,
    "actor": "SB"
  },
  "context": {
    "board": {
      "street": "preflop",
      "cards": [],
      "potBB": 12.5
    },
    "history": [
      {
        "actor": "UTG",
        "type": "fold",
        "street": "preflop"
      },
      {
        "actor": "HJ",
        "type": "raise",
        "sizeBB": 2.5,
        "street": "preflop"
      },
      {
        "actor": "CO",
        "type": "raise",
        "sizeBB": 8.5,
        "street": "preflop"
      },
      {
        "actor": "SB",
        "type": "raise",
        "sizeBB": 95,
        "street": "preflop"
      }
    ],
    "effectiveStackBB": 95,
    "numPlayers": 6,
    "subjectPosition": "SB"
  },
  "options": {
    "aggressiveTemp": 1.2
  }
}'

Sample Response (200 OK)

response.json
{
    "priorRange": {
        "KdAs": 0.35,
        "KsAs": 0.25,
        "QhKh": 0.15,
        "7c7d": 0.15,
        "TcJc": 0.1
    },
    "priorRangeCount": 5,
    "updatedRange": {
        "KdAs": 0.622111252028909,
        "7c7d": 0.377888747971091
    },
    "updatedRangeCount": 2,
    "fcModel": {
        "continue": 0.2714932126696833,
        "fold": 0.7285067873303167
    },
    "fcModelRaw": {
        "continue": 0.48897695541381836,
        "fold": 0.5110230445861816
    },
    "fcProbabilities": {
        "KdAs": 0.41,
        "KsAs": 0.38,
        "QhKh": 0.29,
        "7c7d": 0.12,
        "TcJc": 0.08
    },
    "crProbabilities": {
        "KdAs": 0.23,
        "KsAs": 0.22,
        "QhKh": 0.14,
        "7c7d": 0.05,
        "TcJc": 0.03
    },
    "modelInput": [
        0.12,
        0.34,
        0.56
    ]
}

Type Definitions

types.ts
// ----- Request
export type Combo = string; // e.g., 'KdAs', '7c7d'

export interface WeightedRange {
    [combo: Combo]: number; // probabilities should sum ≈ 1 (lib can renormalize)
}

export interface UpdateRangeOptions {
    aggressiveTemp?: number;      // soften raise/bet logits
}

export interface UpdateRangeRequest {
    range?: WeightedRange;       // opponent prior (defaults to uniform)
    action: {
        type: 'fold' | 'check' | 'call' | 'bet' | 'raise';
        sizeBB?: number;
        actor: string;           // seat label
    };
    context: {
        board: { street: 'preflop' | 'flop' | 'turn' | 'river'; cards: string[]; potBB: number; };
        history: { actor: string; type: 'fold'|'check'|'call'|'bet'|'raise'; sizeBB?: number; street: 'preflop'|'flop'|'turn'|'river'; }[];
        effectiveStackBB: number;
        numPlayers: number;
        subjectPosition: string;
    };
    options?: UpdateRangeOptions;
}

// ----- Response
export interface UpdateRangeResponse {
    priorRange: WeightedRange;   // prior after pruning / normalization
    priorRangeCount: number;   // remaining combos in prior
    updatedRange: WeightedRange; // posterior (default key)
    updatedRangeCount: number; // combos with non-zero weight in posterior
    fcModel?: { continue: number; fold: number }; // ratio of continuing combos
    fcModelRaw?: { continue: number; fold: number }; // unadjusted model output
    fcProbabilities?: WeightedRange;   // FC fold probability per combo
    crProbabilities?: WeightedRange;   // CR aggressive probability per combo
    actionProbabilities?: WeightedRange; // likelihood for observed action per combo
    modelInput?: number[]; // shared CR/action model input (first combo)
}
Notes
  • If range is omitted, the server assumes a uniform distribution over all valid two-card combos (excluding board cards).
  • Probabilities < 1e-6 are treated as zero. Such hands are omitted from responses and the tiny mass is discarded (no renormalization).
  • If a provided range omits some combos, the server assumes the unspecified hands have weight 0. You can simply leave zero-weight hands out of the request payload.
  • priorRange shows the normalized prior after any fold/continue pruning with priorRangeCount indicating how many combos from the request remain in the prior. Even if the posterior trims combos to zero weight, the prior retains them so you can compare before and after. updatedRange is the renormalized posterior with its own updatedRangeCount.
  • Equity gate can suppress combos that fail equity thresholds; in soft mode it downweights, in hard mode it clamps to zero. rescaleToFC preserves fold/continue mass.

Errors

HTTPCodeWhen
400BAD_REQUESTMalformed cards/combos; missing street; bet/raise without sizeBB; invalid probabilities.
401UNAUTHORIZEDMissing/invalid Authorization header.
403FORBIDDENPlan limit exceeded or key lacks scope.
409CONFLICTHistory inconsistent with board/stacks; impossible sequence detected.
422UNPROCESSABLE_ENTITYRange doesn’t sum (and cannot be renormalized); negative/NaN weights.
429RATE_LIMITEDToo many requests.
500INTERNAL_ERRORUnexpected server error.
Implementation Tips
  • Always include street on every history event; ordering is derived from streets + array order.
  • Provide potBB at the current node; the server uses this to translate raise-size buckets into concrete amountBBvalues for the sampled action.
  • For multiway spots, set numPlayers and subjectPosition accurately; both routes rely on position-aware features.
  • Posterior weights are proportional to prior × action likelihood; if every likelihood is zero the server falls back to the prior.