# Sports Odds Scraper (`scrapemint/sports-odds-scraper`) Actor

Scrape live sports betting odds across DraftKings, Pinnacle, FanDuel, BetMGM, Caesars, Bet365 and more. H2H, spreads, totals, props. Normalized schema, best price per outcome, arbitrage detection. No third party API key. Pay per row.

- **URL**: https://apify.com/scrapemint/sports-odds-scraper.md
- **Developed by:** [Kennedy Mutisya](https://apify.com/scrapemint) (community)
- **Categories:** Automation, Other
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, NaN bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per usage

This Actor is paid per platform usage. The Actor is free to use, and you only pay for the Apify platform usage, which gets cheaper the higher subscription plan you have.

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

## 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 Scraper: Live Lines from DraftKings, Pinnacle, FanDuel, BetMGM

Scrape live sports betting odds across DraftKings, Pinnacle, FanDuel, BetMGM, Caesars, Bet365, and any sportsbook event page. One row per event with a normalized markets array covering moneyline, spreads, totals, team totals, first half, and player props. Best price per outcome flagged across books, optional arbitrage detection. No third party API key required. Pay per row.

**Built for** sharp bettors hunting closing line value, syndicate traders running middle and arb strategies, fantasy and DFS analysts modeling implied probabilities, sportsbook operators monitoring competitor lines, BI teams piping odds into a warehouse, content teams powering odds widgets and previews, and AI builders training EV models or pick generators on a clean cross book dataset.

**Keywords this actor ranks for:** sports odds scraper, sportsbook odds api, draftkings odds scraper, fanduel odds scraper, pinnacle odds api, sports betting data, arbitrage finder, best odds tracker, betting line tracker, odds movement, closing line value, sportsbook data feed, odds to JSON, odds to CSV, NFL odds api, NBA odds api, soccer odds scraper, prop bet data.

---

### Why this actor

| Other odds tools | **This actor** |
|---|---|
| Require a paid API key (The Odds API, OpsBet) | Hits public sportsbook JSON endpoints directly. No third party key. |
| Single book or single sport | DraftKings + Pinnacle on every league out of the box, with a JSON-LD fallback for any other book |
| Rough HTML scraping that breaks weekly | Public JSON the books use to render their own clients. Same source, lower brittleness. |
| Decimal only | American, decimal, fractional, and implied probability formats |
| No edge detection | Best price flag per outcome plus arbitrage compute across books |
| No deduplication | Per event dedupe across runs, persisted in a key value store |
| Single market | h2h, spreads, totals, team totals, first half, and player props in one row |
| No periods | Period 0 (full match) plus first half splits where available |

---

### How it works

```mermaid
flowchart LR
    A[Sports + leagues<br/>or Event URLs] --> B[Per book endpoint resolver]
    B --> C[DraftKings<br/>eventgroups JSON]
    B --> D[Pinnacle<br/>matchups + markets]
    B --> E[Direct event URL<br/>JSON-LD fallback]
    C --> F[Normalize<br/>home / away / commenceTime]
    D --> F
    E --> F
    F --> G[Merge by event key<br/>sport+home+away+time]
    G --> H{Best price<br/>or arb requested?}
    H -->|yes| I[Compute edge<br/>per outcome]
    H -->|no| J[Skip]
    I --> K[One row per event<br/>markets[] across books]
    J --> K
    K --> L[(JSON CSV API)]
````

DraftKings event group endpoints serve the same JSON the DK web client renders. Pinnacle exposes a guest API used by their own front end. Both ship structured offers + outcomes with prices, points, and periods. Events are merged into a single row keyed on sport plus normalized home and away plus commenceTime, so a Lakers vs Celtics game shows once with prices indexed by book.

***

### What you get per row

```mermaid
flowchart LR
    R[Event row] --> R1[Identity<br/>sport eventKey commenceTime]
    R --> R2[Teams<br/>home away]
    R --> R3[Books<br/>which sources had data]
    R --> R4[Markets<br/>h2h spreads totals first_half]
    R --> R5[Outcomes<br/>name point prices per book]
    R --> R6[bestPrice<br/>book + edge vs avg]
    R --> R7[arbitrage<br/>edgePct + sides]
```

Toggle `computeBestPrice` and every outcome ships a `bestPrice` object with the highest decimal odds, the source book, and the percentage edge versus the average across books. Toggle `computeArbitrage` and two way markets get an `arbitrage` block when the inverse sum drops below 1.

***

### Quick start

**Today's NBA games across every supported book**

```json
{
  "sports": ["nba"],
  "books": ["draftkings", "pinnacle"],
  "markets": ["h2h", "spreads", "totals"],
  "computeBestPrice": true,
  "totalMaxEvents": 25
}
```

**NFL Sunday slate with arb detection**

```json
{
  "sports": ["nfl"],
  "books": ["draftkings", "pinnacle", "fanduel", "betmgm"],
  "markets": ["h2h", "spreads", "totals"],
  "computeArbitrage": true,
  "minArbPct": 0.5,
  "totalMaxEvents": 16
}
```

**Multi sport sweep with a 24h look ahead window**

```json
{
  "sports": ["nba", "nhl", "mlb", "soccer_epl"],
  "books": ["draftkings", "pinnacle"],
  "lookAheadHours": 24,
  "totalMaxEvents": 100
}
```

**Direct DraftKings event URL with player props**

```json
{
  "eventUrls": [
    "https://sportsbook.draftkings.com/event/lal-vs-bos/29489765"
  ],
  "markets": ["h2h", "spreads", "totals", "player_props"]
}
```

**Tennis ATP slate, decimal odds**

```json
{
  "sports": ["tennis_atp"],
  "books": ["pinnacle"],
  "markets": ["h2h", "totals"],
  "oddsFormat": "decimal",
  "totalMaxEvents": 50
}
```

***

### Sample output

```json
{
  "sport": "nba",
  "home": "Boston Celtics",
  "away": "Philadelphia 76ers",
  "commenceTime": "2026-04-28T23:05:00Z",
  "eventKey": "nba|bostonceltics|philadelphia76ers|2026-04-28T23:05",
  "books": ["draftkings", "pinnacle"],
  "sources": [
    "https://sportsbook.draftkings.com/event/29489765",
    "https://www.pinnacle.com/en/nba/matchup/1629428987"
  ],
  "markets": [
    {
      "key": "h2h",
      "label": "moneyline",
      "outcomes": [
        {
          "name": "Boston Celtics",
          "point": null,
          "prices": {
            "draftkings": { "price": -550, "decimal": 1.182 },
            "pinnacle":   { "price": -528, "decimal": 1.189 }
          },
          "bestPrice": {
            "book": "pinnacle",
            "price": -528,
            "decimal": 1.189,
            "averageDecimal": 1.1855,
            "edgePctVsAverage": 0.30
          }
        },
        {
          "name": "Philadelphia 76ers",
          "point": null,
          "prices": {
            "draftkings": { "price": 400, "decimal": 5.0 },
            "pinnacle":   { "price": 423, "decimal": 5.23 }
          },
          "bestPrice": {
            "book": "pinnacle",
            "price": 423,
            "decimal": 5.23,
            "averageDecimal": 5.115,
            "edgePctVsAverage": 2.25
          }
        }
      ],
      "arbitrage": {
        "point": null,
        "edgePct": 0.81,
        "sides": [
          { "name": "Boston Celtics", "book": "pinnacle", "decimal": 1.189 },
          { "name": "Philadelphia 76ers", "book": "pinnacle", "decimal": 5.23 }
        ]
      }
    },
    {
      "key": "spreads",
      "label": "spread",
      "outcomes": [
        {
          "name": "Boston Celtics",
          "point": -8.5,
          "prices": {
            "draftkings": { "price": -110, "decimal": 1.909 },
            "pinnacle":   { "price": -103, "decimal": 1.971 }
          },
          "bestPrice": {
            "book": "pinnacle",
            "price": -103,
            "decimal": 1.971,
            "averageDecimal": 1.94,
            "edgePctVsAverage": 1.60
          }
        }
      ]
    }
  ],
  "scrapedAt": "2026-04-28T22:30:00.000Z"
}
```

***

### Who uses this

| Role | Use case |
|---|---|
| Sharp bettor | Track closing line value across books. Pick the best number per game. |
| Syndicate trader | Run middle and arb strategies across DraftKings, Pinnacle, FanDuel, BetMGM. |
| DFS / fantasy analyst | Convert moneylines into implied win probabilities for projections. |
| Sportsbook operator | Monitor competitor lines and react to market moves on your own book. |
| BI / data analyst | Pipe daily odds into Snowflake or BigQuery. One row per event, books indexed. |
| Content team | Power matchup pages, previews, and odds widgets with structured data. |
| AI builder | Train EV models, pick generators, or chatbots on a clean cross book dataset. |
| Picks site / handicapper | Source the prices behind your picks without a paid odds vendor. |

***

### Input reference

| Field | Type | What it does |
|---|---|---|
| `sports` | string\[] | Pick leagues (NFL, NBA, MLB, NHL, UFC, soccer EPL/LaLiga/Bundesliga/Serie A/Ligue 1/MLS/UCL/UEL, tennis ATP/WTA, golf PGA, F1, esports). |
| `eventUrls` | string\[] | Direct event page URLs from any sportsbook. Mix and match. |
| `books` | string\[] | draftkings, pinnacle, fanduel, betmgm, caesars, bet365. |
| `markets` | string\[] | h2h, spreads, totals, player\_props, team\_totals, first\_half. |
| `oddsFormat` | enum | american, decimal, fractional, implied\_probability. |
| `computeBestPrice` | boolean | Attach bestPrice per outcome with edge vs average. |
| `computeArbitrage` | boolean | Flag two way markets with guaranteed profit across books. |
| `minArbPct` | number | Drop arb opportunities below this percent. |
| `minBestEdgePct` | number | Filter best price flags to only those that beat market average by N%. |
| `totalMaxEvents` | integer | Hard cap on rows pushed per run. 0 means unlimited. |
| `maxEventsPerSport` | integer | Per league cap. |
| `includeStartedEvents` | boolean | Include in play events. Off keeps to upcoming only. |
| `lookAheadHours` | integer | Only events starting within N hours from now. 0 means no time limit. |
| `dedupe` | boolean | Skip event keys from previous runs. |
| `concurrency` | integer | Parallel book + sport fetches. |
| `proxyConfiguration` | object | Apify proxy. Pinnacle works on datacenter or unproxied. DraftKings is geo gated and Akamai protected, so US residential is required. |

***

### API call

```bash
curl -X POST \
  "https://api.apify.com/v2/acts/YOUR_USER~sports-odds-scraper/runs?token=YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "sports": ["nba", "nhl"],
    "books": ["draftkings", "pinnacle"],
    "markets": ["h2h", "spreads", "totals"],
    "computeBestPrice": true,
    "computeArbitrage": true,
    "lookAheadHours": 24,
    "totalMaxEvents": 30
  }'
```

***

### Pricing

The first few events per run are free so you can validate output before paying. After that, one charge per event row regardless of how many books or markets ship inside it. Best price compute and arbitrage detection are included at no extra cost.

***

### FAQ

#### What is the difference between this and The Odds API?

The Odds API is a paid SaaS aggregator (free tier 500 requests / month, paid tiers $30 to $999 / month). This actor hits public sportsbook JSON endpoints directly so you only pay Apify per row. No third party key, no monthly cap, and you get the full per book breakdown rather than a single normalized line.

#### Why does DraftKings need a US residential proxy?

DraftKings is geo gated to US states where they operate, and protected by Akamai bot detection. Datacenter IPs and non US IPs return 403. Apify residential proxy from US states resolves both. The default proxy preset uses RESIDENTIAL.

#### How accurate is the best price field?

`bestPrice.book` always points to the highest decimal odds across whichever books returned a price for that outcome. `edgePctVsAverage` is the percentage above the simple average across all books that priced the outcome. Useful for spotting which book is currently soft on a side.

#### What does the arbitrage field mean?

For each two way market (moneyline, spread at a given point, total at a given point), the actor takes the best price on each side across all books and computes the inverse sum. If that sum is less than 1, there is a guaranteed profit. `edgePct` is `(1 - inverseSum) * 100`. Stake sizes are left to your bankroll math.

#### Does it cover player props?

Pass `markets: ["player_props"]` and DraftKings player props will be pulled. Pinnacle player props are limited to a handful of leagues. Other books cover props through the JSON-LD fallback when you provide a direct event URL.

#### What about live in play odds?

Set `includeStartedEvents: true` and the actor includes events already in progress. Live lines move fast though. For high frequency live tracking, schedule the actor to run every 60 seconds.

#### How do I track odds movement over time?

Schedule the actor to run on a cron (every 5, 10, or 60 minutes) and the dataset accumulates timestamped snapshots per event. Each row carries `scrapedAt`. Diff between two runs to see line movement. The related Sports Odds Movement Tracker actor is purpose built for this.

#### Can I detect closing line value?

Yes. Run the actor at your bet placement time and again 5 minutes before kickoff. Compare your booked price to the closing decimal. CLV equals `(yourDecimal / closingDecimal) - 1`.

#### Does it work for soccer?

Yes. Pass `sports: ["soccer_epl"]` (Premier League), `soccer_laliga`, `soccer_bundesliga`, `soccer_seriea`, `soccer_ligue1`, `soccer_mls`, `soccer_uefa_cl` (Champions League), or `soccer_uefa_el` (Europa League). Pinnacle is especially deep on soccer markets.

#### Is sports odds scraping legal?

This actor reads JSON any anonymous web visitor of the sportsbook website can see. Respect each sportsbook's terms and rate limit sensibly. Do not redistribute their odds data in a commercial product without checking their TOS and licensing requirements where you operate.

***

### Related actors

- **Sports Odds Movement Tracker**. Same odds shape, optimized for cron schedules with timestamped snapshots and odds movement diffs out of the box.
- **Polymarket Market Monitor**. Same shape applied to crypto prediction markets. Useful as a sanity check against sportsbook lines.
- **Sports Odds Scraper Pro**. Pipeline version that joins live odds with team and player metadata in one row.
- **TripAdvisor Property Rank Tracker**. Daily rank tracking applied to hospitality.
- **Crypto Whale Token Launch Tracker**. Same temporal shape applied to onchain trades.
- **SEC 8-K Event Tracker**. Same temporal shape applied to corporate events.

# Actor input Schema

## `sports` (type: `array`):

Pick which leagues to scrape. Each pulls every upcoming event with currently posted lines from the selected sportsbooks.

## `eventUrls` (type: `array`):

Direct event page URLs from any supported sportsbook (DraftKings, Pinnacle, FanDuel, BetMGM, Caesars, Bet365). Mix and match. Unknown books fall back to JSON-LD extraction.

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

Which sportsbooks to query when sports + leagues are selected. Empty means all supported books.

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

Which betting markets to pull per event.

## `oddsFormat` (type: `string`):

How to express prices in the row.

## `computeBestPrice` (type: `boolean`):

When at least two books are queried, attach a bestPrice field per outcome with the highest odds and which book offers it.

## `computeArbitrage` (type: `boolean`):

Flag two way and three way markets where the best prices across books guarantee a profit. Adds an arbitrage object per market when found.

## `minArbPct` (type: `number`):

Drop arbitrage opportunities below this edge. 0.5 means at least 0.5% guaranteed return.

## `minBestEdgePct` (type: `number`):

Filter best price flags to only those that beat the average price across books by this percent.

## `totalMaxEvents` (type: `integer`):

Hard cap on event rows pushed per run. 0 means unlimited.

## `maxEventsPerSport` (type: `integer`):

Per league cap. Useful when scraping multiple leagues in one run.

## `includeStartedEvents` (type: `boolean`):

Include events already in progress. Off keeps the feed to upcoming events only.

## `lookAheadHours` (type: `integer`):

Only return events starting within this many hours from now. 0 means no time limit.

## `dedupe` (type: `boolean`):

Skip event IDs already pushed in previous runs (keyed by sport + home + away + commenceTime). Turn off to refresh stale rows.

## `concurrency` (type: `integer`):

Number of book + sport combinations fetched in parallel.

## `proxyConfiguration` (type: `object`):

Apify proxy. Pinnacle public API works on datacenter or unproxied. DraftKings is geo gated and Akamai protected, so US residential is required. FanDuel, BetMGM, Bet365 also need residential.

## Actor input object example

```json
{
  "sports": [],
  "eventUrls": [],
  "books": [
    "draftkings",
    "pinnacle"
  ],
  "markets": [
    "h2h",
    "spreads",
    "totals"
  ],
  "oddsFormat": "american",
  "computeBestPrice": true,
  "computeArbitrage": false,
  "minArbPct": 0,
  "minBestEdgePct": 0,
  "totalMaxEvents": 50,
  "maxEventsPerSport": 100,
  "includeStartedEvents": false,
  "lookAheadHours": 0,
  "dedupe": true,
  "concurrency": 4,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}
```

# 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 = {
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ]
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("scrapemint/sports-odds-scraper").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 = { "proxyConfiguration": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
    } }

# Run the Actor and wait for it to finish
run = client.actor("scrapemint/sports-odds-scraper").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 '{
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}' |
apify call scrapemint/sports-odds-scraper --silent --output-dataset

```

## MCP server setup

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

```

## OpenAPI specification

```json
{
    "openapi": "3.0.1",
    "info": {
        "title": "Sports Odds Scraper",
        "description": "Scrape live sports betting odds across DraftKings, Pinnacle, FanDuel, BetMGM, Caesars, Bet365 and more. H2H, spreads, totals, props. Normalized schema, best price per outcome, arbitrage detection. No third party API key. Pay per row.",
        "version": "0.1",
        "x-build-id": "g3msbE9anJxk4FFpY"
    },
    "servers": [
        {
            "url": "https://api.apify.com/v2"
        }
    ],
    "paths": {
        "/acts/scrapemint~sports-odds-scraper/run-sync-get-dataset-items": {
            "post": {
                "operationId": "run-sync-get-dataset-items-scrapemint-sports-odds-scraper",
                "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/scrapemint~sports-odds-scraper/runs": {
            "post": {
                "operationId": "runs-sync-scrapemint-sports-odds-scraper",
                "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/scrapemint~sports-odds-scraper/run-sync": {
            "post": {
                "operationId": "run-sync-scrapemint-sports-odds-scraper",
                "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",
                "properties": {
                    "sports": {
                        "title": "Sports + leagues",
                        "type": "array",
                        "description": "Pick which leagues to scrape. Each pulls every upcoming event with currently posted lines from the selected sportsbooks.",
                        "items": {
                            "type": "string",
                            "enum": [
                                "nfl",
                                "ncaaf",
                                "nba",
                                "wnba",
                                "ncaab",
                                "mlb",
                                "nhl",
                                "ufc",
                                "boxing",
                                "soccer_epl",
                                "soccer_laliga",
                                "soccer_bundesliga",
                                "soccer_seriea",
                                "soccer_ligue1",
                                "soccer_mls",
                                "soccer_uefa_cl",
                                "soccer_uefa_el",
                                "tennis_atp",
                                "tennis_wta",
                                "golf_pga",
                                "f1",
                                "esports_csgo",
                                "esports_lol",
                                "esports_dota2"
                            ],
                            "enumTitles": [
                                "NFL",
                                "NCAA Football",
                                "NBA",
                                "WNBA",
                                "NCAA Basketball",
                                "MLB",
                                "NHL",
                                "UFC / MMA",
                                "Boxing",
                                "Soccer - Premier League",
                                "Soccer - La Liga",
                                "Soccer - Bundesliga",
                                "Soccer - Serie A",
                                "Soccer - Ligue 1",
                                "Soccer - MLS",
                                "Soccer - UEFA Champions League",
                                "Soccer - UEFA Europa League",
                                "Tennis - ATP",
                                "Tennis - WTA",
                                "Golf - PGA",
                                "Formula 1",
                                "Esports - CS:GO",
                                "Esports - League of Legends",
                                "Esports - Dota 2"
                            ]
                        },
                        "default": []
                    },
                    "eventUrls": {
                        "title": "Event URLs",
                        "type": "array",
                        "description": "Direct event page URLs from any supported sportsbook (DraftKings, Pinnacle, FanDuel, BetMGM, Caesars, Bet365). Mix and match. Unknown books fall back to JSON-LD extraction.",
                        "default": [],
                        "items": {
                            "type": "string"
                        }
                    },
                    "books": {
                        "title": "Sportsbooks to include",
                        "type": "array",
                        "description": "Which sportsbooks to query when sports + leagues are selected. Empty means all supported books.",
                        "items": {
                            "type": "string",
                            "enum": [
                                "draftkings",
                                "pinnacle",
                                "fanduel",
                                "betmgm",
                                "caesars",
                                "bet365"
                            ],
                            "enumTitles": [
                                "DraftKings (US)",
                                "Pinnacle (Global, sharpest lines)",
                                "FanDuel (US)",
                                "BetMGM (US)",
                                "Caesars (US)",
                                "Bet365 (Global)"
                            ]
                        },
                        "default": [
                            "draftkings",
                            "pinnacle"
                        ]
                    },
                    "markets": {
                        "title": "Markets",
                        "type": "array",
                        "description": "Which betting markets to pull per event.",
                        "items": {
                            "type": "string",
                            "enum": [
                                "h2h",
                                "spreads",
                                "totals",
                                "player_props",
                                "team_totals",
                                "first_half"
                            ],
                            "enumTitles": [
                                "Moneyline (h2h)",
                                "Spreads / point spread",
                                "Totals (over / under)",
                                "Player props",
                                "Team totals",
                                "First half"
                            ]
                        },
                        "default": [
                            "h2h",
                            "spreads",
                            "totals"
                        ]
                    },
                    "oddsFormat": {
                        "title": "Odds format",
                        "enum": [
                            "american",
                            "decimal",
                            "fractional",
                            "implied_probability"
                        ],
                        "type": "string",
                        "description": "How to express prices in the row.",
                        "default": "american"
                    },
                    "computeBestPrice": {
                        "title": "Compute best price per outcome",
                        "type": "boolean",
                        "description": "When at least two books are queried, attach a bestPrice field per outcome with the highest odds and which book offers it.",
                        "default": true
                    },
                    "computeArbitrage": {
                        "title": "Compute arbitrage edges",
                        "type": "boolean",
                        "description": "Flag two way and three way markets where the best prices across books guarantee a profit. Adds an arbitrage object per market when found.",
                        "default": false
                    },
                    "minArbPct": {
                        "title": "Minimum arbitrage edge (%)",
                        "minimum": 0,
                        "maximum": 50,
                        "type": "number",
                        "description": "Drop arbitrage opportunities below this edge. 0.5 means at least 0.5% guaranteed return.",
                        "default": 0
                    },
                    "minBestEdgePct": {
                        "title": "Minimum best price edge vs market average (%)",
                        "minimum": 0,
                        "maximum": 100,
                        "type": "number",
                        "description": "Filter best price flags to only those that beat the average price across books by this percent.",
                        "default": 0
                    },
                    "totalMaxEvents": {
                        "title": "Total maximum events",
                        "minimum": 0,
                        "maximum": 5000,
                        "type": "integer",
                        "description": "Hard cap on event rows pushed per run. 0 means unlimited.",
                        "default": 50
                    },
                    "maxEventsPerSport": {
                        "title": "Max events per sport",
                        "minimum": 1,
                        "maximum": 5000,
                        "type": "integer",
                        "description": "Per league cap. Useful when scraping multiple leagues in one run.",
                        "default": 100
                    },
                    "includeStartedEvents": {
                        "title": "Include in play events",
                        "type": "boolean",
                        "description": "Include events already in progress. Off keeps the feed to upcoming events only.",
                        "default": false
                    },
                    "lookAheadHours": {
                        "title": "Look ahead window (hours)",
                        "minimum": 0,
                        "maximum": 720,
                        "type": "integer",
                        "description": "Only return events starting within this many hours from now. 0 means no time limit.",
                        "default": 0
                    },
                    "dedupe": {
                        "title": "Deduplicate across runs",
                        "type": "boolean",
                        "description": "Skip event IDs already pushed in previous runs (keyed by sport + home + away + commenceTime). Turn off to refresh stale rows.",
                        "default": true
                    },
                    "concurrency": {
                        "title": "Concurrency",
                        "minimum": 1,
                        "maximum": 16,
                        "type": "integer",
                        "description": "Number of book + sport combinations fetched in parallel.",
                        "default": 4
                    },
                    "proxyConfiguration": {
                        "title": "Proxy configuration",
                        "type": "object",
                        "description": "Apify proxy. Pinnacle public API works on datacenter or unproxied. DraftKings is geo gated and Akamai protected, so US residential is required. FanDuel, BetMGM, Bet365 also need residential.",
                        "default": {
                            "useApifyProxy": true,
                            "apifyProxyGroups": [
                                "RESIDENTIAL"
                            ]
                        }
                    }
                }
            },
            "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
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
```
