Working around limitations in Strava’s Segment Efforts API

The Strava API has a call to retrieve the first 50 efforts for a segment.  In theory, the API supports an offset parameter to allow you to download additional efforts after the first 50.  However the offset parameter does not work currently in the segment efforts API.  I didn’t want to wait until V3 of the API, so…

Additionally, the segment efforts API returns efforts ordered fastest to slowest, so a naive date-based retrieval does not work, and this may also be why the offset parameter is not currently supported.

I now have a workaround which does the job (albeit a little more slowly): use the startDate and endDate parameters, which do work, to initially pull the efforts across “all time” (or, say, from 2010 to now).  If you receive 50 efforts back from the call, then split the time window in half, and retrieve each half.  Rinse and repeat until you receive less than 50 efforts for a window, at which point you know you have all the efforts for that window.  It’s simple enough to merge the arrays that you receive in response.

This works fine, but is a bit more load on the Strava servers.  For example, a segment with 425 efforts required 28 calls as opposed to 9 if the offset parameter worked, or just 1 if you could request the full set of efforts (which is still not a huge download, although really busy segments may be a bit more problematic).

I now optimise to retrieve all efforts for segments just once, then just the last few days worth later on.  The downside is that new subscribers will not have old efforts uploaded (to resolve this I may periodically do the full sync again). New segments would also bump into this issue until a global sync is done.

My implementation is not perfect (I’m ignoring errors for now), and if there are 50 efforts in less than 1 second then I’ll get none of them 🙂  Nevertheless, this little PHP code snippet is one way it could be done.

  function getSegmentEfforts($segmentId, $startDate = 0, $endDate = 0)
    if($startDate == 0 && $endDate == 0)
      $startDate = mktime(0,0,0,1,1,2010);
      $endDate = time();
    if($startDate >= $endDate) return null;
    $startDateString = date(“Y-m-d\\TH:i:s\\Z”, $startDate);
    $endDateString = date(“Y-m-d\\TH:i:s\\Z”, $endDate);

    $efforts = $this->callApi(“/v1/segments/$segmentId/efforts?startDate=$startDateString&endDate=$endDateString”);
    $numEfforts = sizeof($efforts->efforts);
    if($numEfforts == 50)
      // split the time period in 2
      $midDate = round(($startDate + $endDate) / 2, 0);
      $firstEfforts = $this->getSegmentEfforts($segmentId, $startDate, $midDate);
      $secondEfforts = $this->getSegmentEfforts($segmentId, $midDate, $endDate);
      if($firstEfforts == null) return $secondEfforts;
      if($secondEfforts == null) return $firstEfforts;
      $firstEfforts->efforts = array_merge($firstEfforts->efforts, $secondEfforts->efforts);
      return $firstEfforts;
    return $efforts;

10 thoughts on “Working around limitations in Strava’s Segment Efforts API

  1. Finding some of the very popular segments are limiting the feasibility of this approach (e.g. segments with over 2000 attempts). So I’ve tweaked the algorithm to stop recursing after 5 calls for now.

  2. This binary search approach is great, and was working well for me for a while. But I’m starting to believe that the time portion of the startDate and endDate parameters is not supported. The Strava docs state that these should be formatted as YYYY-MM-DD, without mentioning time. And the results for a call such as don’t appear to heed the time parameters at all. Thoughts?

  3. Of course, the concern is what if there’s more than 50 efforts in a day? With Low-Key Hillclimbs this is not unusual.

    I’m really looking forward to v3 coming on-line. For my present application, I need only to read and write single events, however, for which I hope the present infrastructure is sufficient.

  4. I’d been struggling with the API and segments with the offset parameter. After I realized it didn’t work Google directed me to your site. I was just about to implement your method when I noted that other queries ie efforts by athlete use a “?”. Anyway the offset parameter appears to work if you use “?offset=x” rather than “&offset=x”.

    Hope this helps.

    What I’m still struggling with is getting all segment efforts using v2 of the API and getting the leaderboard.


Leave a Reply

Your email address will not be published. Required fields are marked *