ParseForge Scrapers

Sporting Life Scraper - Racecards, Results & Odds API

parseforge/sporting-life-racing-scraper

OtherAIDeveloper tools

Scrape Sporting Life horse racing: racecards, results, runners, weights, official ratings, Timeform commentary, bookmaker odds, tote payouts, and horse, jockey and trainer profiles. UK, Ireland, France and USA. No login, no API key, no proxy.

Run this scraper See the API call
Total users
3
Monthly active
2
Total runs
15
Bookmarked
0
Rating
Not rated yet
Last modified
27 hours ago

Overview

ParseForge

Sporting Life Scraper - Racecards, Results & Odds API

Scrape Sporting Life horse racing: racecards, results, runners, odds, and form. Every runner comes with its weight, official rating, draw, headgear, trainer, jockey, owner, market price, Timeform commentary, and finishing position. UK, Irish, French, and US cards, with results going back two decades. No login, no API key, no cookies, and no proxy. Export to CSV, JSON, Excel, or XML.

Sporting Life publishes the Press Association and Timeform racing feed to anonymous visitors. This reads that same public feed, filters it by course, country, class, going, price, or connections, and returns each match in one flat schema.

Who uses it What they scrape Sporting Life for
Betting modellers Runner-level training data with prices, ratings, and results in one row
Tipsters and analysts Today's cards with Timeform verdicts, market moves, and best available odds
Odds comparison sites Live prices from every bookmaker on the card, refreshed per race
Racing media and bloggers Fast results, tote payouts, and replays minutes after the weigh-in
Bloodstock and form researchers Twenty years of results with pedigree, connections, and full form lines

What it does

This Actor collects public Sporting Life racing data from four source types: race days, individual races, profile IDs, and any sportinglife.com racing URL, and returns each runner as a flat row. Every runner carries:

  • ๐Ÿ‡ The runner: horse name, age, sex, cloth number, draw, weight carried in stones and pounds, official rating, headgear, medication, and jockey's claim.
  • ๐Ÿ The result: finishing position, distance beaten, winner and placed flags, and the reason a horse did not complete.
  • ๐Ÿ’ท The market: starting price as fraction and decimal, implied probability, opening price, full price history, market move, favourite flag, and market rank.
  • ๐Ÿง  Timeform's words: the pre-race commentary for each runner, the race verdict with its selections, and the in-running comment after the race.
  • ๐Ÿ‘ฅ Connections: trainer, jockey, and owner, each with the ID and profile URL to follow up.
  • ๐Ÿ—“๏ธ Race and meeting context: course, country, surface, going, distance in yards, class, age band, handicap flag, prize money, field size, weather, and off time.
  • ๐ŸŽŸ๏ธ Tote returns: win, place, exacta, trifecta, tricast, swingers, straight forecast, placepot, and quadpot on settled races.
  • ๐Ÿ“ˆ Computed signals: decimal odds, implied probability, market rank, lifetime win percentage, and distance in yards, ready to sort.

Results export to CSV, JSON, Excel, or XML, or stream from the API.

What you can do with Sporting Life data

๐Ÿงฎ Build a racing model.

Pull several seasons of results with includeHorseForm and get every runner's weight, rating, going, draw, and starting price alongside where it finished.

๐Ÿ“Š Compare bookmaker prices on today's card.

Tick includeBookmakerOdds and each runner arrives with every firm's fractional and decimal price, each-way terms, places paid, and the best price on offer.

๐Ÿ”” Watch one trainer or jockey.

Set trainerNameContains or jockeyNameContains and schedule the run daily to get only their runners, with prices and results.

โฑ๏ธ Read today's results as they land.

Sporting Life's fast-results feed settles within a minute of the weigh-in. Run the same day with raceStages: ["WEIGHEDIN"] and every finished race arrives with its finishing order, distances beaten, and tote returns.

๐Ÿ† Track a course or a festival.

Set courses and a date range to collect a whole meeting, or use outputMode: meetings for one row per race day with its card nested.

๐Ÿ“œ Rebuild a historical archive.

Results reach back to early 2005, solid from that April. Run a month at a time and page through the years.

Why choose this scraper

What you get
No login, ever No account, no cookies, no session to keep alive. Nothing to get banned.
No proxy cost Sporting Life serves this feed to anonymous clients, so there is no proxy line on your bill.
Timeform words included Per-runner commentary and the race verdict, not just numbers.
Four countries, one run Britain, Ireland, France, and the United States on the same card listing.
Twenty years of results Racecards run about five days ahead; results go back to early 2005.
Three row shapes One row per runner, per race with runners nested, or per meeting with the card nested.
Filters that cut noise Course, country, class, surface, going, prize, time, rating, age, price, and connections. Only matching rows are written and billed.
Opt-in extras, off by default Bookmaker odds, form history, pedigree, replays, naps, and articles are each a checkbox. Leave them off and you are never billed for them.

How it compares

Be clear about the trade-off before you pick this one.

This Actor covers the cards Sporting Life publishes. That is Britain, Ireland, France, and a US selection. It is not a global feed: for Japanese, Hong Kong, or Australian racing you need a scraper built on those sites.

Feature ParseForge jungle_synthesizer alaricus 0xgollum
Source Sporting Life (UK, IRE, FR, US) Equibase (US) HKJC (Hong Kong) Mixed feeds
Runner-level rows with weights and ratings Yes Yes Yes Partial
Timeform commentary and race verdict Yes No No No
Live bookmaker prices, multiple firms Optional No No No
Tote, exacta, trifecta, placepot returns Yes Partial Yes No
Full form history per runner Optional Optional No No
Horse, jockey, and trainer profile rows Yes Yes No No
Historical archive depth Back to early 2005 Recent Recent Recent
Price per 1,000 runners $7.00 ~$1.00 ~$15.00 Not published

The per-runner price is the honest headline: this Actor costs about seven times the cheapest US racing scraper on the Store. What the difference buys is the columns above: Timeform's written analysis, live multi-bookmaker prices, tote returns, and a two-decade archive: on a feed that never needs a proxy. If your job is bulk US chart data at the lowest unit price, use theirs.

What a runner row looks like

{
  "recordType": "runner",
  "meetingId": 120938,
  "meetingDate": "2026-08-24",
  "courseId": 301,
  "courseName": "Brighton",
  "countryCode": "ENG",
  "countryName": "England",
  "meetingGoing": "Good to Firm (Good in places)",
  "meetingSurface": "Turf",
  "weather": "Fine",
  "raceId": 934627,
  "raceName": "Blackmore Building Contractors Handicap",
  "raceUrl": "https://www.sportinglife.com/racing/results/2026-08-24/brighton/934627/blackmore-building-contractors-handicap",
  "raceDate": "2026-08-24",
  "raceTime": "13:30",
  "offTime": "13:30:12",
  "raceType": "FLAT",
  "raceClass": "5",
  "ageBand": "4YO plus",
  "distance": "5f 60y",
  "distanceYards": 1160,
  "surface": "TURF",
  "going": "Good to Firm (Good in places)",
  "isHandicap": true,
  "raceStage": "WEIGHEDIN",
  "declaredRunners": 6,
  "winningTime": "1m 0.75s",
  "winningTimeSeconds": 60.75,
  "verdict": "SOUTH SHORE has made a positive start for his present stable...",
  "firstPrize": 4187,
  "prizeCurrency": "GBP",
  "totalPrize": 7870,
  "placesPaid": 2,
  "bookPercentage": 111.12,
  "toteWin": "5.0 GBP",
  "toteExacta": "14.2 GBP",
  "placepot": "136.8 GBP",
  "rideId": 251626285,
  "clothNumber": 1,
  "drawNumber": 5,
  "finishPosition": 1,
  "finishDistance": null,
  "isWinner": true,
  "isPlaced": true,
  "horseId": 1165390,
  "horseName": "Em Four",
  "horseUrl": "https://www.sportinglife.com/racing/profiles/horse/1165390",
  "horseAge": 4,
  "horseSexLabel": "Gelding",
  "foaledDate": "2022-03-28",
  "daysSinceLastRun": 35,
  "formSummary": "135785",
  "weight": "9-9",
  "weightPounds": 135,
  "officialRating": 74,
  "headgear": [],
  "ownerName": "Mr J A Osborne",
  "trainerId": 197,
  "trainerName": "J A Osborne",
  "jockeyId": 29850,
  "jockeyName": "Saffie Osborne",
  "odds": "4/1",
  "oddsDecimal": 5,
  "impliedProbability": 20,
  "openingOdds": "7/2",
  "oddsHistory": ["4/1", "7/2", "10/3", "7/2"],
  "betMovement": "tchd 10/3",
  "isFavourite": false,
  "marketRank": 3,
  "lifetimeRuns": 33,
  "lifetimeWins": 7,
  "lifetimeWinPercent": 21.2,
  "commentary": "Won 3 times on all-weather earlier in the year and arrives in good heart...",
  "runningComment": "Mounted in chute, wore hood and went early to post, midfield, progress over 2f out...",
  "silkUrl": "https://www.sportinglife.com/images/silks/20260824bgh143001.png",
  "fieldSize": 6,
  "scrapedAt": "2026-08-25T16:44:43.346Z"
}

Upcoming races have no finishPosition, winningTime, or tote returns yet, and settled races no longer carry live bookmaker prices. raceType is stated by the feed only once a race has run, so it is null on tomorrow's card. Those are the data's real state, not gaps in the parser.

Configure the run

Drive the Actor from four source types, alone or together: a date range, Sporting Life URLs, race IDs, and horse, jockey, or trainer IDs. Filters run as each race and runner is read, so only matches reach your dataset. With nothing set it scrapes today's racecards. The Input tab lists every parameter.

Today's card for two courses:

{ "dateFrom": "2026-08-26", "courses": ["Ascot", "Newmarket"], "maxItems": 300 }

A week of results, with each horse's form history attached:

{ "dateFrom": "2026-08-01", "dateTo": "2026-08-07", "raceStages": ["WEIGHEDIN"], "includeHorseForm": true, "maxItems": 5000 }

Tomorrow's Irish card with every bookmaker's price:

{ "dateFrom": "2026-08-27", "countries": ["Eire"], "includeBookmakerOdds": true, "maxItems": 400 }

One trainer's runners over a season, winners only:

{ "dateFrom": "2026-04-01", "dateTo": "2026-09-30", "trainerNameContains": ["Appleby"], "finishPositions": ["winner"], "maxItems": 2000 }

A single race by URL, with replay and last year's winners:

{ "startUrls": ["https://www.sportinglife.com/racing/racecards/2026-08-26/catterick/racecard/934971/berber-leather-maiden-stakes"], "includeVideos": true, "includeLastYearsWinners": true }

Pricing

Pay-per-event: $0.007 per runner, plus a $0.01 fee per race day scanned and a $0.03 run-start fee. You pay only for rows written to your dataset. Every optional block is a separate event that is charged only when you tick its checkbox.

Runners collected Approximate cost
100 $0.74
1,000 $7.04
10,000 $70.13

Choosing outputMode: races bills $0.05 per race instead, with the whole field nested in one row. On an average nine-runner card that is cheaper than per-runner billing; on a five-runner field it is dearer. outputMode: meetings bills $0.02 per meeting.

Optional blocks: bookmaker odds $0.003 per runner, form history $0.003 per runner, horse profile and pedigree $0.008 per horse, race replay $0.003 per race with a direct MP4, tipster nap $0.003 per row, article $0.004 per row. Standalone jockey and trainer profile rows are $0.008 each.

New Apify accounts start with $5 in free credit.

Free users

Free-plan runs return up to 10 rows as a preview. Upgrade your Apify plan to collect up to 1,000,000 rows per run.

Run it

  1. Create a free Apify account with $5 in credit.
  2. Open the Sporting Life Racing Scraper.
  3. Set dateFrom and dateTo, or paste Sporting Life URLs, add any filters, and click Start.
  4. Export the results as CSV, Excel, JSON, or XML from the Dataset tab.

Run it programmatically through the Apify API or the ApifyClient for JavaScript and Python.

Use with AI agents (MCP)

Give an AI agent live access to racing data through the Model Context Protocol. Add the Actor to Claude, Cursor, or any MCP client:

claude mcp add --transport http apify "https://mcp.apify.com?tools=parseforge/sporting-life-racing-scraper"

Then prompt it in plain language:

  • "Pull today's Ascot card and list every runner with its price and official rating."
  • "Get last Saturday's British results and tell me which favourites won."
  • "Compare bookmaker prices for tomorrow's 15:30 at Newmarket."

Copy this into ChatGPT, Claude, or Cursor to start:

Use the Apify Actor "parseforge/sporting-life-racing-scraper" to collect horse racing data from Sporting Life. Input: { "dateFrom": "<YYYY-MM-DD>", "dateTo": "<YYYY-MM-DD>", "courses": ["<course>"], "countries": ["<country>"], "includeBookmakerOdds": <true|false>, "maxItems": <n> }. It returns one row per runner with horse, age, weight, official rating, trainer, jockey, odds, Timeform commentary, finishing position, and race and meeting context. Call it with the ApifyClient and my APIFY_TOKEN.

Troubleshooting

Why am I getting no results?

Check the date. There is no racing on some days, and racecards are only published about five days ahead, a date further out returns nothing but the odd early entry for a big autumn race. Filters stack, so a course plus a class plus a going term can leave zero races; drop them one at a time.

Why are the bookmaker odds empty?

Prices are live markets with a narrow window. Firms price up on the morning of the race, so a card pulled the night before returns SP from every firm and no usable price, that costs you nothing, because the block only bills when a real price is delivered. Once a race is settled the market is gone. Use the starting price in odds and oddsDecimal for past races.

Why is a replay URL missing?

Sporting Life carries two video providers. Racing TV courses ship a direct MP4, which lands in replayUrl. At The Races courses ship only a host-less provider path, which lands in replayStreamPath and is never billed. About 45% of replays are direct MP4s.

Why is raceType null?

Sporting Life states flat, hurdle, or chase only in a horse's own form lines, which means it resolves once a race has been run. Tomorrow's card does not have it yet.

Why fewer rows than I asked for?

maxItems is a ceiling, not a target. The day may hold fewer runners than that, or your filters removed them. Widen the date range or relax the filters.

Why is the run slow?

includePedigree and includeFutureEntries fetch one profile per horse, which multiplies requests on a big card. Turn them off for bulk runs, or lower racesPerDay.

A field stopped filling.

Sporting Life changed its public response shape. Email us with your run ID so we can update the parser.

FAQ

Question Answer
Do I need a Sporting Life account or API key? No. It reads the public racing feed, so there is no login, cookie, or token to manage.
Which countries are covered? Britain, Ireland, France, and a United States selection, as published on Sporting Life's own card listing.
How far back do results go? To early 2005, solid from that April. Racecards run about five days into the future.
Can I get odds from more than one bookmaker? Yes. Tick includeBookmakerOdds and each runner returns every firm Sporting Life carries, plus the best price and who offers it.
Can I download race replays? Partly. Tick includeVideos: Racing TV courses return a direct MP4 URL, At The Races courses return only a provider path with no host. You are billed only for the usable MP4s.
Does it return the full form history? Yes, with includeHorseForm: each previous run's date, course, distance, going, weight, rating, starting price, and the in-running comment.
Can I filter by trainer, jockey, or owner? Yes, by partial name. Filters run before rows are written, so only matches are billed.
Is a proxy needed? No. The feed answers anonymous datacenter requests, so the proxy field can stay off.
How many rows per run? Free plan: 10. Paid: up to 1,000,000, bounded by what the cards and filters return.
What is marketRank? The runner's price position in its own race, 1 being the shortest. Computed here, not published by Sporting Life.
How is the favourite decided? Sporting Life flags it only once a race has run. On an upcoming card isFavourite falls back to the shortest price, which agreed with the flag on every settled race tested. Joint favourites all get the flag.
Is this an official Sporting Life product? No. It is unofficial and reads only publicly available Sporting Life data.

Related actors

Browse the full ParseForge collection for more scrapers.

๐Ÿ†˜ Need help? Email parseforge@protonmail.com with your run ID, your input, and what you expected.

โš ๏ธ Disclaimer. This Actor is unofficial and is not affiliated with, endorsed by, or sponsored by Sporting Life, Sky Betting & Gaming, or Timeform. It collects only publicly available Sporting Life data. You are responsible for using the data in compliance with Sporting Life's terms and applicable laws, including GDPR, CCPA, and PIPL. Gambling carries risk; nothing here is betting advice.

Input

FieldTypeWhat it doesDefault
dateFrom string First race day to collect, as YYYY-MM-DD. Racecards are published about five days ahead and results go back to early 2005. Leave both dates empty to scrape today. Example: 2026-08-24 not set
dateTo string Last race day to collect, as YYYY-MM-DD, inclusive. Leave empty to use the same day as Date from. Example: 2026-08-26 not set
startUrls array Any sportinglife.com racing URL: a racecard, a result, a race-day listing, or a horse, jockey, or trainer profile. Each one is recognised automatically. Examples: - https://www.sportinglife.com/racing/racecards/2026-08-26/catterick/racecard/934971/berber-leather-maiden-stakes - https://www.sportinglife.com/racing/results/2026-08-24 - https://www.sportinglife.com/racing/profiles/horse/em-four/1165390 not set
raceIds array Numeric Sporting Life race IDs to fetch in full, without going through a race-day listing. Example: 934971 not set
horseIds array Numeric horse IDs. Each one returns a profile row with pedigree, career record, full form, and upcoming entries. Example: 1165390 not set
jockeyIds array Numeric jockey IDs. Each one returns a profile row with recent rides, strike rate, and upcoming bookings. Example: 29850 not set
trainerIds array Numeric trainer IDs. Each one returns a profile row with recent runners, strike rate, and upcoming entries. Example: 197 not set
outputMode string (3 options) What one dataset row represents. Runner is the flat, spreadsheet-friendly shape and the default. runners
maxItems integer Total rows to write in this run. Free Apify plans return a 10-row preview. 100
racesPerDay integer Cap the races taken from each day's card so one busy Saturday cannot use up the whole run. Leave empty to take every race that passes the filters. not set
countries array Keep only meetings in these countries. Matches the country name or its short code, case-insensitive. Available: England, Scotland, Wales, Eire, Northern Ireland, France, United States, plus other international cards the feed carries. not set
courses array Keep only these racecourses. Full or partial name, case-insensitive. Examples: - Ascot - Newmarket - Cheltenham not set
surfaces array Keep only races run on these surfaces. not set
raceClasses array Keep only these British race classes. Class 1 is the highest. Irish, French, and US races usually carry no class. not set
raceStages array Keep only races at these stages. DORMANT is an upcoming race, OFF is running, WEIGHEDIN and RESULT are settled. not set
handicapFilter string (3 options) Keep handicaps only, non-handicaps only, or both. any
raceNameContains array Keep only races whose name contains at least one of these terms. Case-insensitive. Examples: - Hurdle - Chase - Group 1 - Maiden not set
goingContains array Keep only races whose going description contains at least one of these terms. Case-insensitive. Examples: - Good to Firm - Soft - Standard not set
minRunners integer Drop races with fewer declared runners than this. not set
maxRunners integer Drop races with more declared runners than this. not set
minPrize number Drop races whose first-place prize is below this. The number is in the race's own currency (GBP, EUR, or USD), which is reported as prizeCurrency. not set
timeFrom string Keep only races off at or after this local course time, as HH:MM. Example: 14:00 not set
timeTo string Keep only races off at or before this local course time, as HH:MM. Example: 18:30 not set
horseNameContains array Keep only runners whose horse name contains at least one of these terms. Case-insensitive. not set
trainerNameContains array Keep only runners from trainers whose name contains at least one of these terms. Case-insensitive. Example: Appleby not set
jockeyNameContains array Keep only runners ridden by jockeys whose name contains at least one of these terms. Case-insensitive. Example: Moore not set
ownerNameContains array Keep only runners whose owner name contains at least one of these terms. Case-insensitive. Example: Godolphin not set
minOfficialRating integer Drop runners rated below this by the handicapper. Unrated horses are dropped when this is set. not set
maxOfficialRating integer Drop runners rated above this by the handicapper. not set
minAge integer Drop runners younger than this, in years. not set
maxAge integer Drop runners older than this, in years. not set
minOddsDecimal number Drop runners priced shorter than this, in decimal odds including the stake. Accepts decimals: 3.5 means 5/2 or bigger. not set
maxOddsDecimal number Drop runners priced longer than this, in decimal odds including the stake. Accepts decimals: 6.5 means 11/2 or shorter. not set
favouritesOnly boolean Keep only the favourite in each race. Sporting Life flags the favourite only once a race has run, so on an upcoming card the shortest price in the race is used instead. Joint favourites all match. Verified to agree with the flag on every settled race tested. false
finishPositions array For settled races, keep only runners with these outcomes. Leave empty to keep every runner. not set
includeNonRunners boolean Keep withdrawn horses as rows, flagged with isNonRunner. Off by default so the dataset holds only horses that actually take part. false
includeBookmakerOdds boolean Add live prices from every bookmaker Sporting Life carries, plus the best available price and its bookmaker, to each runner. One extra request per race. Firms price up on the morning of the race: before that they answer SP and no price is returned or billed, and once a race is settled the market is gone. false
includeHorseForm boolean Add each horse's previous runs as a nested form array: date, course, distance, going, position, weight, official rating, starting price, and the in-running comment. false
includePedigree boolean Add sire, dam, damsire, and coat colour to each runner. Costs one profile request per horse, so it makes big runs noticeably slower. false
includeFutureEntries boolean Add the races each horse is already entered for, with date, course, distance, and race name. Reads the horse profile, so it costs one request per horse and shares that request with Pedigree. false
includeVideos boolean Add the race replay and its caption. Racing TV courses return a direct MP4 in replayUrl; At The Races courses return only a host-less provider path in replayStreamPath, which is reported but never billed. false
includeLastYearsWinners boolean Add the previous runnings' winners of the same race, with trainer, jockey, weight carried, and starting price. false
includeTipsterNaps boolean Also write the naps table: every national tipster's nap of the day with their running wins, losses, and profit to level stakes. false
includeArticles boolean Also write Sporting Life's racing tips and news articles with title, author, snippet, and publish date. false
maxConcurrency integer How many race cards to fetch at once. The default suits every run; lower it only if you are routing through a slow proxy. 12
proxyConfiguration object Optional. The site does not block datacenter traffic, so leaving this off costs you nothing and saves the proxy line on your bill. {"useApifyProxy":false}

Pricing

from $7.00 per 1,000 results

Charged forWhat it isPrice each
Actor Start Charged when the Actor starts running. Number of events charged depends on Actor memory (one event per GB, minimum one event). $0.03
Runner One horse in one race, with its meeting, race, market and result context. $0.007
Race with field nested One race with every runner nested inside it. $0.05
Meeting with card nested One race day at one course with its card nested inside it. $0.02
Race day scanned One day of meetings and races listed and filtered. $0.01
Bookmaker odds Optional. Live prices from every bookmaker on a runner. Not charged before a market is priced up. $0.003
Form history Optional. A runner's previous races with going, weight, rating and comment. $0.003
Horse profile and pedigree Optional. Sire, dam, damsire, colour, external IDs and upcoming entries. $0.008
Jockey profile A jockey row with recent rides, strike rate and upcoming bookings. $0.008
Trainer profile A trainer row with recent runners, strike rate and upcoming entries. $0.008
Race replay Optional. The replay MP4 URL for a settled race. $0.003
Tipster nap Optional. One tipster nap with its running wins, losses and profit. $0.003
Tips or news article Optional. One racing tips or news article with author and publish date. $0.004

Tiered: the lower figure is the price on a higher Apify plan. Billing and the free credit live on Apify.

API

One POST returns the dataset directly. Same shape for every scraper in the library, so swapping the slug is the only change.

POST ยท run and get results
curl -X POST "https://api.apify.com/v2/acts/parseforge~sporting-life-racing-scraper/run-sync-get-dataset-items?token=$APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "helloWorld": 123
  }'

Examples

Input that runs as-is.

input.json
{
  "helloWorld": 123
}

Reviews

No reviews yet. Be the first.

Issues

We build and maintain this scraper, so a problem with it comes to us. Report it on the Apify listing and the thread stays attached to the scraper where the next person can find it: open an issue.

Broken and urgent, or you would rather not post in public? Write to parseforge@protonmail.com and it reaches the people who wrote it.

Related scrapers

Run Sporting Life Scraper - Racecards, Results & Odds API on Apify All scrapers