Skip to content
GRUDGED Free audit
§ Field notes for developers ·

iRacing's Recent Races Endpoint Only Ever Returns Ten

member_recent_races caps at 10 per driver, so a new member looks like a rookie forever. Backfilling means a different endpoint — and a position convention that flips.

I run the results archive for a sim racing team — every race the team enters, per driver, with career totals and a record book on top. The collector sweeps iRacing’s data API every thirty minutes and upserts what it finds.

Then the team grew, and the new drivers all looked like they’d barely raced.

The cap

stats/member_recent_races returns the last ten races for that member. Never more. There’s no paging parameter, no offset, no date range that widens it. Ten is the endpoint.

That’s fine for a driver you’ve been sweeping for months — my own row count was in the hundreds, but only because half a year of thirty-minute polls had accumulated history the endpoint never would have handed over in one go. Every newly added member starts permanently stuck at ten, and stays there until they race an eleventh time, at which point the first one falls off the end.

So the collector wasn’t broken. It was doing exactly what it was told, and the data model quietly depended on having been running since before the drivers joined. That’s a nasty property: it works perfectly in production and fails for every new member, forever.

Backfilling means a different endpoint

The history does exist, via results/search_series — full official-series results for a cust_id, requested one season_year + season_quarter at a time, with event_types=5 and official_only=true.

Two structural differences from the recent-races endpoint:

  • The response is not the data. You get {"data": {chunk_info}} pointing at pre-signed S3 chunk files, which you then fetch without your auth cookie. Same pattern as results/lap_data.
  • You have to know which quarters to ask for. Scanning every quarter since the driver joined mostly returns nothing. Their yearly stats give you the years with starts greater than zero, so you only walk the quarters that can contain something.

The trap that makes the data look wrong

This one cost me the most, because it produces plausible numbers rather than obvious errors:

search_series returns positions that are 0-based and measured against the overall field. member_recent_races returns 1-based, in-class.

Both. At once. So a multi-class race where a driver finished ninth in GT3 comes back from one endpoint as 9 and from the other as 20 — and if you write both into the same column, that driver’s average finish and podium count are now silently wrong, and wrong in a direction that depends on which endpoint happened to supply each row.

The conversion is finish_position_in_class + 1 and starting_position_in_class + 1 on the search rows. I only caught it by backfilling a subsession that the sweep had already collected and diffing the two versions of the same race. If you’re stitching two endpoints together, find an overlapping record and compare it field by field before you trust the merge.

What each endpoint knows

They’re not the same shape either, which matters if you’re upserting one over the other:

  • search_series rows do carry car_name, license_category, starting_position and a nested track.config_name — fields the recent-races endpoint leaves empty.
  • They don’t carry oldi_rating / newi_rating / sub_level, which come back as None. Historical rows therefore have no iRating or Safety Rating delta, and never will.
  • The timestamp is start_time, not session_start_time.

Because each endpoint fills gaps the other leaves, the upsert has to merge rather than replace — ON CONFLICT with COALESCE per column, so a backfill enriches an existing row instead of blanking the ratings the live sweep captured.

One more: results/lap_data returns 400 for some older subsessions. iRacing doesn’t keep lap data forever. Treat it as a skip, not a failure, or a backfill across several seasons will die partway through.

The general shape

An endpoint named “recent” is telling you something. When it’s the only source you have and your dataset looks complete, that’s usually because time has been quietly doing the paging for you — and it will not do the same favour for the next record you add.


I’m Chris Moore — an independent developer and consultant in Henderson, NV, working under Grudged LLC. I build data pipelines and the reporting on top of them, including the results platform behind a sim racing team. If an API is giving you less than you think it is, get in touch.

← All field notes Talk to Chris Moore →