Underdog Fantasy Player Props Scraper - Lines & Line Moves avatar

Underdog Fantasy Player Props Scraper - Lines & Line Moves

Pricing

from $0.11 / 1,000 line returneds

Go to Apify Store
Underdog Fantasy Player Props Scraper - Lines & Line Moves

Underdog Fantasy Player Props Scraper - Lines & Line Moves

For DFS and betting models: every Underdog Higher/Lower prop as a row - player, team, stat, line, payout multiplier, price, implied probability, start time. 5,607 pre-game lines, 11 leagues, one request (2026-09-22). Monitoring returns only lines that moved, with the earlier values.

Pricing

from $0.11 / 1,000 line returneds

Rating

0.0

(0)

Developer

NeverEmpty

NeverEmpty

Maintained by Community

Actor stats

0

Bookmarked

1

Total users

0

Monthly active users

6 days ago

Last modified

Share

For DFS and betting models: every Underdog Fantasy Higher/Lower player prop as one row - player, team, opponent, stat, the line itself, the payout multiplier for each side, American and decimal prices, Underdog's own implied probability and the game start time. On 2026-09-22 a single request returned 5,607 pre-game lines across 11 leagues, in 11.0 seconds. Turn monitoring on and later runs return only the lines that have actually changed, with the previous value, the first value ever seen and both differences in the same row - so you are never charged for the same unchanged line twice.

Export as JSON, CSV or Excel.

Why monitoring matters here

Underdog's board barely moves between runs. Measured by taking the whole board twice, 9 minutes 5 seconds apart, on 2026-09-22:

lines
on the board the second time5,542
unchanged since the first read5,368
changed (line, multiplier, price, odds or status)169
newly opened5
charged in monitoring mode174 (3.1%)
lines that had closed and were gone70

Reading the board costs the same either way - the $0.01 start below. With monitoring on, that run charges for 174 rows instead of 5,542 - about 1/32 of the rows - and each of those rows carries what the value was before.

Both snapshots are in this repository as test/fixtures/board-a-fingerprints.json and board-b-fingerprints.json (the key and the fingerprint of every line, produced by src/underdog.js itself), and npm test recounts every number above. The 45-line and 47-line samples in test/fixtures/lines-a.json and lines-b.json are cut from the same two reads.

How much moves depends entirely on the gap between runs, and one measurement is one measurement. Three more pairs of runs on the same day, all on the same 6,300-line board:

gap between runslines changed
back to back (about a minute)2, then 1
a few minutes256
25 minutes1,052 of 6,182 (17%)

So the shorter the schedule, the smaller the bill per run - the opposite of paying full price for the whole board every time. Set the run's maximum total charge if you want a hard ceiling rather than trusting a rate.

What you get

One row per line. Columns:

  • Identity - lineKey (stable across runs; use it to join runs together), lineId, overUnderId, propId (player + stat, shared by a line and its alternates), appearanceId
  • Player - playerId, playerName, firstName, lastName, position, positionName, jerseyNumber
  • Teams - teamId, teamName, opponentTeamId, opponentName, homeAway
  • Game - matchType, gameId, gameTitle, gameShortTitle, gameStartsAt, gameStatus, gameVenue, competition
  • The prop - league, leagueName, stat (Underdog's code, e.g. rush_rec_yds), statName (Rush + Rec Yards), propTitle, line, lineText, hasAlternateLines, sidesOffered
  • Each side (higher... and lower...) - PayoutMultiplier, AmericanPrice, DecimalPrice, OddsAmerican, OddsDecimal, ImpliedProbabilityPercent, Status, PickText
  • The line's own state - lineStatus, lineType, isLiveLine, providerId, lineUpdatedAt
  • Monitoring only - isNewSinceLastRun, changeKinds (line / multiplier / price / odds / status / new), previousLine, lineMove, firstSeenLine, lineMoveSinceFirstSeen, previousHigherPayoutMultiplier, higherPayoutMultiplierMove, previousLowerPayoutMultiplier, lowerPayoutMultiplierMove, previousHigherOddsAmerican, previousLowerOddsAmerican, firstSeenAt, previouslySeenAt

Three different numbers that all look like a price

Underdog publishes three sets, and they do not agree with each other. They are kept in separate columns and none of them is derived from another:

columnwhat Underdog calls itexample
higherPayoutMultiplierpayout_multiplier - the multiplier shown on the pick1.11
higherAmericanPrice / higherDecimalPriceamerican_price / decimal_price+123 / 2.23
higherOddsAmerican / higherOddsDecimal / higherImpliedProbabilityPercentodds.fantasy+106 / 2.06 / 45

odds.sportsbook and odds.prediction were null on all 9,122 sides measured, so they are not columns.

Leagues

NFL, CFB (College Football), MLB, NHL, WNBA, TENNIS, MMA, CS (Counter-Strike), LOL, VAL (Valorant), F1SZN (Formula 1, season-long). Counts on 2026-09-22: MLB 2,105 / NFL 1,432 / CFB 868 / CS 389 / WNBA 244 / Tennis 237 / MMA 102 / NHL 89 / Valorant 79 / LoL 40 / F1 22. Which leagues are on the board depends on the day; an empty board is Underdog's own answer and comes back as a free row, not as a failure.

That list is what Underdog's board carried on the day this Actor was built, not a promise about what Underdog offers. Leave leagues empty and you get every league on the board, including any Underdog has added since. Naming a league that is not in the list above stops the run before it asks Underdog anything, and that run is free.

Input

Everything is optional. With an empty input the whole board comes back, up to maxLines.

fieldwhat it does
leagueskeep only these leagues. Empty = every league
playerskeep a line only if the player's name contains one of these (partial, case-insensitive)
statskeep a line only if the stat name or Underdog's stat code contains one of these
teamskeep a line only if the team, the opponent or the matchup title contains one of these
keywords / keywordMatch / excludeKeywordskeep or drop on any of the text columns; keywordMatch is any or all
minPayoutMultiplier / maxPayoutMultiplierkeep a line only if a side pays inside this range. 0 = no limit
onlyBoostedLinesonly lines where a side pays something other than 1.0
onlyStandardLinesonly lines where every side offered pays 1.0
onlyBothSidesdrop lines that offer one side only
startsWithinHoursonly games starting within this many hours. 0 = no limit
maxLineshow many rows this run may return (default 500)
monitoringModereturn only lines that changed since the last run with this same input
resetMonitoringStateforget what was already returned, so everything counts as new again
onlyLineMovesmonitoring only: drop changes where only the multiplier, price, odds or status moved
minLineMovemonitoring only: the line must have moved at least this far
minPayoutMultiplierMovemonitoring only: a payout multiplier must have moved at least this much
useProxyuse an Apify proxy and retry a refused request from another address (on by default)

Filtering happens after the board is read, so it never costs an extra request. Filters keep rows whose value is unknown rather than dropping them, because unknown is not the same as small - except teams, where a row with no team and no matchup title (Underdog's season-long props) is dropped, since naming a team means you do not want those.

Honest limits

  • The feed is one big request, and its speed varies a lot. 16,026,326 bytes (1,421,724 over the wire with gzip). The same feed answered in 2.7s, 11.0s, 35.0s and 111.4s on 2026-09-22. Each attempt waits up to 180 seconds and the run tries up to 4 times, so a slow day costs time, not rows.
  • Pre-game only. Underdog's live in-play lines need a logged-in account. This Actor never logs in, so it returns pre-game lines only: all 5,607 lines in the 2026-09-22 snapshot carried live_event: false and line_type: balanced (npm test recounts both from test/fixtures/board-a-fingerprints.json).
  • Team names are not in this feed. Underdog publishes team ids and a matchup title. The title comes in two shapes: Atlanta Falcons @ Green Bay Packers and Esport Academy Copenhagen vs 100 Thieves. A @ B is away at home, so teamName and opponentName are filled in for those. For the vs shape it is not verified which side is home, so those rows leave teamName and opponentName empty rather than guess - teamId, opponentTeamId, homeAway and gameTitle are still there.
  • Some props have no game. Underdog's season-long props (89 NHL lines on 2026-09-22) are attached to a season, not a match, so every game column is empty. Formula 1 season props have no opponent.
  • Some lines offer one side only. 2,092 of 5,607 lines carried a single side - 2,076 Higher-only and 16 Lower-only - so the missing side's columns are empty, not zero.
  • A line Underdog gives no stable id, or repeats, is skipped rather than returned, because there would be no safe way to tell it apart from another line on the next run. The run says how many were skipped; on 2026-09-22 it was 0 of 5,607.
  • Payout multipliers are Underdog's numbers, not a calculation. They ranged from 0.60 to 19.82 on 2026-09-22, and 3,483 of 9,122 sides were the standard 1.0.
  • Monitoring memory is per input. Two schedules with different filters keep separate memories. If two runs with the same input finish at the same instant, one can overwrite the other's memory - Apify key-value stores have no atomic update, so this cannot be fully prevented. The run says so in its log when it happens. Do not put the same input in two schedules that overlap.
  • A changed line that was not returned is not remembered. If a run hits "Maximum lines to return" or the run's maximum total charge, or if a line changed but has not yet moved far enough for minLineMove / minPayoutMultiplierMove / onlyLineMoves, its earlier values are deliberately kept. So a line that moves 0.5 four times does reach a minLineMove of 1 - the movement adds up instead of being reset each run.
  • A changed line your other filters exclude is remembered, at its new value. Leagues, players, stats, teams, keywords, multipliers and start time are about which lines you want at all, so widening one of them later does not dump the whole board on you as new.
  • This is not advice about betting or investing. It returns what Underdog publishes, nothing more.

Rows that are never charged

Every run that returns nothing says why, in one free row:

statusmeaning
no-resultsUnderdog answered correctly and its board is empty
no-filter-matchlines were read, the filters removed all of them
no-line-movemonitoring mode: every line read is unchanged
limit-reachedmore lines matched than "Maximum lines to return"
charge-limit-reachedthe run's maximum total charge was reached before all matching lines were returned
blockedUnderdog refused the request (403 / 429 / 503) from every address tried
unreadablethe feed could not be read, or its shape changed
unsupported-inputthe input asks for something that cannot exist (an unknown league, two filters that contradict each other) - nothing is requested from Underdog

no-results, no-line-move and blocked are deliberately three different things. A refused request is never reported as "there are no lines".

Pricing

Two charges, and nothing else:

eventprice
actor-start - once per run in which Underdog answered with a board carrying at least one line$0.01
line-scraped - per line returned$0.15 per 1,000 (less on paid Apify plans)

Rows that explain why nothing came back are always free. A run is charged nothing at all, not even the start, when Underdog refused it, when it timed out, when the board came back empty, or when it was stopped before asking Underdog anything (an unsupported league, or two filters that contradict each other). Filters that simply match nothing do not make a run free - the board was read, so the $0.01 start applies.

In monitoring mode only the lines that changed are charged; there is no separate per-check fee. A run where nothing moved therefore costs $0.01 and returns one free row saying so. Following 20 lines every 5 minutes costs about $0.01 x 8,640 runs a month in start fees, plus the handful of rows that actually moved - decide the schedule with that in mind, and set the run's maximum total charge if you want a hard ceiling.

How it reads Underdog

One request to Underdog's public pick'em feed, https://api.underdogfantasy.com/v1/over_under_lines. No login, no paywall, no bot check. https://api.underdogfantasy.com/robots.txt was fetched twice on 2026-09-22 and contains a single comment line with no rules at all.

The response is large (16,026,326 bytes; 1,421,724 over the wire with gzip) and its speed varies: 2.7s, 11.0s, 35.0s and 111.4s were all measured on 2026-09-22. Of 12 Apify datacentre addresses tested that day, 11 answered HTTP 200 and 1 answered 403, so a refused or timed-out request is retried up to four times - from a different address when a proxy is on - before the run gives up and writes a free blocked row.

Unofficial. Public data only.

Not affiliated with, endorsed by or connected to Underdog Fantasy or any sportsbook. It reads only what Underdog publishes without a login, and returns no personal data: the people in the output are professional athletes, and the columns are their public playing statistics.