Fantasy
The Fantasy API turns a RaceHooks race weekend into fantasy points. It gives you an estimated points breakdown per driver for any completed Grand Prix or Sprint, a running score while a session is on track, and a pit-lane leaderboard — all keyed to the same canonical driver identity (TLA and team) as every other RaceHooks surface. Pair it with the drop-in fantasy scorer and you have a live leaderboard from race-event webhooks.
https://api.racehooks.io/v1403 Forbidden. See plans & limits for the full tier matrix, and the pricing page to upgrade.What you get
Three endpoints, all authenticated with a Bearer token and versioned under /v1:
| Field | Type | Description |
|---|---|---|
races/:raceId/scores | GET · Developer+ | Estimated fantasy points per driver for a completed race. Grand Prix by default; ?session=sprint scores the Sprint. Returns a full per-driver breakdown. |
session/:sessionId/scores | GET · Developer+ | Running (provisional) fantasy score for each driver while a session is live, computed from the current running order and retirements. |
session/:sessionId/pit-times | GET · Developer+ | Pit-lane time leaderboard for a session, ranked fastest → slowest, with the fastest stop called out. |
The scoring model
The race and session endpoints return RaceHooks' own estimated fantasy model — a transparent, deterministic points system computed from finishing position, qualifying, positions gained, fastest lap, teammate comparison, and retirements. Every entry ships the full breakdownso you can show your users exactly where each point came from, or re-map the components onto your own league's rules. (To score against official-style, DFS-style, or fully custom rule sets instead, use the fantasy scorer further down.)
Grand Prix finishing points
| Finish | Points |
|---|---|
| P1 | 25 |
| P2 | 18 |
| P3 | 15 |
| P4 | 12 |
| P5 | 10 |
| P6 | 8 |
| P7 | 6 |
| P8 | 4 |
| P9 | 2 |
| P10 | 1 |
| P11+ | 0 |
Sprint finishing points
A Sprint scores a compressed P1–P8 table. Qualifying, fastest-lap and quali-teammate components do not apply to a Sprint (their breakdown fields come back null).
| Finish | Points |
|---|---|
| P1 | 8 |
| P2 | 7 |
| P3 | 6 |
| P4 | 5 |
| P5 | 4 |
| P6 | 3 |
| P7 | 2 |
| P8 | 1 |
| P9+ | 0 |
Qualifying points
Grand Prix scoring adds qualifying position points (P1 = 10 down to P10 = 1, −1 per place) plus a segment bonus:
| Field | Type | Description |
|---|---|---|
qualiPosPoints | P1–P10 | 10 points for pole, decreasing by one per place to 1 point at P10. P11+ scores 0. |
q3Bonus | +3 / +2 | +3 for reaching the final qualifying segment (P1–P10); +2 for the middle segment (P11–P15). |
Bonuses & penalties
| Field | Type | Description |
|---|---|---|
positionsGainedPoints | +2 / place | Two points for every position gained between the starting grid and the finish. Only net gains score; positions lost do not deduct. |
fastestLapPoints | +5 | Awarded to the driver credited with the race's fastest lap. Grand Prix only. |
beatTeammateRace | +3 | Finishing ahead of a running teammate. |
beatTeammateQuali | +2 | Out-qualifying a teammate. Grand Prix only. |
dnfPenalty | −15 | Applied to a retirement, disqualification, or a did-not-start (DNF / DSQ / DNS). |
Reading the breakdown object
Every driver entry carries a breakdown with the same shape on both the race and live endpoints. A component is null when it does not apply (Sprint, or a live session before the flag) and 0 when it applies but scored nothing.
| Field | Type | Description |
|---|---|---|
racePosPoints | integer | Finishing-position points from the table above (Grand Prix or Sprint scale). |
qualiPosPoints | integer | null | Qualifying-position points. null when no qualifying result is available, or on a Sprint / live session. |
q3Bonus | integer | null | Qualifying-segment bonus (+3 / +2 / 0). null when qualifying data is unavailable. |
positionsGained | integer | null | Raw places gained (grid − finish); can be negative. null when grid or finish is unknown. |
positionsGainedPoints | integer | Points awarded for net positions gained (positionsGained × 2, gains only). |
fastestLapPoints | integer | null | +5 if this driver set the fastest lap, else 0. null on a Sprint / live session. |
beatTeammateRace | integer | +3 if finishing ahead of a running teammate, else 0. |
beatTeammateQuali | integer | null | +2 if out-qualifying a teammate, else 0. null on a Sprint / live session. |
dnfPenalty | integer | −15 on a DNF / DSQ / DNS, else 0. |
total | integer | Sum of every component above — the driver's fantasy score for the session. |
Race scores
/v1/fantasy/races/:raceId/scoresDEVELOPER+Estimated fantasy points for a completed session, sorted highest total first. Pass ?session=sprint to score the Sprint instead of the Grand Prix; the response sessionType (grand_prix or sprint) echoes which was scored. If a race has no qualifying data yet, qualiPosPoints and q3Bonus come back null and a dataQuality note is included.
| Field | Type | Description |
|---|---|---|
raceId | string (path) | Race id slug, e.g. 2026-italy-r16. See the Historical API for how race ids are formed. |
session | string (query) | "race" (default) scores the Grand Prix; "sprint" scores the Sprint using the compressed points table. |
Live in-session scores
/v1/fantasy/session/:sessionId/scoresDEVELOPER+A running, provisional score for each driver while a session is on track, derived from the live running order and retirements. Position, teammate and DNF components update lap by lap; the grid-derived (positions gained, qualifying) and fastest-lap components are only finalised once the session ends — until then they are null. For the final breakdown, call the race scores endpoint after the flag.
| Field | Type | Description |
|---|---|---|
sessionId | string (path) | Session id, e.g. 2026-italy_r or 2026-italy_s. |
live | boolean | true while a session is genuinely live for this id; false (with an empty scores list and a message) otherwise. |
provisional | boolean | Always true for this endpoint — scores are in-flight and will change. |
currentLap | integer | Current lap of the session. |
totalLaps | integer | Scheduled total laps. |
Push: the fantasy.scores feed
fantasy.scoresliveThe same provisional scores as the endpoint above, pushed to your webhook or SSE stream instead of polled. Both come from one scoring function, so a pushed frame and a polled response never disagree about a driver's points.
It emits on scoring change, not on a timer — a lap counter ticking over is not by itself a delivery. A position swap, a driver leading their teammate, or a retirement is. That keeps a race in the low hundreds of deliveries rather than the ~2,700 an every-tick feed would bill you for. Fires during Race and Sprint sessions only: practice and qualifying have no finishing-position table.
| Field | Type | Description |
|---|---|---|
feed | string | Always "fantasy.scores". |
sessionId | string | The session these scores belong to — the same id you would pass to the REST routes above. |
utc | string | When the running order behind these scores was observed (ISO-8601), not when the frame was serialised. |
live | boolean | Always true — the feed only fires during a running session. |
provisional | boolean | Always true. Grid-derived and fastest-lap components stay null until the flag; call the race scores endpoint after the session for the final breakdown. |
sessionType | string | "grand_prix" or "sprint" — the sprint scores on the compressed points table. |
scores | array | Per-driver entries, sorted by total descending. Each carries a full driver reference and the same breakdown object the REST routes return. |
scores[].driver | object | Full driver reference — driverId, constructorId, number, tla, name, team. |
Pit-stop time leaderboard
/v1/fantasy/session/:sessionId/pit-timesDEVELOPER+Stationary pit-stop times for a session (the seconds the car is stopped in its box), ranked fastest to slowest, with the single fastest stop broken out as fastestStop. Stationary pit-stop time is the constructor scoring category in most fantasy games — feed it straight into a team leaderboard. This is the same pit picture surfaced by the strategy & tyre feeds.
| Field | Type | Description |
|---|---|---|
sessionId | string (path) | Session id, e.g. 2026-italy_r. |
source | "live" | "historical" | Where the ranking came from — an in-progress session vs. the completed record. |
fastestStop | object | null | The fastest single stop of the session (same shape as a stops[] entry), or null when none is recorded. |
stops[].pitStopTimeSec | number | Stationary pit-stop time in seconds (car stopped in its box). stops[] also carries driver (id slug), tla, team, and lap. |
Build a fantasy app
For a live leaderboard driven by your own scoring rules, subscribe to the events.race feed and pipe its payloads into the open-source racehooks-f1-fantasy-scorer package. The scorer is zero-dependency, fully typed, and deterministic — every point is logged with a reason code — and ships official-style, DFS-style, and custom rule sets, plus a budget-aware roster optimizer.
1. Install
2. Subscribe a roster and wire the scorer
Create one events.race webhook filtered to your league's drivers, and point a FantasyScorer at the same roster:
3. Score deliveries as they arrive
Verify each delivery's signature, then hand the payload to scorer.ingest() — it routes each event to the right handler and finalises the classification automatically when the session completes:
events.race payloads it would in a live race, so you can validate a full weekend of scoring against a known result on a quiet afternoon.How race events map to scoring
The scorer draws its points from these events.race events. Two of them are inputs the scorer accepts but RaceHooks does not emit — they are marked below, and the scorer works without them (see src/events.ts in the package for the same note).
| Field | Type | Description |
|---|---|---|
driver_finished | event (not emitted) | Finishing-position points, plus positions gained or lost against the starting grid. RaceHooks emits no canonical driver_finished event — the scorer derives the final classification from the last timing positions when session.complete fires, so this is only needed if you synthesise one upstream. |
session.complete | event | Triggers final classification from the last observed order (the scorer's finalize step). |
overtake | event | Overtake points (handles cumulative overtake counts). |
fastest.lap | event | Fastest-lap bonus, for rule sets that award one. |
pit.stop.complete | event | Constructor pit-stop time points, plus fastest-stop and record-stop bonuses. |
retirement | event | DNF penalty — race vs. sprint aware. |
driver_of_the_day | event (not emitted) | Driver of the Day bonus. Not a live RaceHooks event — Driver of the Day is a post-race fan vote, ingested from F1DB after the session rather than emitted on events.race. Supply it yourself if your rule set awards it. |