@likenttt/garmin-connect
v1.7.0
Published
Makes it simple to interface with Garmin Connect to get or set any data point
Maintainers
Readme
@likenttt/garmin-connect
Maintained TypeScript SDK: @likenttt/[email protected] from
Likenttt/garmin-connect.
The DSH plugin consumes this repository at an immutable Git commit, installed
under its existing garmin-connect dependency key. The scoped package has not
been published to npm. Git installs run prepare to build the bundled JavaScript
and public declarations from the TypeScript sources.
Recovery APIs in this fork
Development is maintained in Likenttt/garmin-connect. Use this maintained fork for the following new methods:
| Method | Data returned |
| ---------------------------------------- | -------------------------------------------------------------------------------- |
| getHrvData(date?) | HRV nightly summary, personal baseline, seven-day average and overnight readings |
| getHrvSummaries(startDate, endDate) | Native inclusive date-range response, including hrvSummaries |
| getStressData(date?) | Full daily stress data and its time series |
| getStressSummaries(startDate, endDate) | Daily stress summaries, preserving each entry's values object |
| getBodyBattery(startDate?, endDate?) | Daily Body Battery reports and time series; end defaults to start |
| getTrainingReadiness(date?) | All readiness snapshots and recovery factors for the requested day |
Dates accept either a strict YYYY-MM-DD string or a Date interpreted in the
machine's local timezone. Omitted daily dates default to today. Both range
endpoints include the start and end dates. Invalid calendar dates and reversed
ranges fail before sending a request. For historical data, date-only strings
avoid unintended timezone conversion.
// With an authenticated GCClient (see the session examples below):
const hrv = await GCClient.getHrvData('2026-10-08');
const hrvHistory = await GCClient.getHrvSummaries('2026-10-01', '2026-10-08');
const stress = await GCClient.getStressData('2026-10-08');
const stressHistory = await GCClient.getStressSummaries(
'2026-10-01',
'2026-10-08'
);
const bodyBattery = await GCClient.getBodyBattery('2026-10-01', '2026-10-08');
const readiness = await GCClient.getTrainingReadiness('2026-10-08');The methods use the client's configured garmin.com or garmin.cn API host.
They preserve raw response fields, zero values, missing baselines, sentinel
values and complete readings. Successful no-content responses become null;
empty arrays and objects remain unchanged. HTTP failures are rejected with the
original client error, including its status. Training readiness usually returns
an array; an object response is also retained as received. recoveryTime is in
minutes.
The new response types, including HrvData, HrvSummaries, StressData,
BodyBatteryData, TrainingReadinessData and GarminDate, are exported from the
package entry point. Fields are optional and nullable because device support,
sync state and available recording history differ. Date-range HRV summaries do
not contain all nightly readings; use getHrvData for those readings.
See the source comparison and next API priorities for pinned Python source references, response evidence and remaining gaps.
Training analysis APIs in this fork
| Method | Data returned |
| ------------------------------------------------------- | --------------------------------------------------------------------------- |
| getDailyTrainingStatus(date?) | Daily training status, acute/chronic load and ACWR for all returned devices |
| getTrainingStatus(date?) | Aggregated training status, without reshaping the response |
| getTrainingLoadBalance(date?) | Four-week training load balance ending on the selected date |
| getMaxMetrics(date?) | Raw max metrics, including VO2 max, using a same-day range |
| getMaxMetricsRange(startDate, endDate) | Native date-range max metrics |
| getRacePredictions(options?) | Latest race predictions for the selected profile |
| getRacePredictionsRange(startDate, endDate, options?) | Native daily or monthly prediction history |
const dailyStatus = await GCClient.getDailyTrainingStatus('2026-10-08');
const status = await GCClient.getTrainingStatus('2026-10-08');
const loadBalance = await GCClient.getTrainingLoadBalance('2026-10-08');
const maxMetrics = await GCClient.getMaxMetrics('2026-10-08');
const maxMetricsHistory = await GCClient.getMaxMetricsRange(
'2026-10-01',
'2026-10-08'
);
const predictions = await GCClient.getRacePredictions();
const predictionHistory = await GCClient.getRacePredictionsRange(
'2026-09-01',
'2026-10-08',
{ aggregation: 'monthly' }
);Dates and successful empty responses follow the recovery API rules above.
Race history defaults to aggregation: 'daily'; it accepts only daily or
monthly. Its start-to-end calendar-date difference must be at most 366 days,
matching the Python client's guard. This is a client limit, rather than a
verified server limit; a 366-day difference includes 367 calendar dates.
The calculation is independent of daylight saving time.
Race methods read the current profile's socialProfile.displayName on each
query. To skip that additional profile request, supply { displayName: '...' }
in either method's options. This is the internal profile identifier, not an
email address or a user-defined account alias. It is encoded as one URL path
segment. Invalid options, dates, names and ranges are rejected before any
request. Resolving the profile on each query avoids retaining an identifier
from a previous account after credentials or an external session change.
The raw responses retain every device and unknown field. The exported
DailyTrainingStatusData and TrainingLoadBalanceData have optional device
maps supported by endpoint-labelled payloads in public recordings. The
TrainingStatusData, MaxMetricsData and RacePredictionsData types remain
open object-or-array shapes; live checks confirmed some root fields, while
nested schemas and device variations still need broader evidence.
See the training API source investigation
for the evidence and its limits.
Tests and session validation
npm ci --registry=https://registry.npmjs.org/
npm testAll 59 unit tests and the strict TypeScript consumer declaration check pass. The offline tests use synthetic fixtures, an Axios adapter and temporary token files; no credentials or network access are required. Coverage includes calendar and timezone boundaries, request contracts, profile changes, isolated token refreshes, retry limits, refresh failures and token file validation/permissions.
Existing local sessions were also used for separate read-only checks: one
China-region account and one global account each called all 13 new APIs for
2026-10-07 and the range 2026-10-01 to 2026-10-07. All 26 calls completed
successfully, including normal empty results. Both sessions were refreshed and
saved through the DSH plugin's DI runtime, which bridged authentication into the
SDK. See the sanitized validation report.
These checks cover the two selected accounts and dates. Native SDK DI session support, broader device coverage and the DSH dependency switch remain separate work. This fork's new APIs have not been published to npm.
v1.6.0 refactor
TODO:
- [x] New HttpClient class
- [x] Login and get user token
- [x] Garmin URLs works with
garmin.cnandgarmin.com - [x] Auto refresh Ouath2 token
- [x] Oauth1,Oauth2 token import and export.
- [x] Download Activity, countActivities, getActivities, getActivity, getUserProfile, getUserSettings
- [x] Upload Activity, delete Activity
- [ ] Implementation of other methods, such as Badge,Workout,Gear etc
- [ ] Handle MFA
- [x] Handle Account locked
- [x] Unit tests for recovery/training APIs, token refresh and token files
- [ ] Listeners
If something is not working, please check https://connect.garmin.com/status/ first.
Currently, most of previous features are working, but some of Rest API are not added, such as Gear,Workout,Badge etc. So if you need these features, please add a PR.
All of above work inspired by https://github.com/matin/garth. Many thanks.
A powerful JavaScript library for connecting to Garmin Connect for sending and receiving health and workout data. It comes with some predefined methods to get and set different kinds of data for your Garmin account, but also have the possibility to make custom requests GET, POST and PUT are currently supported. This makes it easy to implement whatever may be missing to suite your needs.
Prerequisites
This library will require you to add a configuration file to your project root called garmin.config.json containing your username and password for the Garmin Connect service.
{
"username": "[email protected]",
"password": "MySecretPassword"
}How to install
Use the tested 40-character commit recorded by the consuming plugin's lockfile
in place of <commit-sha>:
$ npm install 'garmin-connect@git+https://github.com/Likenttt/garmin-connect.git#<commit-sha>'How to use
const { GarminConnect } = require('garmin-connect');
// Create a new Garmin Connect Client
const GCClient = new GarminConnect({
username: '[email protected]',
password: 'MySecretPassword'
});
// Uses credentials from garmin.config.json or uses supplied params
await GCClient.login();
const userProfile = await GCClient.getUserProfile();Now you can check userProfile.userName to verify that your login was successful.
Reusing your session(since v1.6.0)
Save token to file and reuse it.
GCClient.saveTokenToFile('/path/to/save/tokens');Result:
$ ls /path/to/save/tokens
oauth1_token.json oauth2_token.jsonReuse token:
GCClient.loadTokenByFile('/path/to/save/tokens');Or just save your token to db or other storage.
const oauth1 = GCClient.client.oauth1Token;
const oauth2 = GCClient.client.oauth2Token;
// save to db or other storage
...Reuse token:
GCClient.loadToken(oauth1, oauth2);Reusing your session(deprecated)
This is an experimental feature and might not yet provide full stability.
After a successful login the sessionJson getter and setter can be used to export and restore your session.
// Exporting the session
const session = GCClient.sessionJson;
// Use this instead of GCClient.login() to restore the session
// This will throw an error if the stored session cannot be reused
GCClient.restore(session);The exported session should be serializable and can be stored as a JSON string.
A stored session can only be reused once and will need to be stored after each request. This can be done by attaching some storage to the sessionChange event.
GCClient.onSessionChange((session) => {
/*
Your choice of storage here
node-persist will probably work in most cases
*/
});Login fallback
To make sure to use a stored session if possible, but fallback to regular login, one can use the restoreOrLogin method.
The arguments username and password are both optional and the regular .login() will be
called if session restore fails.
await GCClient.restoreOrLogin(session, username, password);Events
sessionChangewill trigger on a change in the currentsessionJson
To attach a listener to an event, use the .on() method.
GCClient.on('sessionChange', (session) => console.log(session));There's currently no way of removing listeners.
Reading data
User info is not implemented yet. // TODO: Implement this function
Receive basic user information
GCClient.getUserInfo();Social Profile is not implemented yet. // TODO: Implement this function
Receive social user information
GCClient.getSocialProfile();Social Connections is not implemented yet. // TODO: Implement this function
Get a list of all social connections
GCClient.getSocialConnections();Device info is not implemented yet. // TODO: Implement this function
Get a list of all registered devices including model numbers and firmware versions.
GCClient.getDeviceInfo();getActivities(start: number, limit: number, activityType?: ActivityType, subActivityType?: ActivitySubType): Promise<IActivity[]>
Retrieves a list of activities based on specified parameters.
Parameters:
start(number, optonal): Index to start fetching activities.limit(number, optonal): Number of activities to retrieve.activityType(ActivityType, optional): Type of activity (if specified, start must be null).subActivityType(ActivitySubType, optional): Subtype of activity (if specified, start must be null).
Returns:
Promise<IActivity[]>: A Promise that resolves to an array of activities.
Example:
const activities = await GCClient.getActivities(
0,
10,
ActivityType.Running,
ActivitySubType.Outdoor
);getActivity(activity: { activityId: GCActivityId }): Promise<IActivity>
Retrieves details for a specific activity based on the provided activityId.
Parameters:
activity(object): An object containing theactivityIdproperty.activityId(GCActivityId): Identifier for the desired activity.
Returns:
Promise<IActivity>: A Promise that resolves to the details of the specified activity.
Example:
const activityDetails = await GCClient.getActivity({
activityId: 'exampleActivityId'
});News Feed is not implemented yet. // TODO: Implement this function
To get a list of activities in your news feed, use the getNewsFeed method. This function takes two arguments, start and limit, which is used for pagination. Both are optional and will default to whatever Garmin Connect is using. To be sure to get all activities, use this correctly.
// Get the news feed with a default length with most recent activities
GCClient.getNewsFeed();
// Get activities in feed, 10 through 15. (start 10, limit 5)
GCClient.getNewsFeed(10, 5);Download original activity data
Use the activityId to download the original activity data. Usually this is supplied as a .zip file.
const [activity] = await GCClient.getActivities(0, 1);
// Directory path is optional and defaults to the current working directory.
// Downloads filename will be supplied by Garmin.
GCClient.downloadOriginalActivityData(activity, './some/path/that/exists');Upload activity file
Uploads an activity file as a new Activity. The file can be a gpx, tcx, or fit file. If the activity already exists, the result will have a status code of 409.
Upload fixed in 1.4.4, Garmin changed the upload api, the response detailedImportResult doesn't contain the new activityId.
const upload = await GCClient.uploadActivity('./some/path/to/file.fit');
// not working
const activityId = upload.detailedImportResult.successes[0].internalId;
const uploadId = upload.detailedImportResult.uploadId;Upload activity image
Uploads an image to activity
const [latestActivty] = await GCClient.getActivities(0, 1);
const upload = await GCClient.uploadImage(
latestActivty,
'./some/path/to/file.jpg'
);Delete activity image
Delete an image from activity
const [activity] = await GCClient.getActivities(0, 1);
const activityDetails = await GCClient.getActivityDetails(activity.activityId);
await GCClient.deleteImage(
activity,
activityDetails.metadataDTO.activityImages[0].imageId
);getSteps(date?: Date): Promise<number>
Retrieves the total steps for a given date.
Parameters:
date(Date, optional): Date of the steps information requested; defaults to today if no date is supplied.
Returns:
Promise<number>: A Promise that resolves to the total steps for the specified date.
Example:
const totalSteps = await GCClient.getSteps(new Date('2020-03-24'));getSleepData(date: string): Promise<SleepData>
Retrieves all sleep data for a given date
Parameters:
date(Date, optional): Date of information requested, this will default to today if no date is supplied
Returns:
Promise<SleepData>: A Promise that resolves to an object containing detailed sleep information.dailySleepDTO(object): Information about the user's daily sleep.id(number): The unique identifier of the sleep record.userProfilePK(number): The user's profile identifier.calendarDate(string): The date of the sleep record.- ...
sleepMovement(array): An array of sleep movement data.remSleepData(boolean): Indicates whether REM sleep data is available.sleepLevels(array): An array of sleep levels data.restlessMomentsCount(number): Count of restless moments during sleep.- ...
Example:
const detailedSleep = await GCClient.getSleepDuration(new Date('2020-03-24'));getSleepDuration(date: string): Promise<{hours: number, minutes: number}
Retrieves hours and minutes slept for a given date
Parameters:
date(Date, optional): Date of information requested, this will default to today if no date is supplied
Returns:
Promise<{hours: string, minutes: string }>: A Promise that resolves to an object containing information about the sleep durationhours(string): Number of hoursminutes(string): Number of minutes
Example:
const detailedSleep = await GCClient.getSleepDuration(new Date('2020-03-24'));getDailyWeightData(date?: Date): Promise<number>
Retrieves the daily weight and converts it from grams to pounds.
Parameters:
date(Date, optional): Date of information requested. Defaults to the current date.
Returns:
Promise<number>: A Promise that resolves to the daily weight converted from grams to pounds.
Throws:
Error: If valid daily weight data cannot be found for the specified date.
Example:
const weightData = await GCClient.getDailyWeightData(new Date('2023-12-25'));getDailyWeightInPounds(date?: Date): Promise<number>
Retrieves the daily weight in pounds for a given date.
Parameters:
date(Date, optional): Date of information requested; defaults to today if no date is supplied.
Returns:
Promise<number>: A Promise that resolves to the daily weight in pounds.
Example:
const weightInPounds = await GCClient.getDailyWeightInPounds(
new Date('2020-03-24')
);getDailyHydration(date?: Date): Promise<number>
Retrieves the daily hydration data and converts it from milliliters to ounces.
Parameters:
date(Date, optional): Date of the requested information. Defaults to the current date.
Returns:
Promise<number>: A Promise that resolves to the daily hydration data converted from milliliters to ounces.
Throws:
Error: If valid daily hydration data cannot be found for the specified date or if the response is invalid.
Example:
const hydrationInOunces = await GCClient.getDailyHydration(
new Date('2023-12-25')
);getGolfSummary(): Promise<GolfSummary>
Retrieves a summary of golf scorecard data.
Returns:
Promise<GolfSummary>: A Promise that resolves to the golf scorecard summary.
Example:
const golfSummary = await GCClient.getGolfSummary();getGolfScorecard(scorecardId: number): Promise<GolfScorecard>
Retrieves golf scorecard data for a specific scorecard.
Parameters:
scorecardId(number): Identifier for the desired golf scorecard.
Returns:
Promise<GolfScorecard>: A Promise that resolves to the golf scorecard data.
Example:
const scorecardId = 123; // Replace with the desired scorecard ID
const golfScorecard = await GCClient.getGolfScorecard(scorecardId);getHeartRate(date?: Date): Promise<HeartRate>
Retrieves daily heart rate data for a given date.
Parameters:
date(Date, optional): Date of the heart rate data requested; defaults to today if no date is supplied.
Returns:
Promise<HeartRate>: A Promise that resolves to the daily heart rate data.
Example:
const heartRateData = await GCClient.getHeartRate(new Date('2020-03-24'));Modifying data
Update activity is not implemented yet. // TODO: Implement this function
const activities = await GCClient.getActivities(0, 1);
const activity = activities[0];
activity['activityName'] = 'The Updated Name';
await GCClient.updateActivity(activity);Delete an activity
Deletes an activty.
const activities = await GCClient.getActivities(0, 1);
const activity = activities[0];
await GCClient.deleteActivity(activity);updateHydrationLogOunces(date?: Date, valueInOz: number): Promise<WaterIntake>
Adds a hydration log entry in ounces for a given date.
Parameters:
date(Date, optional): Date of the log entry; defaults to today if no date is supplied.valueInOz(number): Amount of water intake in ounces. Accepts negative number.
Returns:
Promise<WaterIntake>: A Promise that resolves to the hydration log entry.
Example:
const hydrationLogEntry = await GCClient.addHydrationLogOunces(
new Date('2020-03-24'),
16
);updateWeight(date = new Date(), lbs: number, timezone: string): Promise<UpdateWeight>
Updates weight information
Parameters:
date(optional): Date object representing the weight entry date. Defaults to the current date if not provided.lbs(number): Weight value in pounds.timezone(string): String representing the timezone for the weight entry.
Returns:
Promise<UpdateWeight>: A Promise that resolves to the result of the weight update.
Example:
await GCClient.updateWeight(undefined, 202.9, 'America/Los_Angeles');Add workout
To add a custom workout, use the addWorkout or more specifically addRunningWorkout.
GCClient.addRunningWorkout('My 5k run', 5000, 'Some description');Will add a running workout of 5km called 'My 5k run' and return a JSON object representing the saved workout.
Schedule workout
To add a workout to your calendar, first find your workout and then add it to a specific date.
const workouts = await GCClient.getWorkouts();
const id = workouts[0].workoutId;
GCClient.scheduleWorkout({ workoutId: id }, new Date('2020-03-24'));This will add the workout to a specific date in your calendar and make it show up automatically if you're using any of the Garmin watches.
Delete workout
Deleting a workout is very similar to scheduling one.
const workouts = await GCClient.getWorkouts();
const id = workouts[0].workoutId;
GCClient.deleteWorkout({ workoutId: id });Custom requests
This library will handle custom requests to your active Garmin Connect session. There are a lot of different url's that is used, which means that this library probably wont cover them all. By using the network analyze tool you can find url's that are used by Garmin Connect to fetch data.
Let's assume I found a GET requests to the following url:
https://connect.garmin.com/modern/proxy/wellness-service/wellness/dailyHeartRate/22f5f84c-de9d-4ad6-97f2-201097b3b983?date=2020-03-24The request can be sent using GCClient by running
// You can get your displayName by using the getUserInfo method;
const displayName = '22f5f84c-de9d-4ad6-97f2-201097b3b983';
const url =
'https://connect.garmin.com/modern/proxy/wellness-service/wellness/dailyHeartRate/';
const dateString = '2020-03-24';
GCClient.get(url + displayName, { date: dateString });and will net you the same result as using the provided way
GCClient.getHeartRate();Notice how the client will keep track of the url's, your user information as well as keeping the session alive.
Limitations
Many responses from Garmin Connect are missing type definitions and defaults to unknown. Feel free to add types by opening a pull request.
For now, this library only supports the following:
- Get user info
- Get social user info
- Get heart rate
- Set body weight
- Get list of workouts
- Add new workouts
- Add workouts to you calendar
- Remove previously added workouts
- Get list of activities
- Get details about one specific activity
- Get the step count
- Get earned badges
- Get available badges
- Get details about one specific badge
