Some of you have seen me around here asking about this, I have participated in many threads about the athletes limit. My app sat at the 10 athlete cap for months. Five applications, five rejections, never a reason (Maybe more than 5 actually) I totally understand that the STRAVA team is busy with so many apps, so never hard feelings.
Then I audited my own integration properly, looking for anything wrong on my side whether or not it was the blocker. Reapplied. The increase came through in 7 days.
I cannot tell you these three things were the reason, because Strava never told me what the reason was. They were wrong, I fixed them, and the sixth application went through.
Context: I built MoguWind Plan, a free route planner that shows cyclists the wind along their route. The Strava connection pulls in saved routes and activities and shows the wind in the activity after. Very basic, but still you need to connect your account etc.
1. The deauthorization webhook, which I was not handling at all
Fix this one first.
I had webhooks working for new activities and assumed that was the whole job. Strava also sends an event when an athlete revokes your app from their own settings:
object_type: "athlete"
aspect_type: "update"
updates: { authorized: "false" }
Note that `authorized` arrives as the string `"false"`, not a boolean. I check for both.
For me this was the most important, but I am no expert. Until I handled this, a revoked athlete stayed in my database as a ghost row, still occupying one of my ten slots, until some unrelated cleanup job noticed their token had stopped refreshing. From Strava's side I was holding authorizations for people who had already said no. Now I delete the row the moment the event arrives. I do not call `/oauth/deauthorize` there, because Strava has already revoked it.
2. Webhook mechanics that are easy to get subtly wrong
The subscription validation is a GET carrying `hub.mode`, `hub.challenge` and `hub.verify_token`. Echo the challenge back as JSON with `hub.challenge` as the key, not as plain text. And check `hub.verify_token` against your own value before echoing anything.
The event POST has a 2 second timeout. If your handler fetches the activity, downloads streams and writes something back, you will not make it. Return 200 immediately and do the work off the request path.
3. Branding and the link back
Two requirements, one element:
- The Brand Guidelines want visible attribution. I show "Powered by Strava" with the official mark wherever Strava data appears. I know this is something basic as well, but just in case.
- The API Agreement wants a link back to Strava. That same element links to the connected athlete's own Strava profile, not to strava.com in general.
What I would do:
- Read the full list of webhook events, not just the one you need. I built everything around "new activity" because that was my feature, and never asked what else Strava might be telling me. The answer was: when someone leaves.
- The limits after approval are 600 requests per 15 minutes and 6,000 per day. Invisible with ten athletes, not with a real user base, especially if you make several calls per activity. I added a retry queue before I needed one.
Happy to answer questions if anyone is stuck on the same thing.
