
SofaScore Scraper
scrape.ly/store/querystone/sofascore-scraperSportsNews
1 monthly user

Scrape Equibase horse racing data for any date: race cards with every horse, jockey, trainer, owner and morning-line odds, results with finish order, times and win/place/show and exotic payouts, plus today's scratches and changes. All US and Canadian tracks.
Get North American horse racing data from Equibase, the official source for US and Canadian Thoroughbred racing. Each race comes back as one clean record: the race card with every horse, jockey, trainer, owner and morning-line odds, then the result with finish order, final time and win/place/show and exotic payouts. Pick a date (or a date range), leave the track list empty, and you get every track that raced. No login, no account and nothing to install.
yesterday, today, tomorrow or a date such as 2026-10-18).KEE, SA, GP, WO) or leave them empty for every track.| Field | What it does |
|---|---|
raceDate | Day to collect: YYYY-MM-DD, MM/DD/YYYY, today, yesterday, tomorrow or -3. Empty = today (US Eastern). |
trackCodes | Equibase track codes such as KEE, SA, GP, CD, WO. Empty = every track. |
dataMode | both (cards and results), entries, results or late_changes (today's scratches and changes). |
maxItems | Most records to save. 0 = no limit. |
endDate | Last day of a date range. |
surfaces, breeds, minPurseUsd, maxPurseUsd, stakesOnly | Filters. |
includeHorseHistory, maxHorseProfiles | Add one horse history record per runner. |
{
"raceDate": "2026-10-18",
"trackCodes": ["KEE", "SA"],
"dataMode": "both",
"maxItems": 50
}
One record per race (record_type: "race"). Card fields are filled as soon as entries are posted; result fields fill in once the race has been run (results_available).
| Field | Description |
|---|---|
race_id | Track, date and race number, e.g. KEE-2026-10-18-R8 |
track_code, track_name, track_country | Track |
race_number, race_name, race_type, breed | Race |
distance, distance_furlongs, surface, scheduled_surface | Distance and surface (surface is where the race was run; scheduled_surface is the card's, so races taken off the turf are visible) |
purse_usd, post_time, post_time_zone, off_time | Purse and times |
age_sex_restriction, conditions, wagers | Eligibility, full conditions and wagers offered |
entries_count, entries, entries_detail | Starters, as text and one object per horse (ids, pedigree, connections, odds, scratches, finish position) |
scratched_horses | Scratches with the reason |
track_condition, winning_time, finish_order, finish_order_detail | Result |
winner_horse, winner_jockey, winner_trainer, winner_owner, winner_breeder, winner_sire | Winner and connections |
win_payout, place_payout, show_payout, payouts_by_horse | $2 win/place/show prices |
exotic_payouts, exotic_payouts_by_type | Exacta, trifecta, superfecta, daily double, pick 3/4/5/6 and more |
entry_url, result_url, scraped_at | Sources and time |
Race-day changes (record_type: "late_change") carry change_type, change_text, old_value, new_value, reason and time_posted. Horse history (record_type: "horse_history") carries past_starts (with Equibase speed figures and a race_id that joins to race records), workouts, ratings, year_stats and career_stats.
Example race record (from a test run, trimmed to two horses; people's names replaced with fictional ones):
{
"record_type": "race",
"race_id": "LRL-2026-10-18-R1",
"race_date": "2026-10-18",
"track_code": "LRL",
"track_name": "Laurel Park",
"track_country": "USA",
"race_number": 1,
"race_name": "Maiden Claiming",
"race_type": "Maiden Claiming",
"breed": "Thoroughbred",
"distance": "Five And One Half Furlongs",
"distance_short": "5 1/2 F",
"distance_furlongs": 5.5,
"surface": "Dirt",
"scheduled_surface": "Turf",
"track_condition": "Fast",
"purse_usd": 30000,
"post_time": "12:00 PM",
"post_time_zone": "ET",
"age_sex_restriction": "2yo F",
"entries_count": 5,
"entries": "1|Kelly's Hardheaded (KY)|J Doe|A Smith|122|8/1 | 7|Hadid (PA)|M Rivera|P Jones|122|7/5",
"entries_detail": [
{
"program_number": "7", "post_position": "7", "horse_name": "Hadid (PA)", "state_bred": "PA",
"horse_refno": "11501091", "horse_registry": "T", "age_sex": "2/F", "medication": "L",
"claim_price_usd": 25000, "jockey": "M Rivera", "trainer": "P Jones", "weight_lbs": 122,
"ml_odds": "7/5", "owner": "Example Stable LLC", "breeder": "Jane Example", "sire": "Example Sire",
"dam": "Example Dam", "dam_sire": "Example Damsire", "is_scratched": false, "finish_position": 1
},
{
"program_number": null, "horse_name": "I Am King (KY)", "horse_refno": "11470130",
"ml_odds": "20/1", "is_scratched": true, "scratch_reason": "Off-Turf"
}
],
"results_available": true,
"winner_horse": "Hadid",
"winner_program_number": "7",
"winner_jockey": "Mario Rivera",
"winner_trainer": "Peter Jones",
"winning_time": "1:07.03",
"finish_order": "1. Hadid, 2. Kelly's Hardheaded, 3. Last Hot Lover, 4. Her Decision, 5. Catty's Angel",
"win_payout": 4.4,
"place_payout": 3.4,
"show_payout": 2.1,
"exotic_payouts": "{\"exacta\":14.6,\"superfecta\":93.3,\"trifecta\":39.2}",
"exotic_payouts_by_type": {
"exacta": {"payoff": 14.6, "raw_label": "$1 Exacta", "wager_unit_usd": 1.0, "winning_numbers": "7-1",
"payoffs": [{"payoff": 14.6, "winning_numbers": "7-1"}]}
},
"entry_url": "https://www.equibase.com/static/entry/LRL101826USA-EQB.html",
"result_url": "https://www.equibase.com/static/chart/summary/LRL101826USA-EQB.html",
"scraped_at": "2026-10-21T19:36:10.000Z"
}
Pay per event, plus platform usage:
Worked examples, measured on test runs (platform usage is about $0.01 for a 20-race run):
You can cap any run with the maximum charge setting; the run stops cleanly when it is reached.
Is it legal to scrape Equibase? The actor reads only public pages anyone can open without an account. Check Equibase's terms of use for how you may use the data, especially commercially.
Do I need a login or a proxy? No. The default proxy setting works; race cards and results are read through a hosted browser with its own US connection.
How far back can I go? Results go back about two months and full race cards about 10 days; older dates fail fast with a clear message instead of costing you anything. Race cards are posted a few days ahead. Dates older than about a month take longer, because the scraper has to check each track for that day.
Why is a field empty? Results fields are empty for races not yet run, race_cancelled is only known for today's card and past races, and some tracks don't publish every detail (for example claiming prices in allowance races).
Why does surface differ from scheduled_surface? The race was moved, usually taken off the turf because of weather. surface is where it was actually run.
Can I schedule it or use the API? Yes. Schedule it daily (for example yesterday in results mode after the last post), or start runs through the API and read the dataset as JSON or CSV.
Every field below can be set in the run form, passed through the API, or saved as a task. Unset fields fall back to the defaults shown.
raceDatestringOptionalRace date
The day to collect, as YYYY-MM-DD (e.g. 2026-10-18) or MM/DD/YYYY. You can also write today, yesterday, tomorrow or -3 (three days ago). Empty = today (US Eastern time). Results exist only for races that have been run; race cards are posted a few days ahead.
trackCodesarrayOptionalTrack codes
Only these tracks, by Equibase track code, e.g. KEE (Keeneland), SA (Santa Anita), GP (Gulfstream Park), CD (Churchill Downs), WO (Woodbine), AQU, BEL, SAR. Leave empty for every track racing on the date (US and Canada).
dataModeenumOptionaldefault "both"Data to collect
Race cards and results: one record per race with the field (horses, jockeys, trainers, owners, morning-line odds) plus finish order, times and payouts once the race has been run. Race cards: entries only. Results: only races that have been run. Race-day changes: today's scratches (with reasons), jockey, weight and equipment changes, track conditions and rail settings, one record per change (Equibase publishes the current race day only).
maxItemsintegerOptionaldefault 20Max records
Stop after this many records (one race = one record; in race-day changes mode one change = one record). A busy day in North America has 200 to 300 races. 0 = no limit.
endDatestringOptionalEnd date (for a date range)
Collect every day from Race date up to and including this date, e.g. 2026-10-20. Leave empty for a single day. Same formats as Race date.
surfacesarrayOptionalSurfaces
Only races on these surfaces. Leave empty for all.
breedsarrayOptionalBreeds
Only races for these breeds. Leave empty for all (most cards are Thoroughbred; some tracks also run Quarter Horse and Arabian races).
minPurseUsdintegerOptionalMinimum purse (USD)
Only races with at least this purse, e.g. 100000.
maxPurseUsdintegerOptionalMaximum purse (USD)
Only races with at most this purse.
stakesOnlybooleanOptionaldefault falseStakes races only
Only stakes races (named and graded races).
includeHorseHistorybooleanOptionaldefault falseAdd horse history
Adds one extra record per distinct horse in the saved races: every career start (track, date, race, race type, finish, Equibase speed figure, with a race_id that joins to race records), workouts from the last 60 days, ratings, current-year and career stats, pedigree and connections. Charged as a horse profile (see Pricing). Not used for race-day changes.
maxHorseProfilesintegerOptionaldefault 100Max horse profiles
Most horse profiles to add per run (only with Add horse history). Horses are taken in card order. 0 = every horse in the saved races.
proxyConfigurationobjectOptionaldefault {"useScrapelyProxy":true,"scrapelyProxyGroups":["DATACENTER…Proxy configuration
Proxy for the race-day changes feed and for finding the tracks of older dates. Race cards and results are read through a hosted browser with its own US connection.
Every run writes its results to a dataset. Preview it in the dashboard, then export it or read it through the API.
| Field | Type | Description |
|---|---|---|
| record_type | string | null | What this record is: race (one race card and/or result), late_change (one race-day change) or horse_history (one horse profile). |
| race_id | string | null | Race id: track code, date and race number, e.g. KEE-2026-10-18-R8. Joins race and change records. |
| race_date | string | null | Race date, YYYY-MM-DD. |
| track_code | string | null | Equibase track code, e.g. KEE, SA, GP, WO. |
| track_name | string | null | Track name, e.g. Keeneland. |
| track_country | string | null | Country code Equibase uses for the track: USA, CAN or PR. |
| race_number | number | null | Race number on the card. |
| race_name | string | null | Stakes name for named races, otherwise the race type. |
| race_type | string | null | Race type, e.g. Maiden Claiming, Allowance Optional Claiming, Stakes. |
| stakes_grade | number | null | Graded stakes level: 1, 2 or 3 (null for ungraded races). |
| black_type | boolean | null | True for black-type (graded or listed) stakes; null for races that are not stakes. |
| breed | string | null | Breed: Thoroughbred, Quarter Horse, Arabian or Mixed. |
| distance | string | null | Distance as Equibase writes it, e.g. Six And One Half Furlongs. |
| distance_short | string | null | Short distance, e.g. 6 1/2 F, 1 1/16 M, 870 Y. |
| distance_furlongs | number | null | Distance in furlongs. |
| surface | string | null | Dirt, Turf or Synthetic. |
| scheduled_surface | string | null | Surface on the race card; differs from surface when a race was moved (e.g. taken off the turf). |
| track_condition | string | null | Track condition, e.g. Fast, Firm, Sloppy (from the result, or today's posted condition). |
| purse_usd | number | null | Purse in US dollars. |
| post_time | string | null | Scheduled post time, local time with the zone, e.g. 1:30 PM ET. |
| post_time_zone | string | null | Time zone of the post time, e.g. ET, CT, MT, PT. |
| off_time | string | null | Time the race went off (results). |
| age_sex_restriction | string | null | Eligibility in short form, e.g. 3yo+ F&M. |
| age_restriction | string | null | Age restriction as written on the result, e.g. Three Year Old and Upward. |
| sex_restriction | string | null | Sex restriction as written on the result, e.g. Fillies and Mares. |
| conditions | string | null | Full race conditions text (eligibility, weights, claiming price). |
| wagers | string | null | Wagers offered on the race. |
| entries_count | number | null | Number of starters (scratches and also-eligibles not counted). |
| entries | string | null | Starters as text: post|horse|jockey|trainer|weight|morning line, separated by ' | '. |
| entries_detail | array | null | One object per horse: program_number, post_position, horse_name, state_bred, horse_refno, horse_registry, age_sex, medication, claim_price_usd, jockey, jockey_id, trainer, trainer_id, weight_lbs, ml_odds, owner, breeder, sire, dam, dam_sire, is_scratched, scratch_reason, is_also_eligible, coupled_group, finish_position, dead_heat. |
| scratched_horses | array | null | Scratched horses with the reason when Equibase gives one. |
| race_cancelled | boolean | null | True when today's race-day changes list the race as cancelled; null when not known (dates other than today). |
| results_available | boolean | null | True when the race has been run and the result is included. |
| winner_horse | string | null | Winning horse. |
| winner_program_number | string | null | Program number of the winner. |
| winner_jockey | string | null | Winning jockey. |
| winner_trainer | string | null | Winning trainer. |
| winner_owner | string | null | Winning owner. |
| winner_breeder | string | null | Winning breeder. |
| winner_sire | string | null | Sire of the winner. |
| winning_time | string | null | Final time, e.g. 1:09.42. |
| finish_order | string | null | Order of finish as text: 1. Horse, 2. Horse, ... |
| finish_order_detail | array | null | Order of finish, one object per horse: finish_position, program_number, horse_name, horse_refno, horse_registry, dead_heat. |
| win_payout | number | null | Win payout on a $2 bet on the winner. |
| place_payout | number | null | Place payout on a $2 bet on the winner. |
| show_payout | number | null | Show payout on a $2 bet on the winner. |
| payouts_by_horse | array | null | Full $2 win/place/show board, one object per paying horse. |
| exotic_payouts | string | null | Exotic payouts as a JSON string, e.g. {"exacta":24.6,"trifecta":98.75}. |
| exotic_payouts_by_type | object | null | Exotic payouts by bet type (exacta, trifecta, superfecta, daily_double, pick_3...): payoff, raw_label, wager_unit_usd, winning_numbers and every payoff line. |
| entry_url | string | null | Equibase race card page for the track and date. |
| result_url | string | null | Equibase results page for the track and date. |
| change_id | string | null | Race-day change: stable id of the change. |
| program_number | string | null | Race-day change: program number of the horse (null for race-level changes). |
| horse_name | string | null | Race-day change: the horse's name. |
| change_type | string | null | Race-day change: scratch, scratch_reason, jockey, weight, equipment, medication, gelding, apprentice, track_condition, rail, course, distance, wager_cancellation or other. |
| change_description | string | null | Race-day change: the change name as Equibase gives it. |
| change_text | string | null | Race-day change: readable text, e.g. Jockey - A changed to B. |
| old_value | string | null | Race-day change: value before the change. |
| new_value | string | null | Race-day change: value after the change. |
| reason | string | null | Race-day change: scratch reason, e.g. RegVet-Unsound, Trainer. |
| time_posted | string | null | Race-day change: when it was posted, Eastern time. |
| time_posted_at | string | null | Race-day change: when it was posted, ISO UTC. |
| scraped_at | string | null | When the record was collected (ISO UTC). |
| state_bred | string | null | Horse history: state or country suffix of the horse's name, e.g. KY, IRE. |
| horse_refno | string | null | Horse history: Equibase horse id (the same id entries_detail carries). |
| horse_registry | string | null | Horse history: registry letter of the horse id (T = Thoroughbred, Q = Quarter Horse). |
| color | string | null | Horse history: coat color code, e.g. B, CH, DK B/. |
| sex | string | null | Horse history: sex code, e.g. C, F, G, M, H. |
| foaled | string | null | Horse history: foaling date, YYYY-MM-DD. |
| sire | string | null | Horse history: sire. |
| dam | string | null | Horse history: dam. |
| dam_sire | string | null | Horse history: sire of the dam. |
| rating | number | null | Horse history: Equibase horse rating (weekly). |
| last_jockey | string | null | Horse history: jockey at the last start. |
| last_trainer | string | null | Horse history: trainer at the last start. |
| owner | string | null | Horse history: owner at the last start. |
| breeder | string | null | Horse history: breeder. |
| year_stats | object | null | Horse history: current-year starts, firsts, seconds, thirds and earnings_usd. |
| career_stats | object | null | Horse history: career starts, firsts, seconds, thirds and earnings_usd. |
| past_starts | array | null | Horse history: one object per career start, newest first: race_id, track_code, track_name, race_date, race_number, race_type, breed, finish_position, finish, speed_figure. |
| workouts | array | null | Horse history: workouts from the last 60 days: track_code, track_name, workout_date, surface, distance, time, note, rank. |
| ratings | array | null | Horse history: rating history (date, rating). |
| entered_race_ids | array | null | Horse history: race_ids of this run's saved races the horse runs in. |
| profile_url | string | null | Horse history: the Equibase horse profile page. |
You pay for the events the actor reports, such as each result or page it produces. Platform usage is included in the event prices.
| Event | Charged when | Price |
|---|---|---|
| Horse profile | One horse history record (career starts, speed figures, workouts), only when Add horse history is on. | $0.0008 |
| Race record | One race (race card and/or result with payouts) or one race-day change. | $0.0008 |
| Run start | Charged once when a run starts. | $0.15 |
Run this actor from your own code. Swap in your API token and the examples work as-is.
import { ScrapelyClient } from 'scrapely-client'
const client = new ScrapelyClient({ token: process.env.SCRAPELY_TOKEN })
// Start the actor and wait for the run to finish.
const run = await client.actor('querystone/equibase-scraper').call({
"raceDate": "yesterday",
"maxItems": 20,
"proxyConfiguration": {
"useScrapelyProxy": true,
"scrapelyProxyGroups": [
"DATACENTER"
]
}
})
// Read the dataset the run produced.
const { items } = await client.dataset(run.defaultDatasetId).listItems()
console.log(items)Tokens live in your account settings. Keep them server-side: a token carries the full permissions of your account.
Connect the Scrape.ly MCP server once and your agent can run this actor as a tool. Pick your tool:
Fetch scrape.ly/agents.md and run querystone/equibase-scraperPaste into Claude Code, Codex, Cursor or any AI agent. It signs you in once in the browser.
{
"mcpServers": {
"scrapely": {
"url": "https://api.scrape.ly/mcp?tools=actors,runs,storage,docs&actors=querystone/equibase-scraper"
}
}
}No key in the file: your client opens the browser to sign you in the first time it connects.
No examples yet
The developer hasn't published example inputs for this actor.
Scrape.ly gives you the infrastructure, the tooling, and the team to extract, process, and operationalize web data at any scale. Setup takes minutes. The advantage lasts forever.