Underdog Fantasy Scraper — Player Props, Lines & Multipliers
Pricing
from $0.15 / 1,000 results
Underdog Fantasy Scraper — Player Props, Lines & Multipliers
Underdog Fantasy scraper: export the live Pick'em board — every player prop, one row per line with Higher/Lower payout multipliers, American and decimal prices, implied probability, player, team, matchup and start time; alternate ladders optional. NFL, CFB, NBA, NHL, soccer, tennis, esports.
Pricing
from $0.15 / 1,000 results
Rating
0.0
(0)
Developer
Logiover
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
16 hours ago
Last modified
Categories
Share
Underdog Fantasy Scraper — Player Props, Lines, Alternate Lines & Payout Multipliers
Export the live Underdog Fantasy Pick'em board — every player prop with the payout multipliers, American and decimal prices and implied probabilities of its Higher and Lower side, player, team, matchup, start time and league, and on request the full ladder of alternate lines — as clean JSON, CSV or Excel. One row per line, both sides in the row. No login, no API key, no browser.
Independent tool. Not affiliated with, endorsed by, or sponsored by Underdog, Underdog Sports or any league, team or player named in the data. "Underdog" and "Underdog Fantasy" are trademarks of their owners and are used here only to describe the public website this Actor reads. This Actor only reads the lines Underdog serves to every anonymous visitor of its web app: it does not log in, does not use an account, does not place or automate entries, and does not offer betting advice.
Underdog Fantasy Scraper is an Underdog Fantasy API alternative and Underdog Pick'em scraper that turns the public board behind underdogfantasy.com and app.underdogsports.com into a flat table you can sort, filter, join and chart. Every row is one line — "Points 24.5" — with the payout multipliers, American, decimal and fractional odds and implied probabilities of both its Higher (over) and Lower (under) side, the player (name, team, position, jersey number, country, headshot), the matchup (home and away team, venue, season type, broadcast), the start time in ISO 8601 UTC, the league (NFL, college football, NBA, NHL, WNBA, soccer, tennis, MMA, golf, NASCAR, Counter-Strike, League of Legends, Dota 2, Valorant …) and the stat (points, rebounds, passing yards, shots on goal, kills on maps 1+2, round strokes …). By default you get the main line of every prop; switch includeAlternates on and it also reads the alternate-line ladder of every prop that has one — the 19.5, 23.5, 29.5, 34.5 and 39.5 next to a 24.5 — each with its own multipliers and prices.
Use it as an Underdog player props API, an Underdog odds scraper, a pick'em line-shopping feed next to PrizePicks or a sportsbook, a props database for models and backtests, a line-movement and closing-line-value tracker when you schedule it, or a Discord / Telegram alert source. Export CSV, Excel, JSON or XML; call it from the Apify API, Make, Zapier, n8n, Google Sheets or an AI agent. Field names follow our PrizePicks and Sleeper Scrapers (playerName, playerTeam, playerPosition, league, statType, lineScore, startTime, projectionId, matchup, overMultiplier / underMultiplier), so the datasets join on player, stat and line. Prefer one row per side? Set rowShape to side.
Table of contents
- At a glance
- What you get
- Who uses it — 14 workflows
- Cookbook — 15 ready-to-run inputs
- Tutorial: your first run in the Apify Console
- Tutorial: run it from the API
- Input reference
- Output data dictionary
- Real output sample
- How to read a row
- Scale and speed (measured)
- What you pay
- Integrations
- Performance tips
- Troubleshooting: every status message explained
- Honest limits
- How it compares
- FAQ
- Glossary
- Is it legal to scrape Underdog?
- Responsible use
- Changelog
- Support
At a glance
| Source | The JSON Underdog's own web app loads: the Pick'em board (/v1/over_under_lines) and the alternate-line ladder of each prop (/v3/over_unders/<id>/alternate_projections). No browser, no login. |
| Row | One line with both sides in it (over* = Higher, under* = Lower) — 73 columns, flat, typed, every field nullable. rowShape: "side" gives one row per side instead. |
| Board on 2026-10-29 | 9,744 main lines on 15 leagues (13,749 sides); 3,794 props carry an alternate-line ladder. |
| Default run (main lines) | 9,740 rows in 10 seconds; $2.44 at the free tier, $1.46 on Gold and above. |
| With alternate lines | Opt-in (includeAlternates: true). Biggest verified run: 24,995 unique line rows (9,744 main + 15,251 alternate lines, 35,473 sides), 0 duplicates, 6.6 minutes, 2026-10-29. |
| Prefill run | 300 rows in 9 seconds, first rows after 8 seconds. |
| Odds formats | American, decimal, fractional, implied and no-vig probability and payout multiplier — both sides, every row. |
| Price | Pay per result — from $0.25 per 1,000 rows, down to $0.15 per 1,000 on higher Store tiers. No start fee, no monthly rent. |
What you get
Every row carries the context of its line, so one row is a complete selection. The columns come in eight groups; the full data dictionary with measured fill rates is further down.
- Identity —
rowId(underdog:line:<projectionId>),recordType,platform,lineKey(the market:<league>:<gameId>:<playerId>:<statKey>),projectionId(the line),overUnderId(Underdog's prop id),appearanceId,playerId,gameId,matchType. - Player —
playerName,playerTeam,playerTeamName,playerPosition,playerPositionName,jerseyNumber,playerCountry,playerImageUrl,isHome. - Event —
league,sportName,competition,gameTitle,matchup(AWAY @ HOME),opponent,homeTeam/awayTeamand their full names,homePlayerName/awayPlayerName,startTime,gameStatus,seasonType,venueName,venueType,broadcasts. - Market and line —
statType,statKey,propTitle,lineScore(the number to beat),mainLineScore,isAlternate,hasAlternates,lineProvider,sidesOffered. - Both sides — for
over(Higher) andunder(Lower):…LineId,…Multiplier,…PayoutTier,…OddsAmerican,…OddsDecimal,…OddsFractional,…ImpliedProbability,…NoVigProbability, the separate…Fantasy…price set and…Status, plusmarketOverround. - Meta —
updatedAt(when Underdog last changed the line) andscrapedAt. - Side shape only — with
rowShape: "side"each side is its own row and carriesside,lineId,oppositeLineId,multiplier,payoutTier,oddsAmerican,impliedProbability… (listed last in the data dictionary).
A real line from a run on 2026-10-29 is shown unmasked in Real output sample below.
Who uses it — 14 workflows
1. Pick'em players and line shoppers
Sort tonight's board by statType and lineScore, compare Underdog's number with the same player elsewhere, and use overPayoutTier / underPayoutTier to find boosted sides. With includeAlternates on, the ladder shows what the 19.5 and the 29.5 pay before you commit to the 24.5.
2. Sports bettors comparing sportsbook props with pick'em lines
Join the export with the props you already collect on playerName + statType + lineScore and compare the implied and no-vig probabilities, computed the same way on every row.
3. DFS content creators and newsletters
A daily "best boosted picks" post is a filter: payoutTiers: ["boosted"], minPayoutMultiplier: 1.15, startsWithinHours: 24. Schedule it, push the dataset to a sheet; team names and headshot URLs are in the row.
4. Sports analytics teams and modellers
Schedule the Actor, keep every run, and key rows by rowId (a line) and lineKey (a prop): line movement, multiplier drift and the moment a side turns suspended become time series.
5. Odds comparison sites and aggregators
One row per line with both sides' American, decimal and fractional odds maps straight onto an odds-table schema; league + startTime + matchup place the event on your fixture list.
6. Discord, Telegram and Slack alert bots
Run every few minutes with a narrow filter, diff against the previous run by rowId, and post when lineScore or a multiplier changes. A webhook on run success triggers the bot.
7. Sports-data journalists
When a star is ruled out the boards react within minutes: pull a player's rows before and after and quote the movement with updatedAt.
8. Fantasy-sports and prediction start-ups
Seed a product with real market lines: projection baselines, difficulty ratings or a "what the market expects" widget. The main lineScore is Underdog's market estimate; with alternates on, the ladder maps the distribution around it.
9. Quantitative researchers
With includeAlternates on, the ladder is a discrete price curve: multiplier and price at 19.5, 23.5, 24.5, 29.5, 34.5, 39.5 for one stat. Fit a curve per prop or look for ladders whose neighbouring thresholds are mispriced.
10. Esports analysts
Counter-Strike, League of Legends, Dota 2 and Valorant props (kills on maps 1+2, assists, deaths) share the board and the columns; a team's whole slate is one teams filter.
11. Tennis, MMA, golf and NASCAR analysts
Individual-sport props (games played, aces, significant strikes, round strokes) come with the event in gameTitle, the tournament in competition, and opponent / homePlayerName / awayPlayerName for one-on-one events.
12. Academic and market-efficiency researchers
A scheduled collection of a small market over a season is a dataset few papers have; flat, typed rows with a stable rowId suit studies of line efficiency and favourite–longshot bias.
13. Risk, pricing and competitive-intelligence teams
See how a competitor prices the pairs you offer: group by league and statType, read the margin from marketOverround, measure how often lines move.
14. AI agents, RAG and LLM pipelines
A small JSON input, a flat output and a SUMMARY record make a clean agent tool: "which boosted sides start in the next three hours?" is one call with payoutTiers and startsWithinHours.
Cookbook — 15 ready-to-run inputs
Every input below is valid as written. Leave a field out to leave that filter off. Rows are returned soonest match first, so a small maxItems always gives you "what is coming up next".
1. A quick look (what the Console pre-fills)
{ "maxItems": 300 }
The soonest 300 rows of the whole board, all leagues: one request for the board, finished in seconds — the input the Store's daily health check uses.
2. The whole live board — main lines (the default)
{}
Every league that has lines right now, the main line of every prop: 9,740 rows in about 10 seconds on 2026-10-29. Raise "Maximum cost per run" if you want to be sure of the ceiling.
3. Alternate lines too — the deep ladders
{ "includeAlternates": true }
One more request per prop: 24,995 rows on 2026-10-29 (15,251 of them alternates), about 6.6 minutes. Narrow it with filters to make it quick and cheap.
4. One league: NFL player props
{ "leagues": ["NFL"] }
League codes are not case-sensitive; a few names work as aliases (soccer → FIFA, golf → PGA, valorant → VAL). With one to eight leagues the Actor downloads just those leagues.
5. Tonight's NBA board: points, rebounds, assists
{ "leagues": ["NBA"], "statTypes": ["Points", "Rebounds", "Assists"], "startsWithinHours": 24 }
statTypes matches the stat name, key or prop title.
6. One player, everything Underdog offers
{ "playerNames": ["Jokic"] }
Case-insensitive, accents ignored, partial names allowed — Jokic finds Jokić. Use several names for a watch-list.
7. One team's whole slate
{ "leagues": ["NFL"], "teams": ["KC", "Chiefs"] }
teams matches the player's team and either side of the matchup, by abbreviation or by name.
8. Boosted sides only
{ "payoutTiers": ["boosted"], "minPayoutMultiplier": 1.2, "startsWithinHours": 48, "includeAlternates": true }
Lines where a side pays more than a standard pick, starting within two days; the row shows both sides. Alternate lines are where the large multipliers live, so this one switches them on.
9. Lower (under) sides that are discounted
{ "side": "lower", "payoutTiers": ["discounted"] }
Lines whose Lower side pays less than a standard pick (multiplier below 1.0).
10. Soccer: shots on target
{ "leagues": ["FIFA"], "statTypes": ["Shots On Target"] }
Soccer is the FIFA league code on Underdog; home and away are resolved for soccer matchups.
11. Esports
{ "leagues": ["CS", "LOL", "DOTA", "VAL"] }
Counter-Strike, League of Legends, Dota 2 and Valorant. playerTeam holds the team name.
12. Individual sports: tennis, MMA, golf
{ "leagues": ["TENNIS", "MMA", "PGA"] }
No team here: competition holds the tournament, homePlayerName / awayPlayerName the two sides of one-on-one events.
13. A single day
{ "startDateFrom": "2026-10-31", "startDateTo": "2026-10-31" }
Both are inclusive UTC days (YYYY-MM-DD); a match at 01:00 UTC on the 1st is the evening of the 31st in the US.
14. Scheduled line-movement tracking
{ "leagues": ["NBA"], "statTypes": ["Points"], "maxItems": 5000 }
Schedule it every 15 minutes (Console → Schedules), keep every run's dataset and compare runs by rowId: the same line has the same rowId each run, so lineScore, overMultiplier and overOddsAmerican line up row by row; rows in one run only are new or removed lines.
15. One row per side
{ "rowShape": "side", "leagues": ["NBA"], "maxItems": 2000 }
Higher and Lower as two rows (side, lineId, oppositeLineId, multiplier, oddsAmerican, impliedProbability …) — twice the rows for the same lines, for tools that expect one selection per row.
Tutorial: your first run in the Apify Console
- Open the Actor and press Try for free or Start. The input form is pre-filled with
maxItems = 300and every filter empty. - Leave the leagues empty to read every league that has lines right now, or pick leagues from the suggestions (you can also type any league code Underdog uses).
- Optionally narrow the board: player names, teams, stat types, side, payout tiers, a multiplier range or a start window. Row shape chooses one row per line (default) or per side.
- Switch Include alternate lines on if you want the whole ladder (more rows, minutes instead of seconds); it is off by default.
- Press Start; the pre-filled input ends in seconds.
- Open the Output tab. Views: Board, Prices, Players, Matches and Ladders (the lines of one prop next to each other). Export as CSV, Excel, JSON or JSONL.
- Storage → Key-value store → SUMMARY has the outcome, the leagues on the board, the number of alternate-line requests and the routes used.
Tutorial: run it from the API
1. Get your token
Console → Settings → API & Integrations → copy the personal API token. Put it in an environment variable instead of in code.
2. Run and get the data in one call (small runs)
curl -X POST "https://api.apify.com/v2/acts/logiover~underdog-fantasy-scraper/run-sync-get-dataset-items?token=$APIFY_TOKEN" \-H "Content-Type: application/json" \-d '{"leagues":["NBA"],"statTypes":["Points"],"maxItems":200}'
The response is the dataset items as JSON. Add &format=csv or &format=xlsx for a file. Synchronous calls end after five minutes: use the next pattern for big boards.
3. Start a run and fetch the data later (big runs)
# startcurl -X POST "https://api.apify.com/v2/acts/logiover~underdog-fantasy-scraper/runs?token=$APIFY_TOKEN" \-H "Content-Type: application/json" -d '{"maxItems":100000}'# poll: GET https://api.apify.com/v2/actor-runs/RUN_ID?token=$APIFY_TOKEN (status SUCCEEDED)# data: GET https://api.apify.com/v2/datasets/DATASET_ID/items?format=csv&token=$APIFY_TOKEN
4. Node.js (apify-client)
import { ApifyClient } from 'apify-client';const client = new ApifyClient({ token: process.env.APIFY_TOKEN });const run = await client.actor('logiover/underdog-fantasy-scraper').call({leagues: ['NFL'], payoutTiers: ['boosted'], startsWithinHours: 72, maxItems: 2000,});const { items } = await client.dataset(run.defaultDatasetId).listItems();for (const r of items.slice(0, 5)) console.log(r.playerName, r.statType, r.lineScore, `over x${r.overMultiplier}`, `under x${r.underMultiplier}`);
5. Python (apify-client)
import osfrom apify_client import ApifyClientclient = ApifyClient(os.environ["APIFY_TOKEN"])run = client.actor("logiover/underdog-fantasy-scraper").call(run_input={"leagues": ["NBA"], "statTypes": ["Points"], "startsWithinHours": 24})rows = client.dataset(run["defaultDatasetId"]).list_items().itemsfor r in sorted((r for r in rows if r["overMultiplier"]), key=lambda r: -r["overMultiplier"])[:10]:print(r["playerName"], r["statType"], r["lineScore"], r["overMultiplier"], r["underMultiplier"])
6. Read the run summary
summary = client.key_value_store(run["defaultKeyValueStoreId"]).get_record("SUMMARY")["value"]print(summary["outcome"], summary["rowsSaved"], summary["board"]["leagues"].keys())
outcome is COMPLETED, PARTIAL, EMPTY_RESULT, BLOCKED, FETCH_FAILED or INVALID_INPUT (see Troubleshooting).
Input reference
Everything is optional. Filters combine with AND.
| Field | Type | Default | What it does |
|---|---|---|---|
leagues | list of text | all live leagues | Underdog league codes: NFL, CFB, NBA, NHL, MLB, WNBA, CBB, FIFA (soccer), TENNIS, MMA, PGA, MOTORSPORTS, CS, LOL, DOTA, VAL, CFL, KBO …; not case-sensitive. A league with no lines returns nothing. |
playerNames | list of text | none | Keep only these players. Partial, case-insensitive, accent-insensitive. |
teams | list of text | none | Keep only props of these teams or matches of these teams (abbreviation or name). |
statTypes | list of text | none | Keep only these stats (name, stat key or prop title; case-insensitive substring). |
rowShape | line / side | line | One row per line with both sides (default) or one row per side. |
side | both / higher / lower | both | Keep lines whose Higher or Lower side passes the payout filters (and, in the side shape, only that side's rows). |
payoutTiers | list of boosted / standard / discounted | none | Keep lines where a side is in these tiers. |
minPayoutMultiplier | number | none | Keep lines where a side pays at least this multiplier. |
maxPayoutMultiplier | number | none | Keep lines where a side pays at most this multiplier. |
includeAlternates | true / false | false | Also read the alternate-line ladder of every prop that has one: one more request per prop, about 2.5 times the rows. Off = main lines only (one request, seconds). |
startsWithinHours | whole number | none | Keep only matches starting within this many hours from now. |
startDateFrom | text YYYY-MM-DD | none | Keep only matches starting on or after this UTC day. |
startDateTo | text YYYY-MM-DD | none | Keep only matches starting on or before this UTC day. |
maxItems | whole number | 100,000 | Upper bound on rows saved (each row is one billed result — a line, or a side in the side shape). The Console pre-fills 300. |
proxyConfiguration | proxy editor | no proxy | Optional. Without it the Actor connects directly and falls back to the Apify datacenter proxy and, for small documents, a capped residential exit. |
Filters that depend on the prop (league, player, team, stat, time window) are applied before any alternate-line request is made, so a narrow input also makes few requests. Filters that depend on the side (side, payoutTiers, multiplier range) keep a line when at least one of its sides passes all of them; in the side shape they decide which side rows are saved.
Output data dictionary
73 columns in the default shape, every one nullable. The Filled column is measured on the 24,995-row verified run of 2026-10-29 (the whole board with includeAlternates: true); a column that is empty for some rows is empty because the source has no value for them, never because the Actor dropped it. The columns of the Side shape group exist only with rowShape: "side".
Identity
| Column | Type | Filled | Meaning | Example |
|---|---|---|---|---|
rowId | text | 100 % | Unique key: underdog:line:<projectionId> (a side row: underdog:<its option id>). | underdog:line:0f900… |
recordType | text | 100 % | line (default) or side. | line |
platform | text | 100 % | Always underdog; a discriminator for merging with the other pick'em Actors. | underdog |
lineKey | text | 100 % | Stable key of the market (one prop): <league>:<gameId>:<playerId>:<statKey>; shared by the main line and its alternates. | TENNIS:329778:0ee77… |
projectionId | text | 100 % | Underdog's id of the line (one threshold of a prop); same name as in the PrizePicks Actor. | 0f900c6e-2edc-4c06-… |
overUnderId | text | 100 % | Underdog's own prop id (player + stat + match). | 15b9456b-f660-48c3-… |
appearanceId | text | 100 % | Underdog's id of the player's appearance in the match. | 672c2c7e-7a53-40e4-… |
playerId | text | 100 % | Underdog's player id. | 0ee77440-ce67-48a6-… |
gameId | text | 100 % | Underdog's match id (team game or individual event). | 329778 |
matchType | text | 100 % | Game (team match) or SoloGame (golf, tennis, MMA, NASCAR). | SoloGame |
Player
| Column | Type | Filled | Meaning | Example |
|---|---|---|---|---|
playerName | text | 100 % | Player name as Underdog shows it. | Kaitlin Quevedo |
playerTeam | text | 97.7 % | Team abbreviation (CLE); team name for esports; null for individual sports and unresolved matchups. | BIG |
playerTeamName | text | 97.7 % | Full team name when Underdog gives one, else the nickname. | BIG |
playerPosition | text | 100 % | Position code (SG, QB, FW, G, Gamer, TP). | TP |
playerPositionName | text | 100 % | Position written out (Shooting Guard, Tight End). | Tennis Player |
jerseyNumber | text | 94.7 % | Jersey number when Underdog lists one. | 15 |
playerCountry | text | 5.7 % | ISO country code when Underdog lists one (golf, tennis, soccer, MMA). | ESP |
playerImageUrl | text | 100 % | Headshot URL on Underdog's asset host. | https://assets.unde… |
isHome | true / false | 99.7 % | true when the player's side is the home side (tennis / MMA: the slot Underdog calls home). | false |
Event
| Column | Type | Filled | Meaning | Example |
|---|---|---|---|---|
league | text | 100 % | League code as Underdog uses it (NFL, CFB, NHL, FIFA = soccer, TENNIS, MMA, CS, LOL …); use it in leagues. | TENNIS |
sportName | text | 100 % | Sport / game name from the match record (Soccer, CS2, Dota 2). | Tennis |
competition | text | 6.1 % | Tournament or competition when there is one (EPL, ATP Vienna). | WTA Guangzhou |
gameTitle | text | 100 % | Match title as Underdog prints it (CLE @ BOS, MCI vs BHA). | Bondar vs Quevedo |
matchup | text | 100 % | AWAY @ HOME (the PrizePicks Actor's opponent shape); A vs. B for one-on-one events. | A. Bondar vs. K. Qu… |
opponent | text | 99.5 % | The player's opponent: a team (name for esports) or, in tennis and MMA, the other competitor (as in the Sleeper Actor). | Anna Bondar |
homeTeam | text | 97.7 % | Home team. AWAY @ HOME titles are exact; vs titles are resolved for soccer and esports only. | BIG |
awayTeam | text | 97.7 % | Away team, same rules as homeTeam. | Eternal Fire |
homeTeamName | text | 97.7 % | Home team's full name when the match record has one. | BIG |
awayTeamName | text | 97.7 % | Away team's full name when the match record has one. | Eternal Fire |
homePlayerName | text | 1.9 % | Home-side competitor in one-on-one events. | Anna Bondar |
awayPlayerName | text | 1.8 % | Away-side competitor in one-on-one events. | Kaitlin Quevedo |
startTime | text | 100 % | Scheduled start, ISO 8601 UTC. | 2026-10-29T10:10:00… |
gameStatus | text | 100 % | scheduled, or scoring in the last minutes before the start. Started matches are not on the board. | scheduled |
seasonType | text | 97.7 % | regular or post, for league matches. | regular |
venueName | text | 82.5 % | Venue, when the match record lists one. | Center Court |
venueType | text | 76.9 % | outdoor, indoor … when listed (mostly NFL / college football). | outdoor |
weatherCondition | text | 0 % (conditional) | Forecast condition. Conditional: empty on every match when measured, so the key is written only when it has a value. | Sunny |
weatherTemperature | number | 0 % (conditional) | Forecast temperature. Conditional, same rule as weatherCondition. | 61 |
broadcasts | list | 94.1 % | TV / streaming channels Underdog lists for the match. | ["Peacock", "USA"] |
Market
| Column | Type | Filled | Meaning | Example |
|---|---|---|---|---|
statType | text | 100 % | The stat as Underdog displays it (Points, Pass Yards, Kills on Maps 1+2). | 1st Set Aces |
statKey | text | 100 % | Underdog's machine key of the stat (points, rush_rec_tds); tells apart stats that share a display name. | period_1_aces |
propTitle | text | 100 % | Prop title as printed on the card. | Kaitlin Quevedo 1st… |
Line
| Column | Type | Filled | Meaning | Example |
|---|---|---|---|---|
lineScore | number | 100 % | The threshold of this line — the number to beat; same name as in the PrizePicks Actor. | 1.5 |
mainLineScore | number | 100 % | The prop's main line; equals lineScore on main-line rows. | 1.5 |
isAlternate | true / false | 100 % | false = main-board line, true = alternate line from the prop's ladder. | false |
hasAlternates | true / false | 100 % | true when Underdog offers a ladder of alternate lines for the prop. | false |
nonDiscountedLineScore | number | 0 % (conditional) | Original line when Underdog discounted the line. Conditional: empty everywhere when measured, so written only when it has a value. | 25.5 |
lineProvider | text | 34.8 % | Who set the main line: swish (model feed) or manual; not given for alternates. | swish |
sidesOffered | number | 100 % | Sides Underdog offers on this line: 2, or 1 (many lines are Higher-only). | 2 |
Sides
| Column | Type | Filled | Meaning | Example |
|---|---|---|---|---|
overLineId / underLineId | text | 99.8 % / 42.1 % | Option id of each side (the Sleeper Actor's column name). | 067fc5be-45e2-4c7e-… / 9bb164e2-0c61-4ff4-… |
overMultiplier / underMultiplier | number | 99.8 % / 42.1 % | Pick'em payout multiplier (1.0 standard, above pays more, below less). | 1.28 / 0.76 |
overPayoutTier / underPayoutTier | text | 99.8 % / 42.1 % | boosted (multiplier above 1.0), standard (1.0) or discounted (below). | boosted / discounted |
overOddsAmerican / underOddsAmerican | number | 99.8 % / 42.1 % | American price Underdog serves (the leg price; equals the sportsbook-set price on ladder documents). | 162 / -200 |
overOddsDecimal / underOddsDecimal | number | 99.8 % / 42.1 % | Decimal price. | 2.62 / 1.5 |
overOddsFractional / underOddsFractional | text | 99.8 % / 42.1 % | Reduced fraction (computed from the American price). | 81/50 / 1/2 |
overImpliedProbability / underImpliedProbability | number | 99.8 % / 42.1 % | 1 / decimal price, 4 decimals (computed; includes the margin). | 0.3817 / 0.6667 |
overNoVigProbability / underNoVigProbability | number | 41.9 % / 41.9 % | Share of the two implied probabilities, so the pair sums to 1 (two-sided lines). | 0.3641 / 0.6359 |
overFantasyOddsAmerican / underFantasyOddsAmerican | number | 94.2 % / 36.5 % | American price of Underdog's separate fantasy price set. | 135 / -228 |
overFantasyOddsDecimal / underFantasyOddsDecimal | number | 94.2 % / 36.5 % | Decimal price of the fantasy set. | 2.35 / 1.44 |
overFantasyProbability / underFantasyProbability | number | 94.2 % / 36.5 % | Probability Underdog attaches to the fantasy set, as a fraction (served as whole percent). | 0.39 / 0.66 |
overStatus / underStatus | text | 99.8 % / 42.1 % | active or suspended. | active / active |
marketOverround | number | 41.9 % | Implied over + implied under − 1: the margin of a two-sided line (computed). | 0.0484 |
Meta
| Column | Type | Filled | Meaning | Example |
|---|---|---|---|---|
updatedAt | text | 100 % | When Underdog last changed the line (later of its two sides), ISO 8601 UTC. | 2026-10-28T15:47:45… |
scrapedAt | text | 100 % | When the board was read, ISO 8601 UTC; same for every row of a run. | 2026-10-29T10:02:46… |
Side shape (only with rowShape: "side": each side is its own row, which carries all the line columns above plus these, with the meaning of the matching over… / under… columns for the row's own side)
side, sideLabel, lineId, oppositeLineId, multiplier, oppositeMultiplier, payoutTier, oddsAmerican, oddsDecimal, oddsFractional, impliedProbability, noVigProbability, fantasyOddsAmerican, fantasyOddsDecimal, fantasyProbability, status, pickLabel, choiceShort. lineId is the option id of THIS side and oppositeLineId that of the other side (the Sleeper Actor's convention); side is over or under; pickLabel is the full pick text (Higher 24.5 Points) and choiceShort its short form (25+).
Real output sample
Two rows from the verified run of 2026-10-29 (alternate lines on), unmasked, empty columns left out: a main line and an alternate line of the same prop.
A main line (both sides in the row)
{"rowId": "underdog:line:286e9f16-17a2-4479-b1cb-38eabc835a2a","recordType": "line","platform": "underdog","lineKey": "NBA:212020:046c78d0-6297-436a-92ab-e50ef6fb2aeb:three_points_made","projectionId": "286e9f16-17a2-4479-b1cb-38eabc835a2a","overUnderId": "87e2370e-ed7a-4867-8202-bcf7397b4333","appearanceId": "f3fecb84-1da2-42d0-840c-9fe75feaaec8","playerId": "046c78d0-6297-436a-92ab-e50ef6fb2aeb","gameId": "212020","matchType": "Game","playerName": "Aaron Wiggins","playerTeam": "ATL","playerTeamName": "Atlanta Hawks","playerPosition": "SG","playerPositionName": "Shooting Guard","jerseyNumber": "2","playerImageUrl": "https://assets.underdogfantasy.com/player-images/nba/20261008_21-d3b28775-473d-471a-8452-e913f4347f0f.png","isHome": true,"league": "NBA","sportName": "NBA","gameTitle": "CLE @ ATL","matchup": "CLE @ ATL","opponent": "CLE","homeTeam": "ATL","awayTeam": "CLE","homeTeamName": "Atlanta Hawks","awayTeamName": "Cleveland Cavaliers","startTime": "2026-10-30T00:00:00.000Z","gameStatus": "scheduled","seasonType": "regular","broadcasts": ["NBA TV", "DAZN-CLE", "Prime-ATL"],"statType": "3-Pointers Made","statKey": "three_points_made","propTitle": "Aaron Wiggins 3-Pointers Made O/U","lineScore": 1.5,"mainLineScore": 1.5,"isAlternate": false,"hasAlternates": true,"lineProvider": "swish","sidesOffered": 2,"overLineId": "a0f85185-0de2-465a-a895-064b0d5c48b1","overMultiplier": 0.79,"overPayoutTier": "discounted","overOddsAmerican": -179,"overOddsDecimal": 1.56,"overOddsFractional": "100/179","overImpliedProbability": 0.641,"overFantasyOddsAmerican": -200,"overFantasyOddsDecimal": 1.5,"overFantasyProbability": 0.63,"overStatus": "active","overNoVigProbability": 0.6109,"underLineId": "a7d3e8b1-09f3-420b-8192-ece437bac78f","underMultiplier": 1.19,"underPayoutTier": "boosted","underOddsAmerican": 145,"underOddsDecimal": 2.45,"underOddsFractional": "29/20","underImpliedProbability": 0.4082,"underFantasyOddsAmerican": 120,"underFantasyOddsDecimal": 2.2,"underFantasyProbability": 0.42,"underStatus": "active","underNoVigProbability": 0.3891,"marketOverround": 0.0492,"updatedAt": "2026-10-29T08:32:25.078Z","scrapedAt": "2026-10-29T10:02:46.600Z"}
An alternate line of the same prop (columns identical to the row above are left out)
{"rowId": "underdog:line:bd0c4862-c5a9-420f-84de-445b87a0588a","projectionId": "bd0c4862-c5a9-420f-84de-445b87a0588a","playerName": "Aaron Wiggins","statType": "3-Pointers Made","lineScore": 2.5,"isAlternate": true,"overLineId": "a03716b5-4adb-499e-997d-b796686f58f1","overMultiplier": 1.3,"overPayoutTier": "boosted","overOddsAmerican": 165,"overOddsDecimal": 2.65,"overOddsFractional": "33/20","overImpliedProbability": 0.3774,"overFantasyOddsAmerican": 138,"overFantasyOddsDecimal": 2.38,"overFantasyProbability": 0.38,"overNoVigProbability": 0.3615,"underLineId": "24079a1e-59b3-443b-8d30-4f3943a00e78","underMultiplier": 0.76,"underPayoutTier": "discounted","underOddsAmerican": -200,"underOddsDecimal": 1.5,"underOddsFractional": "1/2","underImpliedProbability": 0.6667,"underFantasyOddsAmerican": -228,"underFantasyOddsDecimal": 1.44,"underFantasyProbability": 0.66,"underNoVigProbability": 0.6385,"marketOverround": 0.0441,"updatedAt": "2026-10-29T08:29:19.805Z"}
CSV / Excel columns, in order: rowId, recordType, platform, lineKey, projectionId, overUnderId, appearanceId, playerId, gameId, matchType, playerName, playerTeam, playerTeamName, playerPosition, playerPositionName, jerseyNumber, playerCountry, playerImageUrl, isHome, league, sportName, competition, gameTitle, matchup, opponent, homeTeam, awayTeam, homeTeamName, awayTeamName, homePlayerName, awayPlayerName, startTime, gameStatus, seasonType, venueName, venueType, broadcasts, statType, statKey, propTitle, lineScore, mainLineScore, isAlternate, hasAlternates, lineProvider, sidesOffered, overLineId, overMultiplier, overPayoutTier, overOddsAmerican, overOddsDecimal, overOddsFractional, overImpliedProbability, overFantasyOddsAmerican, overFantasyOddsDecimal, overFantasyProbability, overStatus, overNoVigProbability, underLineId, underMultiplier, underPayoutTier, underOddsAmerican, underOddsDecimal, underOddsFractional, underImpliedProbability, underFantasyOddsAmerican, underFantasyOddsDecimal, underFantasyProbability, underStatus, underNoVigProbability, marketOverround, updatedAt, scrapedAt
How to read a row
One row is one line with both sides. "Points 24.5" has a Higher side (over… columns) and usually a Lower side (under… columns), each with its own multiplier and prices. projectionId identifies the line, lineKey the prop — every line of a prop, main or alternate, shares it. When Underdog offers one side only, sidesOffered is 1 and the other side's columns are empty: 59 % of the main lines on 2026-10-29 were one-sided, almost all Higher-only. With rowShape: "side" the same line becomes two rows.
The payout multiplier is the pick'em number. Each pick scales the entry's payout by its own multiplier: 1.00 is standard, above 1.00 the side is boosted (pays more), below 1.00 discounted (pays less). In the verified run the multipliers ran from 0.51 to 19.91, median 1.28: 59 % of sides boosted, 14 % standard, 27 % discounted.
Two price sets, both served by Underdog. overOddsAmerican, overOddsDecimal (and the computed fractional, implied and no-vig probabilities; same for under…) are the price Underdog attaches to the side as a leg; on the alternate-line documents it is identical to the sportsbook price set served next to it — a sportsbook-style quote, not a payout you can collect on its own. The …Fantasy… columns are Underdog's separate fantasy set for the same side. They differ because they carry different margins; the Actor never blends them.
The margin is in the row. …ImpliedProbability is 1 / decimal odds to four decimals; …NoVigProbability is each side's share of the two (the pair sums to 1) and marketOverround is implied over + implied under − 1, the margin built into the line.
Alternate lines (opt-in). isAlternate = false rows come from the main board, true rows from the prop's ladder (only with includeAlternates: true). mainLineScore repeats the main line on every row of a prop, so lineScore - mainLineScore is the distance from it; the further away, the more extreme the multiplier (largest in the verified run: 19.91).
Times. startTime is the scheduled start in ISO 8601 UTC, updatedAt when Underdog last changed the line, scrapedAt when the Actor read the board (the same for every row of a run). The board only carries matches that have not started: in the verified run no row had a start time earlier than the moment it was read.
Scale and speed (measured)
All figures are from runs on the Apify platform on 2026-10-29, 1,024 MB memory (the default), default settings, direct connection first.
| Run | Rows | Time | Notes |
|---|---|---|---|
Console pre-fill (maxItems 300, main lines) | 300 | 9 s | first rows after 8 s |
| 1,000 rows with alternates on | 1,000 | 25 s | soonest matches first |
| Default run: main lines, whole board | 9,740 | 10 s | one request for the board, no ladders (13,742 rows in the side shape) |
Whole board with every alternate line (includeAlternates: true) | 24,995 | 394 s (6.6 min) | 9,744 props, 3,794 ladder requests, 202 MB peak memory, 0 duplicate rowId; 35,473 sides |
How the board is read
The main board is one document of about 25 MB that Underdog serves in roughly a second; the Actor downloads it once and flattens it in memory. A default run stops there: that is why it takes seconds. Alternate lines are not in that document: for every prop flagged hasAlternates (3,794 props on 2026-10-29) Underdog's web app makes one more request for the ladder, and so does the Actor when includeAlternates is on. Those requests are small (a few kilobytes) but numerous, so they are paced: the direct connection is used at a modest rate and, if Underdog's edge refuses it for a while, Apify datacenter sessions (one IP each) carry the load. In the verified run the direct connection answered 3,794 ladder requests, the datacenter sessions 0, and 0 were refused. That is why the whole board with alternates takes 6.6 minutes — but rows are saved from the first seconds on, soonest match first, and maxItems stops the reading as soon as enough rows are in.
What you pay
Pay per result, no start fee, no subscription: $0.25 per 1,000 rows on the free Store tier, falling to $0.15 per 1,000 on the highest tiers (Bronze $0.217, Silver $0.183, Gold and above $0.15). A row is one line (both sides in it); in the side shape a row is one side.
| Run | Rows | Free tier | Gold tier and above |
|---|---|---|---|
| 1,000 rows | 1,000 | $0.25 | $0.15 |
| Default run: whole main-line board (2026-10-29) | 9,744 | $2.44 | $1.46 |
With includeAlternates: true: whole board (2026-10-29) | 24,995 | $6.25 | $3.75 |
| 100,000 rows | 100,000 | $25.00 | $15.00 |
Alternates are opt-in because they multiply the rows and the run time (the default run is 39 % of the rows). Filters cut the bill directly — a filtered-out row is never saved or billed.
Maximum cost per run
Every run has a Maximum cost per run (Console → run options, or maxTotalChargeUsd in the API). The Actor reads the budget at the start, never saves more rows than it pays for, and ends the run as SUCCEEDED with a status message, for example "Saved 230 rows — this run's maximum cost ($0.05) covers 230 results at the current price. Raise "Maximum cost per run" to get more." Verified after pricing: a run capped at $0.05 saved 230 rows and was charged for exactly 230 (the number depends on the price of your tier). Set a small maximum while testing.
Integrations
- Apify API and clients —
apify-clientfor Node.js and Python (examples above), plain HTTP for everything else; the dataset as JSON, JSONL, CSV, XLSX, XML or HTML. - Schedules and webhooks — Console → Schedules (any cron; every 15 minutes for line movement, once a day for a "boosted picks" sheet); a webhook fires on success, failure or timeout with the run and dataset ids.
- Make, Zapier, n8n — the Apify app / node: "Run Actor", then "Get dataset items"; map
playerName,statType,lineScore,overMultiplier,underMultiplierinto the next step. - Google Sheets, Airtable, Notion, Slack, Discord, Telegram — through the Apify Google Sheets integration, Make / Zapier / n8n, or a small webhook handler.
- Warehouses and lakes — JSONL or CSV to S3 / GCS / Azure, or straight into BigQuery, Snowflake or Postgres.
rowIdis a stable primary key per line;scrapedAtis the snapshot key. - MCP, AI agents, Pandas / R / DuckDB — the Apify MCP server exposes the Actor as a tool; typed columns (
lineScore,overMultiplierare numbers,startTimeis ISO 8601) need no cleaning.
Performance tips
- Start from the question. League, player, team, stat and time filters are applied before any alternate-line request: "NBA points in the next 24 hours" needs a few dozen small requests, "everything" thousands.
- Alternates are opt-in. The default is one request for the whole board — 9,740 rows in about 10 seconds on 2026-10-29. Switch
includeAlternateson when you need the ladders, and narrow it with filters. - Use
maxItemsas a safety rail. Rows come out soonest match first; pair it with Maximum cost per run while testing. - Schedule instead of polling by hand, with a narrow filter, and compare runs by
rowId. - Do not start many full-board runs at the same moment. Underdog's edge refuses bursts from one address; the Actor paces itself, but ten parallel full runs are ten times the pressure.
Troubleshooting: every status message explained
The run's status message and the SUMMARY record in the key-value store always say what happened. COMPLETED, PARTIAL and EMPTY_RESULT end as Succeeded; BLOCKED, FETCH_FAILED and INVALID_INPUT end as Failed with the reason.
| Status message (start) | Meaning | What to do |
|---|---|---|
COMPLETED: saved N rows from L league(s) (with alternates on: … (K alternate-line rows)) | Everything requested was read. May end with the "maxItems" limit … was reached or this run's maximum cost … covers …. | Nothing; raise maxItems or the maximum cost for more. |
PARTIAL: … the alternate lines of N prop(s) could not be read (alternates on) | Underdog's edge refused or timed out some ladder requests even after retries on other exits. Those props keep their main line only; every other row is complete. | Run again later, or with fewer leagues. The SUMMARY lists the exits and refusals. |
PARTIAL: … could not read <LEAGUE> | One requested league could not be downloaded; the others are complete. | Run again, or add a proxy. |
PARTIAL: … the run time limit was reached | The timeout came before every prop was read; the rows saved are complete. | Raise the timeout or narrow the filters. |
EMPTY_RESULT: Underdog's board is empty … | Underdog answered but no lines are posted (off-season, no games). | Try again later or choose other leagues. Off-season leagues (MLB, UFL, NPB on 2026-10-29) are empty by nature. |
EMPTY_RESULT: N main lines are on the board but none match these filters. Leagues posting lines now: … | Your filters exclude everything; the message lists the leagues with lines. | Loosen a filter; check the spelling. |
The run's maximum cost … does not cover a single result | The "Maximum cost per run" is below the price of one row. | Raise it. |
Saved N rows — this run's maximum cost … covers N results | The cost ceiling was reached; the rows saved are complete and billed. | Raise "Maximum cost per run" to continue. |
FETCH_FAILED: Underdog answered HTTP 426 (upgrade required) … | Underdog retired or changed the endpoint this Actor reads. | Open an issue; we update the Actor. |
BLOCKED: no board could be read … / FETCH_FAILED: no board could be read … | Every route (direct, datacenter, capped residential) was refused or timed out — an access failure, not an empty board. | Retry in a few minutes; add proxyConfiguration. |
FETCH_FAILED: the board was read but no row could be completed … | The time limit ended before the first rows. | Raise the timeout or leave alternates off. |
INVALID_INPUT: … | A contradictory multiplier range, a date that is not YYYY-MM-DD, or a reversed date window. | Fix the input named in the message. |
SUMMARY notes: Resumed after a platform restart (rows already saved are not written or charged again); … read league by league through the capped residential rescue route (the board was unreachable directly).
Honest limits
- Pre-game lines only. The public board lists matches that have not started. In the data of 2026-10-29 no row had a start time earlier than the moment it was read; 177 team games and individual events were on the board, all
scheduledexcept one Counter-Strike match four minutes from its start (statusscoring). A line leaves the board when its match begins. There is no in-play data and no results or grading. - A snapshot, not a tick history. You get the board at the moment of the run. History is what you build by scheduling runs and keeping the datasets.
- Pick'em prices are not sportsbook bets.
…OddsAmerican/…OddsDecimalare quotes Underdog attaches to a leg, not a price you can bet at; an entry pays by Underdog's rules and the multipliers. Treat them as a market signal. - The board and the ladders are read seconds apart; each row carries its own
updatedAt. - Alternate lines (opt-in) cost requests. Ladders are read one prop at a time; a refused request is retried on another exit, and a prop whose ladder still cannot be read keeps its main line (the run ends
PARTIAL). - Home / away for
vstitles. AnAWAY @ HOMEtitle is exact. For avstitle the Actor resolves soccer (home team first — checked against stadiums and squads) and esports (away team first — checked against the squads of League of Legends and Counter-Strike matches); for any other sport with avstitle it leaveshomeTeam,awayTeamandplayerTeamempty rather than guess. - Weather and sport-specific context. On 2026-10-29 no match carried a forecast and MLB was out of season, so forecast columns are written only when they carry a value and MLB-specific context (probable pitchers) is not mapped; venue and broadcast are mapped where present (83 % and 94 % of rows).
- Not on the public board: pick popularity, player statistics and game logs, injury reports, live scores, the state-gated sportsbook and prediction-market products, contest or entry data.
- Leagues come and go. Off-season leagues return nothing; new leagues appear without a code change.
- The source can change. Underdog retires old API versions (the earlier ones answer HTTP 426). If the endpoint changes the Actor says so in plain words instead of returning an empty file.
How it compares
| Option | Effort | Alternate lines | Upkeep | Cost |
|---|---|---|---|---|
| Copying lines from the app by hand | Hours per slate | Only the ones you open | None | Your time |
| Your own script against the web app's JSON | Days; six document parts to join | Needs the separate ladder endpoint | You fix it when Underdog changes versions | Servers + proxies |
| A general sports-odds API | Low | Usually sportsbook main lines, rarely pick'em multipliers | Vendor | Monthly subscription |
| This Actor | Minutes | Whole ladder of every prop, opt-in | We keep it current | Pay per row, no monthly fee |
The trade-off: this Actor reads one platform's public board; it is not a multi-book odds feed.
FAQ
What is Underdog Fantasy Scraper?
An Apify Actor that reads the public Pick'em board of Underdog Fantasy (underdogfantasy.com / app.underdogsports.com) and returns one row per line with both sides in it — payout multipliers, American / decimal / fractional odds, implied and no-vig probabilities, player, team, matchup, start time, league and stat — with the alternate lines of each prop on request (or one row per side with rowShape: "side").
Is there an official Underdog Fantasy API?
Underdog does not publish an API for its Pick'em board. Its web app loads the board from JSON endpoints that any visitor's browser can call, with no login, cookie or key; this Actor reads those endpoints. They are undocumented and not guaranteed, so the Actor says so plainly when one changes.
How do I export Underdog props to Excel or CSV?
Run the Actor, open the Output tab and press Export → Excel or CSV, or call …/datasets/DATASET_ID/items?format=xlsx (or csv) with your token.
What is the difference between the line shape and the side shape?
The default is one row per line with both sides in it (overMultiplier / underMultiplier, …): about half the rows and price of the side shape. rowShape: "side" writes Higher and Lower as two rows (side, lineId, oppositeLineId, multiplier, oddsAmerican …) for tools that expect one selection per row.
What is a payout multiplier on Underdog?
The factor by which a pick scales the entry's payout: 1.00 standard, above 1.00 pays more, below pays less. Each side of a line has its own: overMultiplier and underMultiplier.
What do boosted, standard and discounted mean?
A derived label per side (overPayoutTier, underPayoutTier): boosted above 1.0, standard at 1.0, discounted below 1.0 — it lines up with PrizePicks' demon / standard / goblin picks.
What are alternate lines and how do I get them?
Other thresholds of the same prop — a 24.5-point prop may also offer 19.5, 29.5, 34.5 — each with its own multipliers and prices. They are off by default (they multiply the rows and the run time); includeAlternates: true adds them as rows of their own, and isAlternate tells the two apart. An alternate can sit at the same number as the main line with different prices (58 NFL props on 2026-10-29).
Why does hasAlternates say true but I only see the main line?
includeAlternates is off (the default), a side / tier filter removed the alternates, or the ladder request for that prop failed (the run then ends PARTIAL and says how many).
How many props and rows are there?
9,744 main lines on 2026-10-29 (the default run), plus 15,251 alternate lines with includeAlternates: true: 24,995 line rows in the verified run (35,473 sides, which is what rowShape: "side" would give). The numbers move with the schedule.
Which leagues and sports are supported?
Whatever Underdog lists that day. On 2026-10-29: NFL (2,929 lines), CFB (3,149 lines), NHL (1,328 lines), FIFA (996 lines), NBA (315 lines), TENNIS (223 lines), CS (207 lines), MMA (193 lines), LOL (120 lines), WNBA (76 lines), MOTORSPORTS (59 lines), DOTA (58 lines), CFL (40 lines), VAL (30 lines), PGA (21 lines). Use the league code in leagues.
Why is MLB (or another league) empty?
Underdog posts no lines for it — off-season or no games. MLB, UFL, NPB and Formula 1 season props were empty on 2026-10-29.
Does it include live, in-play lines?
No. In the verified run no row had a start time earlier than the moment it was read; a match leaves the board when it begins.
How often do lines change, and how often should I run it?
updatedAt shows lines changing minute by minute on a busy day: every 5–15 minutes with a narrow filter for line movement, once in the morning for a newsletter.
How do I track line movement?
Schedule the Actor, keep every run's dataset and compare two runs by rowId: lineScore, overMultiplier and overOddsAmerican of the same row give the movement; rows in one run only are new or removed.
Is rowId stable between runs?
While the line exists, yes. On 2026-10-29, 98 % of the main-line ids of a board read 83 minutes earlier were still on the board.
Why are some lines one-sided?
Underdog offers one side on many lines — mostly Higher-only props such as "anytime touchdown". On 2026-10-29 59 % of main-line rows came from such lines; sidesOffered is 1 and the other side's columns are empty.
What is the difference between overOddsAmerican and overFantasyOddsAmerican?
Underdog serves two price sets per side. …OddsAmerican / …OddsDecimal are the leg price (identical to the sportsbook set on alternate-line documents); …FantasyOdds… is a separate set with its own margin. Both are output as served, never blended.
Are these real sportsbook odds?
They are Underdog's quotes for a pick'em leg in odds formats — good for comparison and modelling, not a bettable price (see Honest limits).
How do I compare Underdog with PrizePicks or a sportsbook?
Join on playerName + statType + lineScore (sportsbooks also startTime). The names match our PrizePicks and Sleeper Scrapers (playerName, playerTeam, playerPosition, league, statType, lineScore, startTime, projectionId, overMultiplier / underMultiplier); the PrizePicks Actor's opponent string is matchup here, and opponent is the opposing team as in the Sleeper Actor.
How fast is it and how much does it cost?
Default (main lines): 9,740 rows in about 10 s; pre-fill: 300 rows in 9 s; the whole board with alternates on: 24,995 rows in 6.6 min. From $0.25 per 1,000 rows (free tier) down to $0.15: a main-line board is about $2.44, the whole board about $6.25 at the free tier.
How do I cap what I spend?
Set Maximum cost per run, use maxItems, or filter — a filtered-out row is never billed. The Actor stops exactly at the budget and says so in the status message.
Why did I get fewer rows than maxItems?
The board (after your filters) had fewer rows; the SUMMARY lists the leagues and counts on the board.
Why is playerTeam empty on some rows?
Individual sports (tennis, MMA, golf, NASCAR) have no team. For a team game with a vs title in a sport other than soccer or esports the Actor does not guess which side is home.
How are home and away decided?
AWAY @ HOME titles are exact. For vs titles, soccer lists the home team first and esports the away team first (checked against stadiums and squads); other sports are left empty.
Does it work from outside the US? Do I need a proxy?
Yes, and no. The Actor runs on Apify's infrastructure and reads Underdog's JSON from there. It connects directly, moves to the Apify datacenter proxy when Underdog's edge refuses the direct connection, and keeps a capped residential exit for small documents as a last resort; you can set your own proxy.
Can I get player stats, injuries, pick popularity, results or live scores?
No — they are not on the public board. The row has the player's position, jersey number, country and image.
Does it include Underdog's sportsbook or prediction markets?
No. Those are separate, state-gated products; this Actor reads the Pick'em board and its alternate lines.
Can I use the data in a commercial product?
That is governed by Underdog's terms, copyright and database law and your gambling and data-protection rules: read Is it legal to scrape Underdog? and take advice.
What happens when Underdog changes its API?
The Actor stops with a plain message (an upgrade-required answer) or returns PARTIAL with the reason instead of an empty file, and we update it.
Two stats share a display name — how do I tell them apart?
Use statKey. On 2026-10-29 only soccer "Assists" had two keys (assists and period_1_2_assists); period stats such as "1Q Rec Yards" have their own names.
Glossary
- Pick'em — a contest format where you pick Higher or Lower on player stats and combine picks into an entry.
- Higher / Lower — Underdog's wording for over / under.
- Line (
lineScore) — the threshold of a prop, e.g. 24.5 points. - Prop — one player, one stat, one match (
overUnderId). - Main line / alternate line / ladder — the line on the main board, another threshold of the same prop, and all lines of one prop ordered by
lineScore. - Payout multiplier — the factor a pick applies to the entry's payout; 1.00 is standard. Above 1.00 is boosted, below is discounted.
- American, decimal, fractional odds — three ways to write one price (+104, 2.04, 26/25).
- Implied probability — the chance a price represents,
1 / decimal odds; it includes the margin. - Margin (vig, juice) — how far the implied probabilities of all sides add up past 1.
- Line shopping / CLV — comparing a prop across platforms; comparing the line you took with the closing line (needs repeated snapshots).
- Solo game — Underdog's name for an individual event (a golf round, a tennis match, a fight, a race).
Is it legal to scrape Underdog?
The Actor reads data that Underdog serves to every anonymous visitor of its web app — it does not log in, bypass a paywall or use anyone's account, and it collects no personal data of Underdog's customers. The rows describe public matches, public athletes and the prices Underdog displays. That is not the same as permission: Underdog's website terms may restrict automated access or reuse, copyright and database rights may apply to the compiled board, and the rules for gambling-related data differ by country and state. You are responsible for how you use the output — check the terms and the law where you operate, take advice before building a commercial product on it, and do not use the data to harm the platform or to evade a restriction on you personally. Athlete names are personal data in a narrow sense; they are public figures and the data is sports statistics, but run your own data-protection assessment if you combine it with other personal data.
Responsible use
This is a data tool for analytics, research and tooling. It does not place bets, enter contests or automate an account, and promises no profit; past lines and multipliers do not predict results. Betting and fantasy pick'em involve real financial risk and can be addictive — take part only where it is legal for you, only with money you can afford to lose, and use the limit and self-exclusion tools of any platform you play on. If gambling is a problem for you or someone you know, contact your national problem-gambling helpline.
Changelog
- 1.0 — 2026-10-29 — first release: the whole Pick'em board (main lines by default, alternate-line ladders on request) as one row per line with both sides, or one row per side; filters for league, player, team, stat, side, tier, multiplier, start window and alternates; American / decimal / fractional odds, probabilities and multipliers on every row; paced reading with a capped residential rescue; pay per result with a maximum-cost guard; verified on a 24,995-row run, no duplicates.
Support
Questions, a missing field, a wrong value, a league that is empty when you expect lines? Open an issue on the Actor page with the run link — we reply within 24 hours. If Underdog changes its web app and a run starts failing, send us the status message; source changes are the first thing we fix.