I’m a developer who’s into fitness and now slowly falling in love with running.
A few months ago, I joined a small running group guided by a coach. Initially, I couldn’t understand why people enjoyed running so much. I struggled, gasped for air, and mostly kept going because I needed a distraction.
When I hit my first non-stop 10k I couldn't stop comparing it with my very first run and appreciate how far I had come. That moment stuck with me.
The Why
A race was coming up for our group. I couldn’t participate due to an injury, but I knew many were giving it their all — their first 5K or 10K. I wanted to build something to celebrate their progress and efforts. And for myself, it was a chance to practice system design by building something end-to-end.
And so, Uplift was born — a quick weekend project built to celebrate each runner’s journey using their Strava data.
Project Goals
- Appreciate the effort of runners in my group
- Show them how far they've come using Strava stats
- Generate a motivating, AI-powered message based on their data
- Ship fast and keep it simple
Tech Stack
Backend: Node.js with Koa (my comfort zone)
Frontend: HTML, CSS, Vanilla JS (needed speed)
Integrations:
Strava API (OAuth + Activity fetch)
OpenAI (with fallback to Together AI)
QuickChart for journey graphs
Deployment: Render (simple and quick)
Logging: Google Sheets + Script
Feature Breakdown
Strava OAuth
- Registered the app to get client_id and client_secret
- Used OAuth flow to get access and refresh tokens
- Set up a callback route /callback
- Read user activities using /athlete/activities
- Followed their guidelines on logo placements etc
Report Generation
Each report includes:
- Total runs and distance
- Longest run
- Fastest run
- Recent Run
- First run (to see the contrast)
- A chart showing their weekly run stats
- A motivational AI insight
** AI Insight**
I crafted a detailed prompt asking the AI model to write a personalized, friendly uplifting message celebrating the user’s journey with the data provided to the LLM.
- OpenAI GPT-3.5 Turbo as the default model
- Together.ai Mistral-7B-Instruct as fallback
- Plain text as fallback if both models fail (If we exceed the quota in both models)
Graphs
Used QuickChart to generate a visual summary of the user’s consistency
Simple, fast and suited for small-scale usage
Download the report as an image
Initially added a text asking the user to manually take a screenshot. After I was done with the design tweaks and data I wanted to show, I realised manual screenshot was not very efficient as the user had to scroll down and take 2 images if the report was slightly longer. So now had to support downloading as an image as that was more user friendly. Used html2Canvas to support this usecase.
Strava-hosted profile images didn’t render in the canvas export. I created a proxy route to handle image downloads properly.
Taking it a step further
I tested on my Strava account. With the race just a day away, I wanted to surprise the group. I enabled international transactions on my card and paid around $5 to enable better AI responses (hello, crazy mode 😄), and told the group something exciting was coming up.
Roadblock: Connected Athletes Limit
Everything was ready and I asked a friend to try it out, thats when I realized there was a limit on the number of users allowed to connect.
I panicked. I had already told my group there was a surprise waiting. I really wanted people to use it right after the race. I wrote an email to Strava with the screenshots of my app, my linkedIn profile etc. I activated my linkedIn premium and reached out to support people from Strava requesting if they could do anything to increase it.
Eventually, I calmed down, figured out the right process, and submitted the form. I was transparent and informed my group about it.
Polish & Improvements
With extra time while waiting for the increased limit, started fine tuning a few things.
Refined prompt
Logging
I wanted to understand the basic usage of my app. But I also wanted something very quick and didn't want to spend too much time on this. Google sheets with a script deployment was sufficient for this.
Used Google Sheets + Script deployment to log basic usage info: timestamp, model used, and origin. No user data was stored — just a hashed athlete ID. Privacy first!
- Supported error states with friendly messages
- Created *feedback form *(Google Form)
- Added email contact for support
- Replaced the “latest run” with more meaningful milestones
Caching
Added caching to handle Strava's 200-requests-per-15-min rate limit
Design Iterations:
I made n number of tweaks on:
- Text placement
- Font sizing
- Chart layout
Logo:
Spent an unreasonable amount of time creating the logo and the name 😅
This is a privacy first app. No user data is stored.
End Result
Here goes my report :D
My takeaways:
- OAuth
- Got to explore Strava APIs
- AI prompting and data feeding for AI insights
- Starting with an idea, building and launching a product end-to-end
- Feedback from real users
- Always check rate limits and access caps
- Ship fast, but communicate clearly when things go sideways
- Tiny touches — profile pics, download buttons, fun copy — make a big difference
- Excitement when users used my app
Try Uplift!
If you’re a runner on Strava, try it out and see your journey celebrated beautifully.
https://sindhus-stride.onrender.com
Pre-requisite: A Strava account with a few runs logged.
Uplift is simple. But it’s made with heart and all the tech I have mentioned in this blog :P A weekend well spent :)
Here's me: LinkedIn
Also published on medium - https://a-sindu15.medium.com/uplift-for-strava-athletes-1317ad5b9645