Is there a way to export bulk data to TCX or GPX files? Seems like I can only bulk export to .csv and individual activities to GPX.

I'm looking for a way to import all of my activities from Garmin into Strava.  I see a bulk export option, but it only seems to allow for .csv exports.  I want the GPX data.  My accounts are linked but it only imported from the date of Strava account creation and onward.

Thanks,

Mike

Top Replies

All Replies

  • I get a similar error response.  The script no longer works. :( 

    The option at https://www.garmin.com/en-US/account/datamanagement/exportdata/ is to submit a request for data export, with no option to specify filters. Open mouth

    This is the code I was pasting into the Firefox console window.  This last worked in April 2023 when I last performed a bulk download of all cycling activities.

    jQuery.getJSON(
    'connect.garmin.com/.../activities
    function(act_list)
    {
    var t=0;
    act_list.forEach(
    function(act)
    {
    setTimeout(function() {
    console.dir(act['activityId'], act['activityName'], act['startTimeLocal']);
    location = 'connect.garmin.com/.../' + act['activityId'];
    }, t+=5000);
    }
    );
    }
    );

  • This looks like the error message:

    Source map error: Error: request failed with status 403 Resource URL: https://connect.garmin.com/web-react/static/js/256.78ca20d9.chunk.js Source Map URL: 256.78ca20d9.chunk.js.map

  • Earlier it worked, so older posts had other problems.

    But now - yes, with latest change DI-Backend and Authorization headers are required. Same for: CSV export.

    jQuery.ajaxSetup({headers:{
     'DI-Backend':'connectapi.garmin.com',
     'Authorization':'Bearer '+JSON.parse(localStorage.token).access_token
    }})

    So old scripts won't work until rewritten with those headers.

  • Updated script to download activities.

    Run with the Web opened: F12 → Console.
    Set activities' limit=2 and wait time t+=5000 as needed.
    Add filters from advanced search to the first link.

    h={
     'DI-Backend':'connectapi.garmin.com',
     'Authorization':'Bearer '+JSON.parse(localStorage.token).access_token
    }

    fetch('https://connect.garmin.com/activitylist-service/activities/search/activities?limit=2',
    {'headers':h}).then((r)=>r.json()).then((all)=>{
     t=0
     all.forEach(async (a)=>{
      await new Promise(s=>setTimeout(s,t+=5000))
      fetch('https://connect.garmin.com/download-service/export/gpx/activity/'+a.activityId,
      {'headers':h}).then((r)=>r.blob()).then((b)=>{
       console.dir(a.activityId)
       f=document.createElement('a')
       f.href=window.URL.createObjectURL(b)
       f.download=a.activityId
       f.click()
      })
     })
    })

     

    Download link can be edited for FIT or GPX or TCX or CSV:

    • files/activity
    • export/gpx/activity
    • export/tcx/activity
    • export/csv/activity
  • I manage to download the files, however the files are downloaded without any file extension. I have tried adding .fit to the f.download, but the problem remains the same as the file is unreadable/corrupted.

  • Browser adds extension, but maybe depends which is used.

    Files are the same as downloaded manually, so .fit is always packed to .zip, others are unpacked.

  • The case is the same accross Chrome, Edge and Firefox. The files are downloaded without extension and has the Filetype "File". I am running windows 11 and the code I enter into f12 => Console is

    h={
    'DI-Backend':'connectapi.garmin.com',
    'Authorization':'Bearer '+JSON.parse(localStorage.token).access_token
    }
    fetch('connect.garmin.com/.../activities
    {'headers':h}).then((r)=>r.json()).then((all)=>{
    t=0
    all.forEach(async (a)=>{
    await new Promise(s=>setTimeout(s,t+=5000))
    fetch('connect.garmin.com/.../' a.activityId,
    {'headers':h}).then((r)=>r.blob()).then((b)=>{
    console.dir(a.activityId)
    f=document.createElement('a')
    f.href=window.URL.createObjectURL(b)
    f.download=a.activityId
    f.click()
    })
    })
    })

    Any other suggestions?

  • Then maybe system adds it, but that's minor issue, if there is no error in Console and files are downloaded properly then simply add correct extension manually.

    As I wrote .fit are always packed to .zip, so obviously add .zip extension, other formats are served raw, for those add standard extension.

    Or if files are way too small and all same size, then add .txt to read error (it would be visible also in Console).

  • Thanks guys for the great script and on the work to keep it working!

    I have made a few changes to make it easier to use. and the downloaded files will have the correct extension.

    
    // Specify the file extension (change 'fit' below to 'fit', 'tcx', 'gpx', or 'csv' depending on which format you want to export)
        const fileExtension = 'fit';
    
    // Specify how many files to retrieve (change the 2 below to the number of activities you want to export)
        const limit = 2;
    
    // There should be no reason to change anything below this point
    if (localStorage.token) {
        // Define the headers object with the required headers
        const h = {
            'DI-Backend': 'connectapi.garmin.com',
            'Authorization': 'Bearer ' + JSON.parse(localStorage.token).access_token
        };
    
        let downloadCount = 0; // Counter to track the number of file downloads
    
        fetch(`connect.garmin.com/.../activities, {'headers':h})
            .then((r) => r.json())
            .then((all) => {
                let t = 0;
                all.forEach(async (a) => {
                    await new Promise(s => setTimeout(s, t += 5000));
                    let fetchUrl, downloadExtension;
                    if (fileExtension === 'fit') {
                        fetchUrl = `connect.garmin.com/.../${a.activityId}`;
                        downloadExtension = 'zip'; // FIT files are downloaded as ZIP files
                    } else {
                        fetchUrl = `connect.garmin.com/.../${a.activityId}`;
                        downloadExtension = fileExtension;
                    }
                    fetch(fetchUrl, {'headers':h})
                        .then((r) => r.blob())
                        .then((b) => {
                            downloadCount++; // Increment the download count
                            console.log(`Downloading file ${downloadCount}: ${a.activityId}.${downloadExtension}`);
                            const f = document.createElement('a');
                            f.href = window.URL.createObjectURL(b);
                            f.setAttribute('download', `${a.activityId}.${downloadExtension}`); // Set the download attribute with appropriate file extension
                            f.click();
                        });
                });
            });
    } else {
        console.error("Token not found in localStorage!");
    }