Recreating WHOOP on Google Fitbit: What It Takes
Google Fitbit Air costs $99 and its hardware is good. Its recovery and load numbers cost another $99 a year, and unlike WHOOP's, Fitbit's are not even documented. So the obvious move is to build your own. This is what that actually involves — which inputs exist, which published methods you can legitimately build on, and the point where a Fitbit reimplementation stops being a WHOOP substitute.
We earn nothing from this page. There are no affiliate links, no sponsorship and no paid placement, and we have no commercial relationship with any tool or vendor named here. Some tools listed are paid products and we say which. How we evaluate software is set out in our editorial standards.
The short answer
You can build something genuinely useful, but it will not be WHOOP. The Google Health API gives you daily HRV, daily resting heart rate, sleep with stage breakdown, and active zone minutes — enough to compute a baseline-relative recovery score and a cardiovascular load score from published, citable methods. What you cannot do is reproduce WHOOP's numbers, because WHOOP does not publish its formula either, and the Fitbit Air lacks the two sensors that make WHOOP's approach distinctive: no ECG, and no continuous heart-rate-variability stream. Treat anything claiming a like-for-like WHOOP port to Fitbit as marketing.
What you are actually trying to reproduce
WHOOP's product is two numbers and a colour. Recovery is a 0–33% band derived mainly from heart rate variability, resting heart rate, sleep performance and respiratory rate, each compared against your own baseline. Strain is a 0–21 logarithmic scale of cardiovascular exertion accumulated across the day.
Two properties of those scores matter more than the numbers themselves:
- They are baseline-relative. A recovery score is not an absolute measurement of how recovered you are. It is a statement about where you sit compared to your own recent normal. This is the single most important design decision, and it is the one you must get right.
- Strain is logarithmic. Going from strain 8 to strain 12 is not the same physiological event as going from 18 to 21, and a scale that treated them as equal would be useless for training decisions.
Neither company publishes how they actually compute these. Fitbit's Readiness, Cardio Load and Sleep Score are proprietary, as is WHOOP's Recovery and Strain. That is not a scandal — it is normal — but it has a consequence that gets glossed over: there is no reference implementation to copy, and no ground truth to validate against. You are choosing a method, not reproducing a known answer.
What the Google Health API actually gives you
The API is served from health.googleapis.com under version 4, and replaces the retired Fitbit Web API. It uses Google OAuth 2.0 rather than Fitbit's own OAuth, which is the single biggest change for anyone migrating: existing tokens cannot be transferred and every user has to consent again.
Around 39 data types are available. The ones that matter for a recovery or load score, with the method that works for each, as documented by Google and confirmed in the field by open-source implementations:
| Signal | Data type | Method | Notes |
|---|---|---|---|
| Heart rate variability | daily-heart-rate-variability | list | A daily summary, not a continuous R-R stream. This is the most important limitation on the whole page. |
| Resting heart rate | daily-resting-heart-rate | list | One value per day. Good enough for a baseline. |
| Sleep, with stages | sleep | list | Session record. Supports list, reconcile, create, update and delete. |
| Active zone minutes | active-zone-minutes | dailyRollUp | Time in heart-rate zones. Fitbit Air is supported. |
| Heart rate, intraday | heart-rate | dailyRollUp | Aggregates. Raw point-in-time samples come from reconcile with the google-wearables source family. |
| Respiratory rate | daily-respiratory-rate | list | Available on some devices; confirm against your own data. |
| Oxygen saturation | daily-oxygen-saturation | list | Daily summary. |
| Recorded workouts | exercise | list | Session metrics. Also exports as TCX. |
| VO2 max | vo2-max | list | Sample type. May be sparse. |
| Steps, distance, calories | steps | dailyRollUp | Interval types, well supported. |
Three traps that will cost you an afternoon each
1. Data type identifiers are kebab-case in URLs and snake_case in filters. The path takes dataTypes/heart-rate; the filter parameter takes heart_rate.sample_time.physical_time. Sending camelCase returns an HTTP 400 with an unhelpful message.
2. Rollup ranges are capped. The dailyRollUp endpoint enforces a maximum query range per data type — heart rate fails beyond roughly 14 days. Fetch long history in chunks of 14 days or less and merge the results.
3. Your refresh token dies every seven days. Every Google Health scope is a restricted scope, so production use requires Google's OAuth verification and an annual paid security assessment. Personal tools stay in Testing mode, and Testing-mode refresh tokens expire after seven days. Re-consenting takes about ten seconds. This is a platform constraint that applies to every self-hosted Fitbit tool equally, and it is the thing most likely to make you think a tool is broken when it is not.
Building the recovery score
Given the inputs above, the defensible approach is a baseline-relative z-score composite. It is not exotic, it is not original to us, and every step is published.
z = (today − baseline mean) ÷ baseline SD. The sign matters and is frequently got wrong: for HRV, higher is better, so the raw z-score is already correctly oriented. For resting heart rate, higher is worse, so the sign must be inverted before weighting.
The RMSSD measure used to summarise HRV here is the standard short-term metric, and the reference for its interpretation is the 1996 Task Force consensus statement on heart rate variability standards, which remains the citation almost every implementation points back to.
This is our convention, not a standard
The 28-day window, the specific weights, and the choice to map through the normal CDF are our construction and our recommended defaults, not a published standard and not any vendor's method. Published literature supports the baseline-relative principle and the use of HRV as a fatigue marker. It does not prescribe a window length or a weighting. Where you see a number produced this way, treat it as one defensible choice among several, and expect to tune it.
Building the strain score
Strain is easier and more defensible, because it derives from heart rate rather than from a comparison against your own history.
- Convert heart rate to a percentage of heart rate reserve. HRR is
HRmax − HRrest, and the reserve fraction is(HR − HRrest) ÷ HRR. This is the Karvonen method, and normalising against reserve rather than againstHRmaxdirectly is what makes the number comparable between people with very different resting heart rates. - Estimate
HRmax. The Tanaka formula,208 − 0.7 × age, is a reasonable population default. If you have a measured maximum, use it — the error in this step propagates straight into every strain number. - Weight the reserve fraction to get a training impulse. The Edwards TRIMP weighting for men is
HRr × 0.64 × e^(1.92 × HRr)and for womenHRr × 0.86 × e^(1.67 × HRr). Banister's impulse-response model is the alternative, adding an exponentially decaying fatigue term so yesterday's load still counts today. - Log the result onto the 0–21 scale. Logarithmic, because strain 18 to 21 is qualitatively different from 8 to 12 and a linear scale flattens exactly the region that matters for overreaching decisions.
Active zone minutes give you a shortcut if you would rather not process raw heart rate: time in each Fitbit-defined zone, weighted by zone, is a serviceable approximation of the same quantity. Several projects do exactly this because it is far less code and the difference in practical use is small.
On the acute:chronic workload ratio
If you go further and start tracking a weekly load ratio, be aware it is contested. The acute:chronic workload ratio originates with Foster's 1998 monitoring framework and is widely used, but its interpretation as a causal injury predictor has been substantially challenged since — most prominently by Gabbett, whose work argues the apparent relationship is largely explained by confounding from fitness already being low when athletes get injured. We report the ratio where projects expose it and do not present it as a validated risk model, because it is not one.
What the existing projects actually compute
You do not have to write any of this. Several open-source projects read the Google Health API and publish their formulas. We have not run any of them — this is documentation and repository research, and our standards page explains exactly what that means — so treat the descriptions below as claims made by each project about itself.
| Project | Shape | Stated scoring | Licence |
|---|---|---|---|
| fitbit-health-explorer | Electron desktop app, local JSON store, npm start | Publishes the most detail of any of these: recovery as weighted z-scores of HRV, resting HR, sleep and respiratory rate against a trailing 30-day baseline, mapped through the normal CDF; strain 0–21 as a logarithmic load from active zone minutes, active energy and steps; readiness as a blend; plus a Physio Age estimate. All weights live in settings rather than code. | Open source |
| fettle | FastAPI + Next.js + SQLite, single user | Readiness 0–100 against a 28-day baseline, a sleep score, and a TRIMP-style cardio load, with thresholds traced to citations in a metrics specification document. Ships 25 MCP tools, so an AI agent can read it directly. | MIT |
| vitals | FastAPI + vanilla JS, Docker or a single install script, multi-profile | Recovery, strain, sleep performance and a body-age estimate, with the formulas documented in a dedicated algorithms file. Ships 9 read-only MCP tools and a demo mode with 150 days of synthetic data, so you can see the shape before connecting anything. | MIT |
| Vitals Command Center | TypeScript, installable PWA, MCP | Takes a different angle: rather than trusting one device, it reconciles Fitbit, Oura, WHOOP and Apple Health into one weighted consensus with a per-metric confidence level, and routes each device either through the Google Health bridge or its own adapter so nothing is double-counted. | MIT |
| HealthLog | Docker PWA plus a native iOS client | Broader scope than scoring: adds medication, glucose, mood and cycle tracking alongside sleep and recovery, with per-metric source priority so overlapping wearables resolve consistently. Reads Fitbit through your own Google Cloud client. | PolyForm Noncommercial 1.0.0, AGPL to v1.15.18 |
| Pulseboard | Node, no third-party runtime dependencies, Docker | A read-oriented dashboard rather than a scoring engine. Encrypts the persisted refresh token with AES-256-GCM, and is the one project here aimed specifically at the Fitbit Air. | Open source |
A licensing trap worth knowing
HealthLog is source available, not open source, and the distinction matters. It is free to run and free to modify for noncommercial use; commercial use requires a separate agreement. If your interest is a personal dashboard, that is fine. If it is ever going in a product, that licence is a blocker, and it is the reason the table lists licences rather than leaving them out.
If you also want an AI assistant to read the same data rather than just a human-facing dashboard, add an MCP server. Several exist for the Google Health API specifically — one exposes 29 read-only tools, another adds sync into an Obsidian vault, and a third implements remote MCP with OAuth so it can be registered as a ChatGPT connector. That is a different article, and it is covered on the cluster index alongside the platform differences that determine which approach you need.
Where this stops being a WHOOP clone
This is the part that decides whether you should build it at all, so it is worth being blunt rather than encouraging.
WHOOP straps and some Fitbit models carry single-lead ECG for atrial fibrillation detection. Fitbit Air offers irregular-rhythm notification alerts, which is a different thing. If irregular-heart-rhythm detection is why you were considering WHOOP, no amount of software substitutes for the sensor.
This is the deepest limitation. WHOOP computes recovery from continuous beat-to-beat data. The Google Health API's daily-heart-rate-variability type is an aggregate for the whole day. That is enough to see a trend against your baseline; it is not enough to reproduce any method that depends on within-day structure, such as detecting overnight HRV deterioration or a parasympathetic rebound pattern.
You cannot validate your score against WHOOP's, and you cannot validate it against Fitbit's. What you can check is whether it tracks your own experience — whether green days feel green. That is a weaker guarantee than it sounds, because it is easy to fit a score to your memory after the fact.
The Fitbit Web API is being retired and the Google Fit API dies at the end of 2026. Fitbit has already removed its web dashboard, its challenges and its badges once. Your data is portable, but the platform you read it through is demonstrably not a stable contract.
If you connect a Garmin or an Oura into Google Health to enrich your history, Google's own documentation states that Sleep Score and Cardio Load are calculated from first-party data only. Those scores will not improve. Anything you compute yourself will, because you control the inputs.
Given all that, here is the honest recommendation. If you want a recovery number to make training decisions, buy the subscription — not because the number is better validated than yours would be, but because supporting the raw sensor is the part you cannot replicate. If you want to look at a year of your own data on a laptop, build it, because Google removed that capability and will not bring it back. Those are two different problems and the right answer is different for each.
Frequently asked
Can you recreate WHOOP on a Fitbit?
Partly. The Google Health API exposes daily heart rate variability, daily resting heart rate, sleep with stage breakdown, and active zone minutes — enough to compute a baseline-relative recovery score and a cardiovascular load score from published methods. But Fitbit publishes no formula for its own Readiness or Cardio Load, and Fitbit Air has neither an ECG nor continuous HRV, so the result is a different instrument rather than a copy of WHOOP's.
What data can I pull from the Google Health API?
Around 39 data types, including steps, distance, active and total calories, active minutes, active zone minutes, intraday heart rate, daily resting heart rate, daily heart rate variability, oxygen saturation, respiratory rate, VO2 max, body fat, sleep sessions with stages, and recorded exercise sessions. It is served from health.googleapis.com under version 4, using Google OAuth 2.0 rather than the retired Fitbit OAuth, so existing tokens cannot be migrated and users must consent again.
Is there an open-source WHOOP alternative that reads Fitbit data?
Several. They split into self-hosted dashboards — Vitals, Vitals Command Center, Fitbit Health Explorer, fettle, HealthLog and Pulseboard — and Model Context Protocol servers that expose the same data to AI assistants. Most use your own Google Cloud OAuth client rather than a shared backend, so your credentials never pass through someone else's server.
Why does my self-hosted Fitbit tool keep asking me to sign in again?
Because every Google Health API scope is restricted. Production use requires Google's OAuth verification, which for restricted scopes means an annual paid security assessment. Personal tools stay in Testing mode, and Testing-mode refresh tokens expire after seven days. Re-consent takes about ten seconds. It is a platform constraint, not a fault in the project.
Is a self-computed recovery score as good as WHOOP's?
You cannot judge it that way, because neither number is validated against a reference standard — WHOOP's formula and Fitbit's are both proprietary. What you can judge is whether your score tracks your own baseline consistently over weeks, which is the property that actually matters for training decisions. We would hold any new score to that test before trusting it with a training decision.
Related
- Google Health Data: Portals, Scores and AI — the cluster index, including the seven-day token problem in full
- Does WHOOP recovery predict your lift performance? — read this before you start acting on a recovery number you built yourself
- Sleep consistency and your training data — which sleep metrics are worth scoring
- Fitbit Air vs WHOOP — the hardware and subscription comparison
- Best fitness tracker for recovery 2026 — if you are still choosing a device
Sources
- Google Health API overview — Google for Developers
- Google Health API data types, scopes and compatible devices — Google for Developers
- Google Health API REST reference — list, reconcile, rollUp, dailyRollUp and batchDelete methods
- Migrating from the Fitbit Web API — Google for Developers. Source of the token-transfer and user-identifier change
- Connect third-party devices and apps to the Google Health app — Google Health Help Center
- Introducing the all-new Fitbit Air — Google, 7 May 2026. Sensor list and absence of ECG
- Task Force of the European Society of Cardiology and the North American Society of Pacing and Electrophysiology (1996). Heart rate variability: standards of measurement, physiological interpretation and clinical use. Circulation 93(5):1043–1065
- Karvonen, J., Kannel, M. & Salo, J. (1981). Effects of age and body weight on cardiovascular response during exercise. Heart 87(5):441–451
- Tanaka, M., Monahan, K. & Seals, D. R. (2001). Age-predicted maximal heart rate revisited. Journal of the American College of Cardiology 37(7):1537–1541
- Edwards, S. (1991). Heart rate reserve. Medicine & Science in Sports & Exercise 23(2 Suppl.):S498–S503
- Banister, E. W., Calvert, T. W., Horner, F. T. & Brooks, R. J. (1991). An integrated model of endurance athlete training. International Journal of Sports Medicine 12(1):5–10
- Foster, C. (1998). Monitoring training in athletes with reference to overtraining syndrome. Medicine & Science in Sports & Exercise 30(9):1164–1168
- Gabbett, T. J. (2016). The training-injury prevention paradox: should athletes be training smarter and harder? British Journal of Sports Medicine 50(5):273–280
- Paari/fitbit-health-explorer — README, scoring documentation and Google Health API field notes
- Deekshith-Dade/fettle — README and metrics specification
- DocStream-Oficial/vitals — README and algorithms documentation
- 8tp/Vitals-Command-Center — README and adapter documentation
- MBombeck/HealthLog — README and licence terms
- sardistic/health — README