Cycling Races, Riders & Results
Pricing
from $3.62 / 1,000 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
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
15 days ago
Last modified
Categories
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.
| Mode | You give it | You get |
|---|---|---|
calendar | a year, optionally a circuit and a date window | one row per race |
startlist | one or more races | one row per rider entered |
results | one or more races and a stage | one row per finisher / non-finisher |
rider | rider URLs and/or names | one profile row per rider |
rankings | ranking type and year | one 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.
| Field | Modes | Meaning |
|---|---|---|
raceName, race | calendar, startlist, results | Race title |
raceUrl, raceSlug, year | calendar, startlist, results | Canonical race link, slug and season |
startDate, endDate | calendar | ISO dates |
country, countryCode | calendar | Host country |
category | calendar | UCI class, for example 2.UWT |
winnerName, winnerUrl | calendar | Winner when the race is finished |
stagesCount | calendar | Only with includeStagesCount |
bibNumber | startlist, results | Race number |
riderName, givenName, surname, riderUrl, riderSlug | startlist, results | Rider as listed (surname in capitals) |
team, teamUrl, teamClass | startlist, results, rider, rankings | Team of the race / season |
nationality, nationalityCode | startlist, results, rider, rankings | Country name and ISO-style code |
statusNote | startlist | Text such as DNF #18 when the page shows one |
stage, stageName | results | gc, result, 5, ... and the title of that stage |
rank, status | results | Place (null when not classified) and FINISHED, DNF, DNS, OTL, ... |
age | results, rider | Age at the time of the race / today |
timeText, gapText | results | Finishing time and gap, as written on the site. Same-time riders repeat the gap above them |
points, uciPoints | results | Ranking points awarded (site points and UCI points) |
name, dateOfBirth, placeOfBirth, weightKg, heightM | rider | Profile |
specialtyPoints | rider | Object with oneday, gc, timeTrial, sprint, climber, hills |
pcsRanking, allTimeRanking, uciRanking, uciPoints, careerWins | rider | Ranking places and win count when shown |
profileUrl | rider | Rider page |
rankingType, scope, previousRank, points, name, url | rankings | One ranking place |
error, errorMessage, url | any | Error 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.
maxItemsis 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 olderrace/<slug>-<year>-gcform copied from calendar links are all accepted. A bare slug needs theyearinput. - 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 installmkdir -p storage/key_value_stores/defaultecho '{"mode":"calendar","year":2026,"circuit":"1","maxItems":10}' > storage/key_value_stores/default/INPUT.jsonCRAWLEE_STORAGE_DIR=./storage npm start
Rows land in storage/datasets/default.