Docs menu
API reference

Optimize a slate

How much to stake on each bet in a slate, sized across the whole slate, and how that could turn out. Counts one against your daily allowance.
POST/v1/optimize
The answer is allocation.stakes
Every response starts with allocation: a dollar stake for each bet that gets one, and their totalStake. Everything after it explains how the optimizer got there. Bets with no stake are left out of stakes; picks[].reason says why.
{
  "allocation": {
    "kellyMultiplier": 1,
    "riskConstraintMet": true,
    "totalStake": 65.07,
    "stakes": [
      {
        "pickIndex": 0,
        "id": "lad",
        "team": "Dodgers",
        "betType": "MONEYLINE",
        "line": null,
        "odds": -145,
        "fraction": 0.055356,
        "stake": 55.36
      },
      {
        "pickIndex": 1,
        "id": "chc",
        "team": "Cubs",
        "betType": "MONEYLINE",
        "line": null,
        "odds": 120,
        "fraction": 0.009715,
        "stake": 9.71
      }
    ]
  }
}

How it works

Each pick’s price is de-vigged to a fair probability. Your confidence moves it, or your probability replaces it. That probability drives a Kelly-shaped allocation across the whole slate, with each bet capped at the smaller of perBetCap and 1.5 times its single-bet Kelly. A simulation of 100,000 slates measures the risk of each sizing and chooses the share of Kelly the allocation uses.

Request

FieldTypeRequiredMeaning
bankrollnumberyesUSD. Greater than 0, at most 100,000,000.
picksarrayyes1 to 50 picks (Free 3, Bettor 12). See below.
maxTotalExposurenumbernoMost of the bankroll at risk across the slate, above 0 and at most 1.
perBetCapnumbernoMost of the bankroll on any one bet, 0.01 to 1. Not settable on Free (fixed at 25% of the at-risk amount).
maxBetsintegernoMost bets with a stake, 1 to 50. Omit for no maximum.
useAccountDefaultsbooleannoDefault true. false ignores your saved app settings.
seedintegernoSame seed and same input give an identical result. See Same answer twice.

Where settings come from

Each setting comes from the request, else your saved KellyIQ app settings (Bettor and up), else the defaults: 100% exposure, 20% per bet, no maximum. perBetCap never exceeds maxTotalExposure; an adjusted value comes with a warning. settingsApplied in the response reports every value and its source, and GET /v1/me shows what an omitted setting resolves to.

Each pick

FieldTypeRequiredMeaning
sportstringyesA key from GET /v1/sports, e.g. baseball_mlb.
teamstringyesMoneyline and spread: the team you are betting. Totals: either team in the game (see totals).
opponentstringyesThe other team in the game.
oddsintegeryesAmerican odds for your side: the team, or the Over or Under for totals.
opponentOddsintegeryesAmerican odds for the other side of the same market, so the margin can be removed.
drawOddsintegernoThree-way markets (soccer).
confidencestringone ofSTRONG_FADE, MODERATE_FADE, SLIGHT_FADE, MARKET, SMALL_EDGE, MODERATE_EDGE or STRONG_EDGE: how far your view is from the fair price.
probabilitynumberone ofYour own win probability, 0.000001 to 0.999999. Bettor and up.
betTypestringnoMONEYLINE (default), SPREAD, TOTAL_OVER or TOTAL_UNDER.
linenumbernoRequired for spreads and totals. Spreads are from your side: -1.5 means your team must win by 2 or more. Totals: the total, e.g. 8.5.
eventIdstringnoPicks sharing an eventId are treated as the same game.
gameDatestringnoYYYY-MM-DD.
idstringnoYour own label, up to 64 characters, echoed back.

Confidence or probability

Every pick sends exactly one of confidence or probability, and every pick in a request sends the same one. A pick with both, or a slate that mixes them, is refused with 400 VALIDATION_ERROR and counts against your allowance. For example, the Cubs below send both and the Yankees send a probability while the first pick sent a confidence:

{
  "error": "VALIDATION_ERROR",
  "message": "The request is invalid; see violations.",
  "violations": [
    "picks[1]: send exactly one of confidence or probability",
    "picks[2]: all picks must use the same input: confidence or probability"
  ],
  "requestId": "req_c7558abd757947bb94fe70d516136622"
}

To use your own number for just some bets, convert the rest to probabilities first, for example the fair probability from Remove the vig.

Totals

For TOTAL_OVER and TOTAL_UNDER, team and opponent name the two teams in the game, in either order; betType says which side you are on. odds is the price of your side (the Over for TOTAL_OVER), opponentOdds the price of the other side, and line the total. The third pick in the probability example is an Over 8.5.

Examples

Rate each bet with a confidence

curl https://api.kellyiq.com/v1/optimize \
  -H "Authorization: Bearer $KELLYIQ_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "bankroll": 1000,
  "picks": [
    {
      "id": "lad",
      "sport": "baseball_mlb",
      "team": "Dodgers",
      "opponent": "Padres",
      "odds": -145,
      "opponentOdds": 125,
      "confidence": "STRONG_EDGE"
    },
    {
      "id": "chc",
      "sport": "baseball_mlb",
      "team": "Cubs",
      "opponent": "Cardinals",
      "odds": 120,
      "opponentOdds": -140,
      "confidence": "MODERATE_EDGE"
    }
  ]
}'

Set limits for this call only

curl https://api.kellyiq.com/v1/optimize \
  -H "Authorization: Bearer $KELLYIQ_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "bankroll": 1000,
  "maxTotalExposure": 0.5,
  "perBetCap": 0.1,
  "useAccountDefaults": false,
  "seed": 42,
  "picks": [
    {
      "id": "lad",
      "sport": "baseball_mlb",
      "team": "Dodgers",
      "opponent": "Padres",
      "odds": -145,
      "opponentOdds": 125,
      "confidence": "STRONG_EDGE"
    },
    {
      "id": "chc",
      "sport": "baseball_mlb",
      "team": "Cubs",
      "opponent": "Cardinals",
      "odds": 120,
      "opponentOdds": -140,
      "confidence": "MODERATE_EDGE"
    }
  ]
}'
{
  "allocation": {
    "kellyMultiplier": 1,
    "riskConstraintMet": true,
    "totalStake": 32.54,
    "stakes": [
      {
        "pickIndex": 0,
        "id": "lad",
        "team": "Dodgers",
        "betType": "MONEYLINE",
        "line": null,
        "odds": -145,
        "fraction": 0.027678,
        "stake": 27.68
      },
      {
        "pickIndex": 1,
        "id": "chc",
        "team": "Cubs",
        "betType": "MONEYLINE",
        "line": null,
        "odds": 120,
        "fraction": 0.004857,
        "stake": 4.86
      }
    ]
  },
  "settingsApplied": {
    "maxTotalExposure": {
      "value": 0.5,
      "source": "request"
    },
    "perBetCap": {
      "value": 0.1,
      "source": "request"
    },
    "maxBets": {
      "value": null,
      "source": "default"
    }
  },
  "warnings": []
}

Your own probabilities, with a run line and a total

curl https://api.kellyiq.com/v1/optimize \
  -H "Authorization: Bearer $KELLYIQ_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "bankroll": 1000,
  "picks": [
    {
      "id": "lad",
      "sport": "baseball_mlb",
      "team": "Dodgers",
      "opponent": "Padres",
      "odds": -145,
      "opponentOdds": 125,
      "probability": 0.62
    },
    {
      "id": "chc-rl",
      "sport": "baseball_mlb",
      "team": "Cubs",
      "opponent": "Cardinals",
      "odds": 150,
      "opponentOdds": -175,
      "betType": "SPREAD",
      "line": -1.5,
      "probability": 0.42
    },
    {
      "id": "nyy-o",
      "sport": "baseball_mlb",
      "team": "Yankees",
      "opponent": "Red Sox",
      "odds": -110,
      "opponentOdds": -110,
      "betType": "TOTAL_OVER",
      "line": 8.5,
      "probability": 0.55
    }
  ]
}'
{
  "allocation": {
    "kellyMultiplier": 0.6,
    "riskConstraintMet": true,
    "totalStake": 95.41,
    "stakes": [
      {
        "pickIndex": 0,
        "id": "lad",
        "team": "Dodgers",
        "betType": "MONEYLINE",
        "line": null,
        "odds": -145,
        "fraction": 0.042448,
        "stake": 42.45
      },
      {
        "pickIndex": 1,
        "id": "chc-rl",
        "team": "Cubs",
        "betType": "SPREAD",
        "line": -1.5,
        "odds": 150,
        "fraction": 0.019704,
        "stake": 19.7
      },
      {
        "pickIndex": 2,
        "id": "nyy-o",
        "team": "Yankees",
        "betType": "TOTAL_OVER",
        "line": 8.5,
        "odds": -110,
        "fraction": 0.033258,
        "stake": 33.26
      }
    ]
  }
}

Response

The full response to the first example:

{
  "allocation": {
    "kellyMultiplier": 1,
    "riskConstraintMet": true,
    "totalStake": 65.07,
    "stakes": [
      {
        "pickIndex": 0,
        "id": "lad",
        "team": "Dodgers",
        "betType": "MONEYLINE",
        "line": null,
        "odds": -145,
        "fraction": 0.055356,
        "stake": 55.36
      },
      {
        "pickIndex": 1,
        "id": "chc",
        "team": "Cubs",
        "betType": "MONEYLINE",
        "line": null,
        "odds": 120,
        "fraction": 0.009715,
        "stake": 9.71
      }
    ]
  },
  "bankroll": 1000,
  "atRisk": 1000,
  "settingsApplied": {
    "maxTotalExposure": {
      "value": 1,
      "source": "default"
    },
    "perBetCap": {
      "value": 0.2,
      "source": "default"
    },
    "maxBets": {
      "value": null,
      "source": "default"
    }
  },
  "warnings": [],
  "picks": [
    {
      "index": 0,
      "id": "lad",
      "team": "Dodgers",
      "opponent": "Padres",
      "betType": "MONEYLINE",
      "line": null,
      "odds": -145,
      "eventId": null,
      "included": true,
      "reason": null,
      "probability": {
        "market": 0.571116,
        "yours": 0.614004
      },
      "expectedValue": 0.037456,
      "sizing": {
        "full": {
          "fraction": 0.055356,
          "stake": 55.36
        },
        "half": {
          "fraction": 0.027678,
          "stake": 27.68
        },
        "quarter": {
          "fraction": 0.013839,
          "stake": 13.84
        },
        "fixed": {
          "fraction": 0.850708,
          "stake": 850.71
        },
        "riskAdjusted": {
          "fraction": 0.055356,
          "stake": 55.36
        }
      }
    },
    {
      "index": 1,
      "id": "chc",
      "team": "Cubs",
      "opponent": "Cardinals",
      "betType": "MONEYLINE",
      "line": null,
      "odds": 120,
      "eventId": null,
      "included": true,
      "reason": null,
      "probability": {
        "market": 0.437956,
        "yours": 0.459854
      },
      "expectedValue": 0.011679,
      "sizing": {
        "full": {
          "fraction": 0.009715,
          "stake": 9.71
        },
        "half": {
          "fraction": 0.004857,
          "stake": 4.86
        },
        "quarter": {
          "fraction": 0.002429,
          "stake": 2.43
        },
        "fixed": {
          "fraction": 0.149292,
          "stake": 149.29
        },
        "riskAdjusted": {
          "fraction": 0.009715,
          "stake": 9.71
        }
      }
    }
  ],
  "risk": {
    "full": {
      "independent": {
        "trials": 100000,
        "meanProfit": 1.96,
        "stdDev": 46.77,
        "probLoss": 0.38836,
        "var5": -65.07,
        "cvar5": -65.07
      },
      "correlated": {
        "trials": 100000,
        "meanProfit": -1.48,
        "stdDev": 47.89,
        "probLoss": 0.42941,
        "var5": -65.07,
        "cvar5": -65.07
      }
    },
    "half": {
      "independent": {
        "trials": 100000,
        "meanProfit": 1.19,
        "stdDev": 23.36,
        "probLoss": 0.38437,
        "var5": -32.54,
        "cvar5": -32.54
      },
      "correlated": {
        "trials": 100000,
        "meanProfit": -0.86,
        "stdDev": 23.95,
        "probLoss": 0.43221,
        "var5": -32.54,
        "cvar5": -32.54
      }
    },
    "quarter": {
      "independent": {
        "trials": 100000,
        "meanProfit": 0.56,
        "stdDev": 11.69,
        "probLoss": 0.38569,
        "var5": -16.27,
        "cvar5": -16.27
      },
      "correlated": {
        "trials": 100000,
        "meanProfit": -0.35,
        "stdDev": 11.98,
        "probLoss": 0.42795,
        "var5": -16.27,
        "cvar5": -16.27
      }
    },
    "fixed": {
      "independent": {
        "trials": 100000,
        "meanProfit": 34,
        "stdDev": 719.11,
        "probLoss": 0.38545,
        "var5": -1000,
        "cvar5": -1000
      },
      "correlated": {
        "trials": 100000,
        "meanProfit": -24.51,
        "stdDev": 736.33,
        "probLoss": 0.42977,
        "var5": -1000,
        "cvar5": -1000
      }
    },
    "riskAdjusted": {
      "independent": {
        "trials": 100000,
        "meanProfit": 2.4,
        "stdDev": 46.73,
        "probLoss": 0.38378,
        "var5": -65.07,
        "cvar5": -65.07
      },
      "correlated": {
        "trials": 100000,
        "meanProfit": -1.54,
        "stdDev": 47.88,
        "probLoss": 0.42938,
        "var5": -65.07,
        "cvar5": -65.07
      }
    }
  },
  "seed": 5331169363815191,
  "apiVersion": "1.0",
  "engineVersion": "d4c05f6",
  "requestId": "req_4e0feecfd37049a9a7adce0cc98c2c3b",
  "explanation": "(plain-English summary, omitted here)"
}
FieldMeaning
allocationThe result. stakes: one row per bet with a stake (pickIndex, id, team, betType, line, odds, fraction of the bankroll, stake in USD). totalStake: their sum. kellyMultiplier: the share of full Kelly used. riskConstraintMet: whether that share keeps risk within bounds.
atRiskbankroll × maxTotalExposure: the amount being sized.
settingsAppliedEach setting's value and source: request, account, default or tier.
warningsAdjustments made to your input, in words.
picksOne per pick, in your order. included (has a stake) and reason when not (NO_EDGE, MAX_BETS, NOT_SELECTED, NOT_PRICEABLE); probability.market (fair) and .yours (used); expectedValue per 1 staked; sizing under each strategy.
riskSimulated results for each sizing (riskAdjusted is the allocation): meanProfit, stdDev, probLoss, var5 (5th percentile), cvar5 (average of the worst 5%). correlated is null below Sharp.
explanationPlain-English summary for display. Do not parse it; the wording changes.
seedThe seed used. Resend it with the same input to reproduce this result.
apiVersion, engineVersion, requestIdWhat produced the result. Keep them with the seed.

Errors

StatuserrorWhen
400VALIDATION_ERRORBad or missing fields, or mixed inputs: every problem is listed. An unknown sport is reported on its own, after the other fields pass.
403UPGRADE_REQUIREDToo many picks for your plan, or probability on Free.
422SLATE_NOT_OPTIMIZABLEValid, but nothing could be sized.
429DAILY_LIMIT_EXCEEDEDToday's allowance is used up.
429BURST_LIMIT_EXCEEDEDMore than 10 a minute, or another optimize is still running.
503SERVICE_BUSYServer capacity or a temporary outage. Not counted.

Plus the errors every endpoint can return; see Errors.