Cycling Races, Riders & Results avatar

Cycling Races, Riders & Results

Pricing

from $3.62 / 1,000 results

Go to Apify Store
Cycling Races, Riders & Results

Cycling Races, Riders & Results

Professional cycling data: race calendar, startlists, stage and GC results, rider profiles with specialty points, and UCI/team/nation rankings. Export to JSON, CSV or Excel.

Pricing

from $3.62 / 1,000 results

Rating

0.0

(0)

Developer

Scraper Master

Scraper Master

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

15 days ago

Last modified

Share

Turns the public pages of ProCyclingStats (procyclingstats.com) into clean, structured rows: the season calendar, who starts a race, who finished where, a rider's profile and specialty scores, and the current or past rankings. No login, no API key, no browser: it reads plain HTML pages, politely.

What it does

One Actor, five modes. Pick the mode with the mode input; every mode writes to the same dataset and charges one result-item event per row delivered. Pages that fail produce a separate error row (not charged) instead of stopping the run.

ModeYou give itYou get
calendara year, optionally a circuit and a date windowone row per race
startlistone or more racesone row per rider entered
resultsone or more races and a stageone row per finisher / non-finisher
riderrider URLs and/or namesone profile row per rider
rankingsranking type and yearone row per ranking place

Example inputs

Calendar

{ "mode": "calendar", "year": 2026, "circuit": "1", "startDate": "2026-06-01", "endDate": "2026-08-31", "maxItems": 50 }

Circuits: 1 UCI WorldTour, 24 Women's WorldTour, 26 ProSeries, 13 Europe Tour, 12 Asia Tour, 11 Africa Tour, 14 Oceania Tour, 18 America Tour, 2 World Championships, 23 National Championships, 16 Women Elite, 15 / 17 Junior. Empty means all circuits. Set includeStagesCount to true to also count stages (one extra request per race).

Startlist

{ "mode": "startlist", "raceUrls": ["race/tour-de-france/2026"], "maxItems": 200 }

Results

{ "mode": "results", "raceUrls": ["race/tour-de-france/2025"], "stage": "gc", "maxItems": 50 }

stage can be gc (final classification; for one-day races this is the race result), a stage number such as 5, prologue, or all for every stage in turn.

Rider

{ "mode": "rider", "riderNames": ["Tadej Pogačar"], "riderUrls": ["rider/remco-evenepoel"] }

Rankings

{ "mode": "rankings", "rankingType": "team", "year": 2025, "maxItems": 30 }

rankingType: individual, team, nations or uciIndividual (current UCI table only). rankingScope is season (points of the chosen year) or rolling (the live rolling ranking, year ignored).

Output fields

Fields that do not apply to a mode are simply absent. Every row has mode and scrapedAt.

FieldModesMeaning
raceName, racecalendar, startlist, resultsRace title
raceUrl, raceSlug, yearcalendar, startlist, resultsCanonical race link, slug and season
startDate, endDatecalendarISO dates
country, countryCodecalendarHost country
categorycalendarUCI class, for example 2.UWT
winnerName, winnerUrlcalendarWinner when the race is finished
stagesCountcalendarOnly with includeStagesCount
bibNumberstartlist, resultsRace number
riderName, givenName, surname, riderUrl, riderSlugstartlist, resultsRider as listed (surname in capitals)
team, teamUrl, teamClassstartlist, results, rider, rankingsTeam of the race / season
nationality, nationalityCodestartlist, results, rider, rankingsCountry name and ISO-style code
statusNotestartlistText such as DNF #18 when the page shows one
stage, stageNameresultsgc, result, 5, ... and the title of that stage
rank, statusresultsPlace (null when not classified) and FINISHED, DNF, DNS, OTL, ...
ageresults, riderAge at the time of the race / today
timeText, gapTextresultsFinishing time and gap, as written on the site. Same-time riders repeat the gap above them
points, uciPointsresultsRanking points awarded (site points and UCI points)
name, dateOfBirth, placeOfBirth, weightKg, heightMriderProfile
specialtyPointsriderObject with oneday, gc, timeTrial, sprint, climber, hills
pcsRanking, allTimeRanking, uciRanking, uciPoints, careerWinsriderRanking places and win count when shown
profileUrlriderRider page
rankingType, scope, previousRank, points, name, urlrankingsOne ranking place
error, errorMessage, urlanyError rows only

Good to know

  • Speed and manners. At most 3 requests in flight (default 2), a short random pause before each, a regular browser User-Agent, and exponential backoff on 429 and 5xx answers. Large all-stage runs take a while by design.
  • Costs. maxItems is a hard cap. If the run has a maximum charge, the Actor stops as soon as that budget is spent. Error rows are not billed.
  • Race references. race/<slug>/<year>, the full URL, and the older race/<slug>-<year>-gc form copied from calendar links are all accepted. A bare slug needs the year input.
  • Team time trials. The site lists these per team, so every rider of the team gets one row carrying the team's rank, time and gap. Bib, age and points are not available there.
  • Times. Time trials keep hundredths (36:42,02). For non-winners the time is computed from the winner's time plus the listed gap.
  • Rankings by year. Past seasons use the end-of-year table. Individual tables hold 100 places per page, and the Actor pages through them up to your maxItems.
  • Rider names. A name becomes a profile address by simple transliteration; if a rider is not found, paste the profile URL instead.
  • Not covered. Women's race pages work only where they follow the same layout; classification jerseys (points, mountains, youth, teams) are not extracted, only the main table.
  • Data belongs to its source; use it in line with the source site's terms and applicable law.

Run locally

npm install
mkdir -p storage/key_value_stores/default
echo '{"mode":"calendar","year":2026,"circuit":"1","maxItems":10}' > storage/key_value_stores/default/INPUT.json
CRAWLEE_STORAGE_DIR=./storage npm start

Rows land in storage/datasets/default.