# Sports Odds → Fair Probability API (`westerly_breaker/sports-odds-fair-probability`) Actor

Aggregate sportsbook odds via The Odds API (bring your own key) and strip the vig with multiplicative/power/Shin de-vig to get fair, vig-free probabilities.

- **URL**: https://apify.com/westerly\_breaker/sports-odds-fair-probability.md
- **Developed by:** [Daniel Posztos](https://apify.com/westerly_breaker) (community)
- **Categories:** AI, Agents, Automation
- **Stats:** 1 total users, 0 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.01 / 1,000 dataset items

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.

Learn more: https://docs.apify.com/platform/actors/running/actors-in-store#pay-per-event

## What's an Apify Actor?

Actors are a software tools running on the Apify platform, for all kinds of web data extraction and automation use cases.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.
The best way to integrate Actors is as follows.

In JavaScript/TypeScript projects, use official [JavaScript/TypeScript client](https://docs.apify.com/api/client/js.md):

```bash
npm install apify-client
```

In Python projects, use official [Python client library](https://docs.apify.com/api/client/python.md):

```bash
pip install apify-client
```

In shell scripts, use [Apify CLI](https://docs.apify.com/cli/docs.md):

````bash
# MacOS / Linux
curl -fsSL https://apify.com/install-cli.sh | bash
# Windows
irm https://apify.com/install-cli.ps1 | iex
```bash

In AI frameworks, you might use the [Apify MCP server](https://docs.apify.com/platform/integrations/mcp.md).

If your project is in a different language, use the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).


# README

## Sports Odds → Fair Probability API — The Odds API De-Vig

**SEO title:** Sports Odds Fair Probability API — De-Vig Odds
**SEO description:** Aggregate sportsbook odds via The Odds API (bring your own key) and strip the vig (multiplicative/power/Shin) to get fair, vig-free probabilities. No scraping, no anti-bot.

Raw sportsbook odds are a commodity — dozens of sites publish them. The **processed, vig-free probability** is the product: this actor pulls moneyline/spread/total odds from [The Odds API](https://the-odds-api.com/) (you bring your own key — zero scraping, zero anti-bot risk) and strips the bookmaker's margin with your choice of **multiplicative**, **power**, or **Shin (1992)** de-vig, returning one row per `(event, bookmaker, market)` with raw odds, raw implied probability, and the fair de-vigged probability side by side.

Built for betting modelers, cross-market arbitrage tooling (pairs naturally with actor #7, Prediction Market Odds API), and AI agents that need a clean, structured, already-de-vigged number instead of raw decimal odds.

### Why this exists

Every sportsbook's quoted odds sum to **more than 100%** implied probability — that extra margin is the bookmaker's vig (their guaranteed edge). A raw odds feed tells you what the book is charging; it doesn't tell you the book's actual assessment of the probability. Removing the vig ("de-vig") requires picking a model for how the margin was distributed across outcomes, and three are in common use, each with different assumptions:

- **Multiplicative** (aka proportional): normalizes raw implied probabilities to sum to 1. Simple, robust, the industry default.
- **Power**: solves for a shared exponent applied to every outcome's raw implied probability. Shrinks longshot probabilities more aggressively than multiplicative.
- **Shin (1992)**: models the vig as compensation for insider/informed betting. Widely considered the most theoretically grounded for sports betting specifically (as opposed to prediction markets). This actor's Shin implementation is n-way (works for 2-outcome moneylines/spreads/totals AND 3-outcome soccer 1X2 markets), validated against a hand-verified 2-way reference; it falls back to multiplicative (logged) only for the rare book with ~zero/negative overround, where Shin's model has no solution.

This actor computes all three the same way for every row, so you can compare methods on the exact same odds instead of re-implementing the math yourself.

### Input

| Field | Type | Default | Description |
|---|---|---|---|
| `odds_api_key` | string (secret) | — | **Required.** Your own The Odds API key ([free tier: 500 requests/month](https://the-odds-api.com/#get-access)). Bring-your-own-key: never logged, never in output. |
| `sport` | string | — | **Required.** A The Odds API sport_key, e.g. `"basketball_nba"`, `"soccer_epl"`, or the special value `"upcoming"` (next games across all sports). Validated against the live `/v4/sports` catalog at run time (free) — an unknown key fails fast with example valid keys. |
| `leagues` | array of strings | `[]` | Additional sport_keys to fetch in the SAME run alongside `sport` (each costs its own separate quota). |
| `books` | array of strings | `[]` | Bookmaker keys to restrict to, e.g. `["draftkings", "fanduel", "pinnacle"]`. Maps to the Odds API `bookmakers` param. |
| `regions` | array of strings | `["us"]` | Odds API region codes (`us`, `uk`, `eu`, `au`, ...) controlling which bookmakers are eligible. Each additional region multiplies quota cost. |
| `markets` | array of strings | `["h2h"]` | `"h2h"` (moneyline/1x2), `"spreads"` (point spread), `"totals"` (over/under). Each requested market multiplies quota cost. |
| `devig_method` | string | `"multiplicative"` | `"multiplicative"`, `"power"`, or `"shin"`. |
| `max_events` | integer (1–200) | `25` | Max DISTINCT events, fair-shared across every requested sport_key. Each event still produces one row per (bookmaker × market) it has. |

#### Worked example — input

```json
{
  "odds_api_key": "YOUR_THE_ODDS_API_KEY",
  "sport": "basketball_wnba",
  "markets": ["h2h"],
  "devig_method": "multiplicative",
  "max_events": 3
}
````

#### Worked example — output (1 real row, live data 2026-07-06)

```json
{
  "event": {
    "id": "1fbe348b23c9f547e63ce18ccf0888db",
    "sport_key": "basketball_wnba",
    "sport_title": "WNBA",
    "commence_time": "2026-07-06T23:40:00Z",
    "home_team": "Washington Mystics",
    "away_team": "Golden State Valkyries"
  },
  "bookmaker": { "key": "draftkings", "title": "DraftKings" },
  "market": "h2h",
  "devig_method": "multiplicative",
  "outcomes": [
    { "name": "Golden State Valkyries", "point": null, "raw_odds": 1.4, "implied_prob": 0.7142857142857143, "devigged_prob": 0.6853932584269663 },
    { "name": "Washington Mystics", "point": null, "raw_odds": 3.05, "implied_prob": 0.3278688524590164, "devigged_prob": 0.3146067415730337 }
  ],
  "overround": 1.0421545667447307,
  "devig_note": null,
  "last_update": "2026-07-06T21:29:09Z",
  "fetched_at": "2026-07-06T21:29:13.328287+00:00"
}
```

Note `implied_prob` sums to `1.0421...` (the 4.2% overround/vig DraftKings is charging) while `devigged_prob` sums to exactly `1.0` — the whole point of this actor. The same market run through `devig_method: "power"` gave `0.6973`/`0.3027`; through `"shin"`, `0.6932`/`0.3068` — all three sum to 1.0, all three pull the favorite up and the underdog down relative to raw implied probability, by different (documented) amounts.

### Output schema

One row per `(event, bookmaker, market)`. See `.actor/dataset_schema.json` for the full JSON Schema.

| Field | Type | Notes |
|---|---|---|
| `event` | object | `id`, `sport_key`, `sport_title`, `commence_time` (ISO 8601 UTC), `home_team`, `away_team` |
| `bookmaker` | object | `key` (e.g. `"draftkings"`), `title` (e.g. `"DraftKings"`) |
| `market` | string | `"h2h"`, `"spreads"`, or `"totals"` |
| `devig_method` | string | Which method this run used (uniform across every row) |
| `outcomes` | array | `name`, `point` (spread/total line; `null` for h2h), `raw_odds` (decimal), `implied_prob` (1/raw\_odds, pre-devig), `devigged_prob` (fair, post-devig; `null` if skipped — see `devig_note`) |
| `overround` | number or null | Sum of `implied_prob` across the row's outcomes, pre-devig. `null` when de-vig was skipped |
| `devig_note` | string or null | Non-null only when de-vig was skipped for this row — explains why |
| `last_update` | string or null | When this bookmaker last updated this market, per The Odds API |
| `fetched_at` | string | When this run fetched this row |

#### How spreads/totals are de-vigged

A `spreads` or `totals` market object, as returned by The Odds API's main-lines endpoint, already contains exactly the 2 complementary outcomes for **one single line** (e.g. `-6.5`/`+6.5`, or `Over 155.5`/`Under 155.5`) — confirmed live, never multiple alternate lines mixed into one market. So this actor de-vigs a market's own `outcomes` list directly, with no extra cross-line or cross-bookmaker grouping step: `h2h`, `spreads`, and `totals` are all handled by the exact same code path. If a market has fewer than 2 outcomes, or any outcome is missing a usable price (suspended/unavailable), de-vig is skipped for that row (`devigged_prob: null` on every outcome) and `devig_note` explains why — the row still ships with `raw_odds`/`implied_prob` wherever available, it is never silently dropped.

### Pricing (pay-per-event)

| Event | Price | When it's charged |
|---|---|---|
| `event-row` | $0.002 | Once per `(event, bookmaker, market)` row returned |

Plus Apify's own `apify-actor-start` synthetic event (first 5 seconds of compute free, platform-managed). **Your own Odds API quota is separate** — this actor never charges you for that, but a free-tier key is capped at 500 requests/month, and `markets`/`regions` multiply how many of those a single run costs (see "Quota notes" below).

**Example:** 25 events × ~6 bookmakers × 1 market averaged = ~150 rows: `150 × $0.002 = $0.30`.

If a run's cost would exceed the `Max total charge USD` you set, the actor stops delivering further rows at exactly that point — every delivered row was paid for, nothing paid-for is ever dropped, no crash. Because ALL upstream Odds API fetching happens once per requested sport\_key, up front, before any charging starts, a budget stop never leaves a half-fetched, wasted upstream request behind either.

### Quota notes (The Odds API, your own key)

- `GET /v4/sports` (used internally to validate `sport`/`leagues`) is **free** — confirmed live, doesn't touch your quota.
- Each odds fetch costs **`(number of markets requested) × (number of regions requested)`** of your quota, regardless of `books` filtering or how many events/bookmakers come back. Requesting `markets: ["h2h", "spreads", "totals"]` costs 3× a single-market request.
- Each additional `leagues` entry is a separate fetch with its own cost.
- Free-tier keys get 500 requests/month total (shared across every tool you use that key with, including this one).

### Error messages

- **Missing/invalid `odds_api_key`:** *"Missing or invalid 'odds\_api\_key': provide your own The Odds API key..."* — exit 1, no upstream call made at all.
- **Missing/invalid `sport`:** *"Missing or invalid 'sport': provide a The Odds API sport\_key..."* — exit 1, no upstream call made.
- **Unsupported `markets`/`devig_method`:** states the exact allowed values — exit 1, no upstream call made.
- **Unknown `sport`/`leagues` value:** *"Unknown sport\_key(s) \[...] -- not present in The Odds API's live /v4/sports catalog..."* with example valid keys — exit 1. This check itself is free (doesn't cost quota).
- **Invalid/expired `odds_api_key` (caught live, not at input-validation time):** *"The Odds API rejected this run: ... API key is not valid... (error\_code=INVALID\_KEY)"* — exit 1.
- **A requested sport\_key doesn't support the requested markets** (e.g. an outrights-only league like `golf_the_open_championship_winner` with `markets: ["h2h"]`): logged and that sport\_key is skipped (`INVALID_MARKET_COMBO`, itself free); other requested sport\_keys still run.
- **0 rows with otherwise valid input:** not an error — the run succeeds, but the log has an explicit `WARNING: 0 results produced despite valid input (...)` line and the run's status message says so.

### How it works

1. Validate structural input (key present, `devig_method`/`markets` known, ...) — fails fast with NO upstream call at all if anything is wrong.
2. `GET /v4/sports` (free) validates your API key live AND validates every `sport`/`leagues` value against the live catalog, before any paid call.
3. `GET /v4/sports/{sport}/odds` once per requested sport\_key (a fair per-key share of `max_events`, so one league can't consume the whole budget).
4. Flatten every `(event, bookmaker, market)` triple into one row, de-vig its outcomes directly.
5. Charge-and-deliver each row atomically — whatever is charged is delivered, and vice versa.
6. 0 rows with valid input logs an explicit warning instead of looking like a normal empty run.

### Known limitations (documented, not hidden)

- **Only "main" lines are fetched**, not alternate spreads/totals (The Odds API's `alternate_spreads`/`alternate_totals` markets are out of scope for this actor's low-effort MVP).
- **Shin de-vig falls back to multiplicative (logged) for a book with ~zero/negative overround** — a mathematical edge case (Shin's model has no solution there), not a "doesn't work for 3-way markets" limitation; this actor's test suite validates Shin for both 2-way and 3-way real-odds cases.
- **`max_events` caps events, not rows.** A run can still return far more `event-row`s (and cost far more) than `max_events` suggests, since each event fans out into one row per bookmaker × market.
- **Your Odds API quota is shared across every tool using that key** — this actor doesn't track your remaining quota across runs, only logs it per-call (`x-requests-remaining` in the run log).
- **`sport` and `leagues` are both The Odds API `sport_key` values** (there is no separate "sport vs. league" hierarchy in the underlying API) — `leagues` is simply "additional sport\_keys to fetch in the same run," not a sub-filter within `sport`.

### Data source

[The Odds API](https://the-odds-api.com/) (`api.the-odds-api.com/v4`) — bring your own key, zero scraping, zero anti-bot risk. This actor never ships with a shared/pooled key.

# Actor input Schema

## `odds_api_key` (type: `string`):

Your OWN The Odds API key (https://the-odds-api.com/ -- free tier: 500 requests/month). This actor is bring-your-own-key: it never ships with a shared/pooled key, and your key is never logged, stored in output, or shared. Get one at https://the-odds-api.com/#get-access.

## `sport` (type: `string`):

The Odds API sport\_key to fetch, e.g. "basketball\_nba", "soccer\_epl", "americanfootball\_nfl". Use the special value "upcoming" for the next games across ALL sports when you don't know a specific league key. Validated against The Odds API's live /v4/sports catalog at run time (a free call) -- an unknown key fails the run immediately with a list of valid examples. Call https://api.the-odds-api.com/v4/sports?apiKey=YOUR\_KEY to see the full current list.

## `leagues` (type: `array`):

Optional list of ADDITIONAL sport\_key values to fetch in the SAME run alongside 'sport' (e.g. sport="basketball\_nba", leagues=\["basketball\_wnba"] fetches both). Each additional key costs its own separate Odds API quota (see Pricing/quota notes in the README) and its own fair share of 'max\_events'. Leave empty to fetch only 'sport'.

## `books` (type: `array`):

Optional list of The Odds API bookmaker keys to restrict results to, e.g. \["draftkings", "fanduel", "pinnacle"]. Maps directly to the Odds API 'bookmakers' param. Leave empty to get every bookmaker available in the requested 'regions' instead.

## `regions` (type: `array`):

Odds API region codes controlling which bookmakers are eligible, e.g. "us", "uk", "eu", "au". Ignored for a book already named explicitly in 'books', but still required by the upstream API in general -- defaults to \["us"]. Each additional region multiplies quota cost (cost = markets requested x regions requested, see README).

## `markets` (type: `array`):

Which market type(s) to fetch and de-vig: "h2h" (moneyline/1x2 -- the clean 2-or-3-way case), "spreads" (point spread, 2 outcomes per line), "totals" (over/under, 2 outcomes per line). Each requested market multiplies quota cost (cost = markets requested x regions requested, see README) -- fetching all three in one call is 3x the cost of just "h2h".

## `devig_method` (type: `string`):

How to strip the bookmaker's margin (vig) to compute fair probabilities. "multiplicative" (default): normalize raw implied probabilities to sum to 1 -- simple, robust. "power": solves for a shared exponent -- shrinks longshot probabilities more aggressively than multiplicative. "shin": Shin's (1992) insider-trading model -- generally considered the most theoretically grounded for sports betting markets; falls back to multiplicative (logged) for the rare book with ~zero/negative overround, where Shin's model has no solution.

## `max_events` (type: `integer`):

Maximum number of DISTINCT events to include, fair-shared across every requested sport\_key ('sport' + 'leagues') so one league can't consume the whole budget. NOTE: this caps events, not output rows -- each included event still produces one output row per (bookmaker x market) it has, so total billed 'event-row's is usually a multiple of this number. Must be between 1 and 200.

## Actor input object example

```json
{
  "sport": "basketball_nba",
  "leagues": [],
  "books": [],
  "regions": [
    "us"
  ],
  "markets": [
    "h2h"
  ],
  "devig_method": "multiplicative",
  "max_events": 25
}
```

# Actor output Schema

## `event_rows` (type: `string`):

One row per (event, bookmaker, market): raw odds, implied probability, and vig-free de-vigged probability. Stored in the run's default dataset.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "sport": "upcoming"
};

// Run the Actor and wait for it to finish
const run = await client.actor("westerly_breaker/sports-odds-fair-probability").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = { "sport": "upcoming" }

# Run the Actor and wait for it to finish
run = client.actor("westerly_breaker/sports-odds-fair-probability").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print("💾 Check your data here: https://console.apify.com/storage/datasets/" + run["defaultDatasetId"])
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "sport": "upcoming"
}' |
apify call westerly_breaker/sports-odds-fair-probability --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=westerly_breaker/sports-odds-fair-probability",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

```json
{
    "openapi": "3.0.1",
    "info": {
        "title": "Sports Odds → Fair Probability API",
        "description": "Aggregate sportsbook odds via The Odds API (bring your own key) and strip the vig with multiplicative/power/Shin de-vig to get fair, vig-free probabilities.",
        "version": "0.1",
        "x-build-id": "ogf8AMdnHJyYllmnj"
    },
    "servers": [
        {
            "url": "https://api.apify.com/v2"
        }
    ],
    "paths": {
        "/acts/westerly_breaker~sports-odds-fair-probability/run-sync-get-dataset-items": {
            "post": {
                "operationId": "run-sync-get-dataset-items-westerly_breaker-sports-odds-fair-probability",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for its completion, and returns Actor's dataset items in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        },
        "/acts/westerly_breaker~sports-odds-fair-probability/runs": {
            "post": {
                "operationId": "runs-sync-westerly_breaker-sports-odds-fair-probability",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor and returns information about the initiated run in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/runsResponseSchema"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/acts/westerly_breaker~sports-odds-fair-probability/run-sync": {
            "post": {
                "operationId": "run-sync-westerly_breaker-sports-odds-fair-probability",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for completion, and returns the OUTPUT from Key-value store in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        }
    },
    "components": {
        "schemas": {
            "inputSchema": {
                "type": "object",
                "required": [
                    "odds_api_key",
                    "sport"
                ],
                "properties": {
                    "odds_api_key": {
                        "title": "The Odds API key (bring your own)",
                        "type": "string",
                        "description": "Your OWN The Odds API key (https://the-odds-api.com/ -- free tier: 500 requests/month). This actor is bring-your-own-key: it never ships with a shared/pooled key, and your key is never logged, stored in output, or shared. Get one at https://the-odds-api.com/#get-access."
                    },
                    "sport": {
                        "title": "Sport / league key",
                        "type": "string",
                        "description": "The Odds API sport_key to fetch, e.g. \"basketball_nba\", \"soccer_epl\", \"americanfootball_nfl\". Use the special value \"upcoming\" for the next games across ALL sports when you don't know a specific league key. Validated against The Odds API's live /v4/sports catalog at run time (a free call) -- an unknown key fails the run immediately with a list of valid examples. Call https://api.the-odds-api.com/v4/sports?apiKey=YOUR_KEY to see the full current list."
                    },
                    "leagues": {
                        "title": "Additional leagues",
                        "type": "array",
                        "description": "Optional list of ADDITIONAL sport_key values to fetch in the SAME run alongside 'sport' (e.g. sport=\"basketball_nba\", leagues=[\"basketball_wnba\"] fetches both). Each additional key costs its own separate Odds API quota (see Pricing/quota notes in the README) and its own fair share of 'max_events'. Leave empty to fetch only 'sport'.",
                        "default": [],
                        "items": {
                            "type": "string"
                        }
                    },
                    "books": {
                        "title": "Bookmakers filter",
                        "type": "array",
                        "description": "Optional list of The Odds API bookmaker keys to restrict results to, e.g. [\"draftkings\", \"fanduel\", \"pinnacle\"]. Maps directly to the Odds API 'bookmakers' param. Leave empty to get every bookmaker available in the requested 'regions' instead.",
                        "default": [],
                        "items": {
                            "type": "string"
                        }
                    },
                    "regions": {
                        "title": "Regions",
                        "type": "array",
                        "description": "Odds API region codes controlling which bookmakers are eligible, e.g. \"us\", \"uk\", \"eu\", \"au\". Ignored for a book already named explicitly in 'books', but still required by the upstream API in general -- defaults to [\"us\"]. Each additional region multiplies quota cost (cost = markets requested x regions requested, see README).",
                        "default": [
                            "us"
                        ],
                        "items": {
                            "type": "string"
                        }
                    },
                    "markets": {
                        "title": "Markets",
                        "type": "array",
                        "description": "Which market type(s) to fetch and de-vig: \"h2h\" (moneyline/1x2 -- the clean 2-or-3-way case), \"spreads\" (point spread, 2 outcomes per line), \"totals\" (over/under, 2 outcomes per line). Each requested market multiplies quota cost (cost = markets requested x regions requested, see README) -- fetching all three in one call is 3x the cost of just \"h2h\".",
                        "items": {
                            "type": "string",
                            "enum": [
                                "h2h",
                                "spreads",
                                "totals"
                            ],
                            "enumTitles": [
                                "Moneyline / 1x2 (h2h)",
                                "Point spread",
                                "Totals (over/under)"
                            ]
                        },
                        "default": [
                            "h2h"
                        ]
                    },
                    "devig_method": {
                        "title": "De-vig method",
                        "enum": [
                            "multiplicative",
                            "power",
                            "shin"
                        ],
                        "type": "string",
                        "description": "How to strip the bookmaker's margin (vig) to compute fair probabilities. \"multiplicative\" (default): normalize raw implied probabilities to sum to 1 -- simple, robust. \"power\": solves for a shared exponent -- shrinks longshot probabilities more aggressively than multiplicative. \"shin\": Shin's (1992) insider-trading model -- generally considered the most theoretically grounded for sports betting markets; falls back to multiplicative (logged) for the rare book with ~zero/negative overround, where Shin's model has no solution.",
                        "default": "multiplicative"
                    },
                    "max_events": {
                        "title": "Max events",
                        "minimum": 1,
                        "maximum": 200,
                        "type": "integer",
                        "description": "Maximum number of DISTINCT events to include, fair-shared across every requested sport_key ('sport' + 'leagues') so one league can't consume the whole budget. NOTE: this caps events, not output rows -- each included event still produces one output row per (bookmaker x market) it has, so total billed 'event-row's is usually a multiple of this number. Must be between 1 and 200.",
                        "default": 25
                    }
                }
            },
            "runsResponseSchema": {
                "type": "object",
                "properties": {
                    "data": {
                        "type": "object",
                        "properties": {
                            "id": {
                                "type": "string"
                            },
                            "actId": {
                                "type": "string"
                            },
                            "userId": {
                                "type": "string"
                            },
                            "startedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "finishedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "status": {
                                "type": "string",
                                "example": "READY"
                            },
                            "meta": {
                                "type": "object",
                                "properties": {
                                    "origin": {
                                        "type": "string",
                                        "example": "API"
                                    },
                                    "userAgent": {
                                        "type": "string"
                                    }
                                }
                            },
                            "stats": {
                                "type": "object",
                                "properties": {
                                    "inputBodyLen": {
                                        "type": "integer",
                                        "example": 2000
                                    },
                                    "rebootCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "restartCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "resurrectCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "computeUnits": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "options": {
                                "type": "object",
                                "properties": {
                                    "build": {
                                        "type": "string",
                                        "example": "latest"
                                    },
                                    "timeoutSecs": {
                                        "type": "integer",
                                        "example": 300
                                    },
                                    "memoryMbytes": {
                                        "type": "integer",
                                        "example": 1024
                                    },
                                    "diskMbytes": {
                                        "type": "integer",
                                        "example": 2048
                                    }
                                }
                            },
                            "buildId": {
                                "type": "string"
                            },
                            "defaultKeyValueStoreId": {
                                "type": "string"
                            },
                            "defaultDatasetId": {
                                "type": "string"
                            },
                            "defaultRequestQueueId": {
                                "type": "string"
                            },
                            "buildNumber": {
                                "type": "string",
                                "example": "1.0.0"
                            },
                            "containerUrl": {
                                "type": "string"
                            },
                            "usage": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "integer",
                                        "example": 1
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "usageTotalUsd": {
                                "type": "number",
                                "example": 0.00005
                            },
                            "usageUsd": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "number",
                                        "example": 0.00005
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
```
