Kelly Criterion Google Sheet with Live Odds via API
The Kelly Criterion Google Sheet is one of the most searched tools in serious sports betting — and most versions floating around the internet are either broken, oversimplified, or frozen in time with hardcoded odds. This post fixes all three problems.
You'll build a live spreadsheet that:
- Pulls current no-vig probabilities straight from the MoneyLine API
- Calculates full Kelly and fractional Kelly stake sizes automatically
- Flags any bet where your edge exceeds a configurable threshold
No developer background required. You'll write a few formulas and paste one script. That's it.
What Kelly Actually Tells You (and What It Doesn't)
Before touching a spreadsheet, let's be clear on what the Kelly Criterion is and isn't.
Kelly answers one question: Given my edge and my odds, what fraction of my bankroll maximizes long-run growth rate?
The formula is:
f* = (bp - q) / b
Where:
- b = decimal odds minus 1 (your net profit per unit)
- p = your estimated probability of winning
- q = 1 - p (probability of losing)
If you believe the true probability of a -110 line is 55% and the book is pricing it at 52.4%, Kelly will tell you to bet a specific fraction. What it won't tell you is whether your probability estimate is correct. That's the hard part — and it's where pulling no-vig market odds matters.
A word on full Kelly: It's theoretically optimal under perfect probability estimates, which you'll never have. Most sharp bettors use quarter-Kelly or half-Kelly to stay solvent through variance. The sheet you're building will support both.
Sheet Structure: What to Build First
Open a new Google Sheet. You'll need three tabs:
- Config — bankroll, Kelly fraction, edge threshold
- Odds Input — where live API data lands
- Kelly Calculator — your actual analysis
Config Tab
| Row | Column A | Column B | |-----|----------|----------| | 1 | Bankroll | 5000 | | 2 | Kelly Fraction | 0.25 | | 3 | Min Edge Threshold | 0.03 |
Reference these cells everywhere else as Config!B1, Config!B2, Config!B3 so you never hardcode a number.
Odds Input Tab
This tab will be populated by the Apps Script you're about to write. For now, manually stub out the headers in row 1:
Event | Market | Selection | Book Odds (American) | No-Vig Prob | Book Implied Prob
Kelly Calculator Tab
Headers in row 1:
Event | Selection | Your Edge | Decimal Odds | Kelly % | Stake ($) | Flag
The Core Formulas
Assume your Odds Input tab has data starting at row 2, with book odds in column D and no-vig probability in column E.
Converting American Odds to Decimal
In Kelly Calculator, column D (Decimal Odds):
=IF('Odds Input'!D2>0, ('Odds Input'!D2/100)+1, (100/ABS('Odds Input'!D2))+1)
This handles both positive and negative American odds correctly.
Book Implied Probability (with vig)
In Odds Input, column F (Book Implied Prob):
=IF(D2>0, 100/(D2+100), ABS(D2)/(ABS(D2)+100))
Your Edge
In Kelly Calculator, column C (Your Edge):
='Odds Input'!E2 - 'Odds Input'!F2
This is your no-vig true probability minus the book's implied probability. Positive means you have an edge.
Full Kelly Percentage
In Kelly Calculator, column E (Kelly %):
=IF(C2<=0, 0, (('Odds Input'!E2 * (D2-1)) - (1-'Odds Input'!E2)) / (D2-1))
Breaking this down against the f* formula:
'Odds Input'!E2is p (your true probability, no-vig)D2-1is b (net decimal profit per unit)1 - 'Odds Input'!E2is q
Fractional Kelly Stake in Dollars
In Kelly Calculator, column F (Stake ($)):
=IFERROR(E2 * Config!B2 * Config!B1, 0)
Config!B2 is your Kelly fraction (0.25 for quarter-Kelly). Config!B1 is your bankroll.
Edge Flag
In Kelly Calculator, column G (Flag):
=IF(C2 >= Config!B3, "✅ BET", "—")
This puts a clean visual indicator on any bet where edge clears your minimum threshold (default 3%).
Protecting Against Garbage In
Wrap the Kelly calculation in a guard so negative-edge bets show zero rather than a negative stake:
=MAX(0, IFERROR((('Odds Input'!E2 * (D2-1)) - (1-'Odds Input'!E2)) / (D2-1) * Config!B2 * Config!B1, 0))
Use this directly in column F to collapse the Kelly % and Stake into one calculation if you want a tighter sheet.
Wiring Live Odds: Google Apps Script
This is where the sheet stops being a static calculator and becomes something worth opening daily. The Apps Script below calls the MoneyLine API /v1/edge endpoint, which returns no-vig probabilities and edge values pre-calculated for you.
Go to Extensions → Apps Script in your Google Sheet. Paste this:
const API_KEY = "YOUR_MLAPI_KEY"; // replace with your key
const BASE_URL = "https://mlapi.bet";
function fetchEdgeData() {
const sheet = SpreadsheetApp.getActiveSpreadsheet();
const oddsSheet = sheet.getSheetByName("Odds Input");
const url = `${BASE_URL}/v1/edge?sport=baseball_mlb&markets=h2h,spreads&bookmakers=fanduel,draftkings,betmgm`;
const options = {
method: "get",
headers: {
"Authorization": `Bearer ${API_KEY}`,
"Content-Type": "application/json"
},
muteHttpExceptions: true
};
const response = UrlFetchApp.fetch(url, options);
const statusCode = response.getResponseCode();
if (statusCode !== 200) {
Logger.log("API error: " + statusCode + " — " + response.getContentText());
return;
}
const data = JSON.parse(response.getContentText());
// Clear old data (keep headers in row 1)
oddsSheet.getRange("A2:F1000").clearContent();
const rows = [];
data.edges.forEach(edge => {
edge.outcomes.forEach(outcome => {
rows.push([
edge.event_name,
edge.market,
outcome.name,
outcome.price, // American odds
outcome.no_vig_prob, // True probability (decimal, e.g. 0.547)
outcome.implied_prob // Book implied prob with vig
]);
});
});
if (rows.length > 0) {
oddsSheet.getRange(2, 1, rows.length, 6).setValues(rows);
}
Logger.log(`Loaded ${rows.length} outcomes.`);
}
// Auto-refresh every 30 minutes
function createTrigger() {
ScriptApp.newTrigger("fetchEdgeData")
.timeBased()
.everyMinutes(30)
.create();
}
Run fetchEdgeData() once manually to test it. Then run createTrigger() once — after that, the sheet refreshes automatically every 30 minutes during the day.
A free-tier account at MoneyLine API gives you 1,000 credits/month. Each /v1/edge call costs 1 credit. Running every 30 minutes is 48 calls/day — well within limits if you're not running this 24/7.
To scope to specific sports or markets, adjust the query params. For live odds across all available books, swap in /v1/odds and parse outcomes[].price alongside a separately fetched no-vig probability.
Making It Actually Useful: Conditional Formatting and Sort
A spreadsheet with 80 rows of odds is noise. Here's how to filter it down.
Conditional Formatting on the Flag Column
- Select column G in Kelly Calculator
- Format → Conditional formatting
- Custom formula:
=G2="✅ BET" - Fill color: green
Now flagged bets pop visually.
Auto-Sort by Edge
Add this to the end of fetchEdgeData() in Apps Script to sort Kelly Calculator by edge descending every time new data loads:
const kellySheet = sheet.getSheetByName("Kelly Calculator");
const lastRow = kellySheet.getLastRow();
if (lastRow > 1) {
const range = kellySheet.getRange(2, 1, lastRow - 1, 7);
range.sort({ column: 3, ascending: false }); // Column C = Your Edge
}
Now every refresh surfaces the highest-edge plays at the top.
Connecting to the Broader EV Workflow
The Kelly Criterion tells you how much to bet. Expected value tells you whether to bet. They're different calculations on the same underlying data.
If you want to see how the no-vig probabilities this sheet uses are computed, check out the EV betting explainer — it walks through the math on positive-EV identification that the /v1/edge endpoint automates for you.
For a comparison of how MoneyLine's API data compares to other odds providers on latency and market coverage, see the API comparison page — relevant if you're deciding whether to run this sheet off one source or blend multiple feeds.
FAQ
What Kelly fraction should I actually use?
Most practitioners use quarter-Kelly (0.25) or half-Kelly (0.5). Full Kelly (1.0) is theoretically optimal only if your probability estimates are perfect — they aren't. Quarter-Kelly cuts your variance dramatically while still capturing most of the growth-rate benefit. Start at 0.25 and move up only if you have a multi-year track record validating your edge estimates.
How do I get no-vig probabilities without an API?
You can calculate them manually: for a two-way market, divide each side's implied probability by the sum of both implied probabilities. For example, if both sides are -110, each implied prob is 52.38%, sum is 104.76%, so no-vig is 52.38/104.76 = 50%. It's tedious at scale, which is why pulling pre-calculated values from /v1/edge saves time.
Can I use this sheet for parlays?
Kelly Criterion applies cleanly to single-game bets. Parlays are mathematically messier because you're compounding correlated or uncorrelated edges. A conservative approach: treat each parlay leg independently, and only Kelly-size parlays if every leg clears your edge threshold individually. Otherwise, just don't parlay.
Does this work for player props?
Yes, with one caveat: props often have wider spreads and lower liquidity, so the "market" no-vig price is less reliable. If you're using MoneyLine's /v1/edge data for props, pay attention to the number of books contributing to the consensus — fewer books means less reliable no-vig estimates.
What happens when the API returns no data?
The fetchEdgeData() function logs errors and exits cleanly — it won't wipe your existing data if the call fails. You'll see the error in the Apps Script log (View → Logs). Common causes: expired API key, wrong sport slug, or a market that's not yet open for the day.
What You've Built
At this point you have a live Kelly Criterion spreadsheet that:
- Pulls no-vig probabilities and edge data from the MoneyLine API every 30 minutes
- Converts American odds to decimal automatically
- Calculates fractional Kelly stakes against a configurable bankroll
- Flags bets that clear your minimum edge threshold
- Sorts itself by edge after every refresh
The only number you're responsible for is your bankroll. Everything else is data-driven. That's the point — remove as many subjective inputs as possible and let the math do the work.
If you want to extend this further — adding a line-movement column, a closing-line-value tracker, or a season P&L summary — the structure is already there. Add tabs, reference the same Config values, and keep the data flowing from the API.