To integrate Replit with Garmin Connect, apply for the Garmin Health API program, implement OAuth 1.0a with your consumer key and secret stored in Replit Secrets (lock icon π), and call the Garmin Health API to pull user activities, wellness summaries, and sleep data. OAuth 1.0a requires per-request HMAC-SHA1 signing. Use a Reserved VM deployment for background sync jobs that poll activity data on a schedule.
Garmin Health API Integration from Replit
Garmin wearables capture detailed fitness and wellness data: GPS activity routes, heart rate variability, steps, sleep stages, stress scores, body battery, and dozens of other metrics. The Garmin Health API (formerly Garmin Connect API) provides structured access to this data for developers building health and fitness applications. Use cases include corporate wellness dashboards, personalized coaching apps, fitness app integrations, and research tools.
Garmin uses OAuth 1.0a β a signing protocol that predates the more common OAuth 2.0. The key difference is that OAuth 1.0a does not use Bearer tokens; instead, every API request must include a cryptographically signed Authorization header computed from the request method, URL, parameters, timestamp, nonce, and your consumer secret plus the user's token secret. This is more complex to implement than OAuth 2.0 but many enterprise APIs still use it.
Access to the Garmin Health API is not self-service. You must apply through the Garmin Developer Program (developer.garmin.com/gc-developer-program/), describe your use case, and receive approval before getting consumer credentials. The review process typically takes several days to weeks. Consumer-level access (for apps you build for yourself or your users) differs from health operator access (for medical/clinical applications). Make sure you apply for the right tier for your use case.
Integration method
Garmin Connect uses OAuth 1.0a for user authorization, which is more complex than OAuth 2.0 β every API request must be individually signed with an HMAC-SHA1 signature using your consumer credentials and the user's OAuth token. Your Replit server stores consumer key and secret in Replit Secrets, implements the OAuth 1.0a handshake to obtain user tokens, and signs each Garmin Health API request with the OAuth parameters. The Garmin Health API is not self-service β you must apply for access through the Garmin Developer Program.
Prerequisites
- A Replit account with a Node.js or Python Repl ready
- An approved Garmin Developer Program account with Health API access (apply at developer.garmin.com)
- Consumer key and consumer secret provided by Garmin after approval
- A Garmin Connect account with test data for development
Step-by-step guide
Apply for Garmin Health API Access
Apply for Garmin Health API Access
Navigate to developer.garmin.com/gc-developer-program/ and click 'Register'. Create a Garmin Developer account and fill in the application form. You will need to describe your application: what fitness data you need to access, how you will use it, your app's target audience, and your data storage and privacy practices. Garmin has multiple access tiers. Consumer access is for most developers building fitness and wellness apps. Health operator access is for clinical applications that handle PHI (protected health information) and requires additional compliance documentation. Apply for consumer access unless your app has clinical requirements. After submitting the application, the Garmin team will review it. Approval typically takes 3-10 business days. Garmin will email you with your consumer key and consumer secret β these are your permanent OAuth 1.0a credentials for your application. Unlike OAuth 2.0 client IDs, consumer keys in OAuth 1.0a are often longer, opaque strings. While waiting for approval, you can start building the OAuth 1.0a flow using test credentials. Garmin provides a sandbox environment for development. Refer to the Garmin Health API documentation at developer.garmin.com for the full list of endpoints, data types, and sandbox test procedures. Note: Garmin pushes data to a webhook URL you register rather than requiring polling for most data types. You register a push notification URL in the developer portal, and Garmin sends new activity data to that URL when a user's device syncs.
Pro tip: In your application, be specific about your data use case. Generic descriptions like 'fitness app' get rejected or delayed. Explain the exact data fields you need, why you need them, and how user data is stored and protected.
Expected result: Garmin Developer Program account created. Consumer key and consumer secret received via email after approval. Sandbox access enabled for development testing.
Store Garmin Credentials in Replit Secrets
Store Garmin Credentials in Replit Secrets
Click the lock icon (π) in the left Replit sidebar to open the Secrets pane and add the following secrets: GARMIN_CONSUMER_KEY: your OAuth 1.0a consumer key from Garmin. GARMIN_CONSUMER_SECRET: your OAuth 1.0a consumer secret. This is critically sensitive β anyone with the consumer secret can impersonate your application and generate OAuth tokens. GARMIN_OAUTH_REQUEST_URL: the Garmin OAuth request token endpoint (https://connectapi.garmin.com/oauth-service/oauth/request_token). GARMIN_OAUTH_AUTHORIZE_URL: the authorization URL (https://connect.garmin.com/oauthConfirm). GARMIN_OAUTH_ACCESS_URL: the access token endpoint (https://connectapi.garmin.com/oauth-service/oauth/access_token). When users authorize your app, you will also store their per-user OAuth token and token secret β store these in your database keyed by user ID, not as environment variables. For a single-user personal tool, you can store them in Secrets as GARMIN_USER_TOKEN and GARMIN_USER_TOKEN_SECRET after completing the OAuth handshake once. Replit's Secret Scanner monitors code files for credential patterns. The consumer secret must never appear in code.
1// check-garmin-secrets.js2const required = [3 'GARMIN_CONSUMER_KEY',4 'GARMIN_CONSUMER_SECRET',5 'GARMIN_OAUTH_REQUEST_URL',6 'GARMIN_OAUTH_AUTHORIZE_URL',7 'GARMIN_OAUTH_ACCESS_URL'8];9for (const key of required) {10 if (!process.env[key]) {11 throw new Error(`Missing secret: ${key}. Set it in Replit Secrets (lock icon π).`);12 }13}14console.log('Garmin OAuth secrets verified.');15console.log('Consumer key:', process.env.GARMIN_CONSUMER_KEY.substring(0, 8) + '...');Pro tip: The OAuth 1.0a consumer secret is like a private key β it is permanent and tied to your developer account. Treat it with the same care as a database password. Rotate it immediately if it is ever exposed.
Expected result: All five Garmin OAuth secrets appear in Replit Secrets. The check script prints the first 8 characters of the consumer key as a verification step.
Implement OAuth 1.0a Authorization Flow (Node.js)
Implement OAuth 1.0a Authorization Flow (Node.js)
Install dependencies in the Shell tab: npm install oauth axios express. The oauth package provides a ready-to-use OAuth 1.0a client that handles the HMAC-SHA1 request signing automatically, saving you from implementing the signature base string manually. OAuth 1.0a has three steps (three-legged OAuth): Step 1 β get a request token from Garmin using your consumer credentials. Step 2 β redirect the user to Garmin's authorization page with the request token. Step 3 β after the user authorizes, Garmin redirects back to your callback URL with an OAuth verifier; exchange the request token + verifier for a permanent access token. The OAuth class from the oauth package takes your consumer key, consumer secret, request URL, access URL, OAuth version, callback URL, and signature method. Call getOAuthRequestToken() for step 1, build the authorization redirect URL for step 2, and call getOAuthAccessToken() with the verifier for step 3. After obtaining the access token and token secret, use them to sign all subsequent Garmin Health API requests. The same OAuth object can sign GET and POST requests via the get() and post() methods, which automatically add the OAuth Authorization header with the correct HMAC-SHA1 signature. Garmin Health API endpoints include: /wellness-api/wellness/dailies/{userId} for daily summaries, /wellness-api/wellness/activities/{userId} for activity data, and /wellness-api/wellness/sleeps/{userId} for sleep metrics. Replace {userId} with the Garmin user ID returned during the OAuth flow.
1// garmin.js β Garmin Health API with OAuth 1.0a for Node.js on Replit2const OAuth = require('oauth').OAuth;3const express = require('express');4const axios = require('axios');56const app = express();7app.use(express.json());89const CALLBACK_URL = `${process.env.REPLIT_URL || 'http://localhost:3000'}/auth/callback`;1011// Initialize OAuth 1.0a client12const oauth = new OAuth(13 process.env.GARMIN_OAUTH_REQUEST_URL,14 process.env.GARMIN_OAUTH_ACCESS_URL,15 process.env.GARMIN_CONSUMER_KEY,16 process.env.GARMIN_CONSUMER_SECRET,17 '1.0',18 CALLBACK_URL,19 'HMAC-SHA1'20);2122// In-memory store for request tokens (use DB in production)23const tokenStore = {};2425// Step 1: Get request token and redirect to Garmin authorization26app.get('/auth/login', (req, res) => {27 oauth.getOAuthRequestToken((err, requestToken, requestTokenSecret) => {28 if (err) return res.status(500).json({ error: err.message });29 tokenStore[requestToken] = requestTokenSecret;30 const authUrl = `${process.env.GARMIN_OAUTH_AUTHORIZE_URL}?oauth_token=${requestToken}`;31 res.redirect(authUrl);32 });33});3435// Step 2: OAuth callback β exchange request token + verifier for access token36app.get('/auth/callback', (req, res) => {37 const { oauth_token, oauth_verifier } = req.query;38 const requestTokenSecret = tokenStore[oauth_token];39 40 if (!requestTokenSecret) return res.status(400).send('Request token not found');41 42 oauth.getOAuthAccessToken(43 oauth_token, requestTokenSecret, oauth_verifier,44 (err, accessToken, accessTokenSecret, results) => {45 if (err) return res.status(500).json({ error: err.message });46 delete tokenStore[oauth_token];47 48 // In production: save to database keyed by user ID49 // For single-user: store in Secrets as GARMIN_USER_TOKEN and GARMIN_USER_TOKEN_SECRET50 console.log('Garmin OAuth complete.');51 console.log('User display name:', results?.display_name);52 res.json({ success: true, displayName: results?.display_name });53 }54 );55});5657// Get daily wellness summaries58app.get('/api/wellness/daily', async (req, res) => {59 const token = process.env.GARMIN_USER_TOKEN;60 const tokenSecret = process.env.GARMIN_USER_TOKEN_SECRET;61 const { startDate, endDate } = req.query;62 63 if (!token || !tokenSecret) {64 return res.status(401).json({ error: 'Run OAuth flow first to get user token' });65 }66 67 const apiUrl = `https://healthapi.garmin.com/wellness-api/rest/dailies`;68 const params = `uploadStartTimeInSeconds=${startDate}&uploadEndTimeInSeconds=${endDate}`;69 70 oauth.get(`${apiUrl}?${params}`, token, tokenSecret, (err, data) => {71 if (err) return res.status(err.statusCode || 500).json({ error: err.data });72 try {73 res.json(JSON.parse(data));74 } catch {75 res.json({ raw: data });76 }77 });78});7980app.listen(3000, '0.0.0.0', () => console.log('Garmin server running on port 3000'));Pro tip: The Garmin Health API uses epoch timestamps (Unix seconds) for date range parameters, not ISO date strings. Convert date ranges using Math.floor(new Date('2026-01-01').getTime() / 1000) before passing them as query parameters.
Expected result: Visiting /auth/login redirects to the Garmin authorization page. After the user authorizes, /auth/callback exchanges tokens. GET /api/wellness/daily returns the user's daily wellness summaries.
Python Integration for Garmin Health API
Python Integration for Garmin Health API
For Python Replit projects, install requests_oauthlib and flask: pip install requests requests_oauthlib flask. The requests_oauthlib library provides OAuth1 session support that handles the signature generation automatically. Create an OAuth1Session with your consumer key and secret. Call the fetch_request_token() method to get a request token, redirect the user to the authorization URL, then call fetch_access_token() with the OAuth verifier from the callback to get the permanent access token and token secret. After the OAuth flow, create a new OAuth1Session with all four credentials (consumer key, consumer secret, access token, access token secret) to make signed API requests. The session handles HMAC-SHA1 signing transparently β just call session.get(url) and the authorization header is added automatically. For background sync jobs (pulling new Garmin data on a schedule), deploy as a Reserved VM so the process keeps running continuously. Garmin also supports push notifications to a registered webhook URL β when a Garmin device syncs, Garmin sends the new data to your endpoint, which is more efficient than polling.
1# garmin_api.py β Garmin Health API with OAuth 1.0a for Python on Replit2import os3from requests_oauthlib import OAuth1Session4from flask import Flask, request, redirect, jsonify, session5import secrets67app = Flask(__name__)8app.secret_key = secrets.token_hex(32)910CONSUMER_KEY = os.environ['GARMIN_CONSUMER_KEY']11CONSUMER_SECRET = os.environ['GARMIN_CONSUMER_SECRET']12REQUEST_URL = os.environ['GARMIN_OAUTH_REQUEST_URL']13AUTHORIZE_URL = os.environ['GARMIN_OAUTH_AUTHORIZE_URL']14ACCESS_URL = os.environ['GARMIN_OAUTH_ACCESS_URL']15CALLBACK_URL = os.environ.get('GARMIN_CALLBACK_URL', 'http://localhost:3000/auth/callback')1617@app.route('/auth/login')18def login():19 oauth = OAuth1Session(CONSUMER_KEY, client_secret=CONSUMER_SECRET,20 callback_uri=CALLBACK_URL)21 fetch_response = oauth.fetch_request_token(REQUEST_URL)22 session['request_token'] = fetch_response.get('oauth_token')23 session['request_token_secret'] = fetch_response.get('oauth_token_secret')24 auth_url = oauth.authorization_url(AUTHORIZE_URL)25 return redirect(auth_url)2627@app.route('/auth/callback')28def callback():29 oauth_verifier = request.args.get('oauth_verifier')30 oauth_token = request.args.get('oauth_token')31 32 oauth = OAuth1Session(33 CONSUMER_KEY,34 client_secret=CONSUMER_SECRET,35 resource_owner_key=session.get('request_token'),36 resource_owner_secret=session.get('request_token_secret'),37 verifier=oauth_verifier38 )39 40 tokens = oauth.fetch_access_token(ACCESS_URL)41 # Store these securely (Secrets for single-user, DB for multi-user)42 print('OAuth complete. Access token obtained.')43 print('User:', tokens.get('displayName', 'unknown'))44 return jsonify({'success': True, 'message': 'Store tokens in Secrets or DB'})4546@app.route('/api/activities')47def get_activities():48 access_token = os.environ.get('GARMIN_USER_TOKEN')49 access_secret = os.environ.get('GARMIN_USER_TOKEN_SECRET')50 51 if not access_token or not access_secret:52 return jsonify({'error': 'Run OAuth flow first β GARMIN_USER_TOKEN not set'}), 40153 54 oauth = OAuth1Session(55 CONSUMER_KEY,56 client_secret=CONSUMER_SECRET,57 resource_owner_key=access_token,58 resource_owner_secret=access_secret59 )60 61 import time62 end_time = int(time.time())63 start_time = end_time - (7 * 24 * 3600) # Last 7 days64 65 response = oauth.get(66 'https://healthapi.garmin.com/wellness-api/rest/activities',67 params={'uploadStartTimeInSeconds': start_time, 'uploadEndTimeInSeconds': end_time}68 )69 70 if not response.ok:71 return jsonify({'error': response.text}), response.status_code72 return jsonify(response.json())7374if __name__ == '__main__':75 app.run(host='0.0.0.0', port=3000)Pro tip: requests_oauthlib handles the OAuth 1.0a signature generation automatically. Never try to implement HMAC-SHA1 OAuth signing manually β the signature base string construction is complex and easy to get wrong, causing subtle authentication failures.
Expected result: GET /auth/login redirects to Garmin's authorization page. POST /auth/callback exchanges verifier for access token. GET /api/activities returns the authorized user's activity data from the last 7 days.
Common use cases
Personal Fitness Dashboard
Build a custom fitness dashboard that pulls daily activity summaries, step counts, heart rate data, and sleep metrics from Garmin and displays them alongside data from other health sources. Create custom views and analytics not available in the Garmin Connect app.
Build an Express API that fetches the last 7 days of Garmin daily summaries including steps, calories, and sleep duration, and returns the data formatted for a dashboard chart.
Copy this prompt to try it in Replit
Team Wellness Monitoring
For corporate wellness programs, aggregate anonymized activity metrics from consenting team members who have authorized your app. Track team-wide fitness trends, step challenge progress, or recovery scores across participants.
Create a wellness API that aggregates daily step counts from multiple authorized Garmin users and returns team totals and individual rankings for a step challenge leaderboard.
Copy this prompt to try it in Replit
Training Load Sync
Sync Garmin activity data with a training planning tool. When a user completes a workout, pull the activity details (distance, duration, heart rate zones) from Garmin and record it in your training database alongside scheduled workouts to track plan adherence.
Build a webhook receiver that processes new Garmin activity notifications and stores the activity summary in a database, then calculates weekly training load based on the last 7 days of activities.
Copy this prompt to try it in Replit
Troubleshooting
oauth_problem=signature_invalid error on every API request
Cause: The HMAC-SHA1 signature is being computed incorrectly. Common causes: the consumer secret or token secret contains extra whitespace, the timestamp is too far from the server time (Garmin rejects requests with timestamp skew > 5 minutes), or the nonce has been reused.
Solution: Use the oauth npm package or requests_oauthlib Python library rather than implementing signing manually. Verify GARMIN_CONSUMER_SECRET has no extra whitespace in Secrets. Check that your server's clock is accurate β Replit servers should be NTP-synced, but very long-running processes might have clock drift.
1// Verify timestamp matches server time2console.log('Current Unix timestamp:', Math.floor(Date.now() / 1000));3console.log('Compare with: https://www.unixtimestamp.com/');oauth_problem=token_rejected after completing the OAuth flow
Cause: The access token or token secret stored in Secrets is incorrect, was generated for a different consumer key, or the user has revoked the authorization in their Garmin Connect account settings.
Solution: Re-run the OAuth flow to get a fresh access token. Verify that GARMIN_USER_TOKEN and GARMIN_USER_TOKEN_SECRET were copied correctly from the oauth callback results. Users can revoke authorization in their Garmin Connect account β check if the user has revoked access.
403 Forbidden on Garmin Health API endpoints after OAuth
Cause: Your Garmin Developer Program account does not have access to the specific endpoint you are calling. Different API endpoints require different permission tiers (consumer vs health operator).
Solution: Review your Developer Program permissions in the developer portal. Some endpoints (sleep data, body metrics, stress) may require health operator access. Check the Garmin Health API documentation to confirm which tier each endpoint requires and apply for the appropriate access level.
Request token not stored β OAuth callback fails with missing state
Cause: The tokenStore object (in-memory) lost the request token because the server restarted between the login and callback requests, or the session middleware is not persisting across requests.
Solution: Use a database or persistent session store to save request tokens during the OAuth flow. For development, ensure the server does not restart between login and callback. For production, use Redis or a database to persist the token during the OAuth handshake.
Best practices
- Store GARMIN_CONSUMER_KEY and GARMIN_CONSUMER_SECRET in Replit Secrets (lock icon π) β these are permanent credentials for your developer account
- Use the oauth npm package (Node.js) or requests_oauthlib (Python) to handle HMAC-SHA1 signing β manual OAuth 1.0a implementation is error-prone
- Store per-user OAuth tokens in a database for multi-user apps; for personal tools, store them in Replit Secrets after completing the OAuth flow once
- Use epoch Unix timestamps (not ISO strings) for Garmin API date range parameters, and always express them in seconds not milliseconds
- Register a webhook URL in the Garmin Developer Portal to receive push notifications instead of polling β Garmin sends data when devices sync, which is more efficient
- Handle token revocation gracefully β users can revoke access in Garmin Connect; catch token_rejected errors and redirect users to re-authorize
- Deploy as Reserved VM on Replit for background sync jobs that continuously process incoming Garmin webhook data
- Apply for Garmin Developer Program access early β the review process can take days to weeks and blocks all development progress until approved
Alternatives
HealthKit covers Apple Watch and iOS health data but requires an iOS app and has no cloud API, while Garmin's cloud Health API is accessible from any server.
RescueTime tracks computer productivity time rather than physical activity, but uses a simpler OAuth 2.0 API if you need a less complex integration setup.
Use Notion as a database to log and display Garmin fitness data synced through your Replit integration.
Frequently asked questions
How do I connect Replit to Garmin Connect?
Apply for the Garmin Health API program at developer.garmin.com, receive consumer credentials, store them in Replit Secrets (lock icon π), and implement OAuth 1.0a using the oauth npm package or requests_oauthlib. Every API request must be signed with HMAC-SHA1 using your consumer credentials and the user's OAuth token.
Does Replit work with Garmin Connect?
Yes, but Garmin Health API access is not self-service. You must apply for developer access and receive approval before getting credentials. Once approved, any HTTP-capable server β including a Replit app β can call the Garmin Health API using OAuth 1.0a authentication.
What is OAuth 1.0a and how is it different from OAuth 2.0?
OAuth 1.0a requires every individual API request to be signed with an HMAC-SHA1 signature computed from request details and your credentials. OAuth 2.0 uses Bearer tokens β once you have the token, you just include it in a header. OAuth 1.0a is more complex but does not require a separate transport-layer security assumption. Use a library like oauth (npm) or requests_oauthlib (Python) to handle the signing automatically.
How do I store my Garmin consumer secret in Replit?
Click the lock icon (π) in the Replit sidebar and add GARMIN_CONSUMER_KEY and GARMIN_CONSUMER_SECRET as separate secrets. Never put these values in your code files. Access them in your server with process.env.GARMIN_CONSUMER_KEY (Node.js) or os.environ['GARMIN_CONSUMER_KEY'] (Python).
Can I use Garmin Connect API for free?
The Garmin Health API is free to use after your developer application is approved. There are no per-request fees, but there are rate limits. Commercial applications that monetize Garmin data may require a different partnership agreement with Garmin.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation