[SOLVED] Discrepency between Python and Javascript API connections in VSCode

I am not able to connect to the api and modify wellness data as an example in this case using Javascript executed with node.js in VSCode, but I can connect and make changes with my python code. Trying to figure out the possible underlying issue here.

This is my javascript code

const API_KEY = "myapikeyhere";
const ATHLETE_ID = 0; // Use 0 to fetch your own data

// Get today's date in YYYY-MM-DD format
const today = new Date().toISOString().split('T')[0];

// Construct the API URL
const url = `https://intervals.icu/api/v1/athlete/${ATHLETE_ID}/wellness/${today}`;

// JSON payload with updated hrvSDNN value
const wellnessData = {
    "hrvSDNN": 80  // Replace with actual HRV SDNN value
};

// Convert data to JSON
const body = JSON.stringify(wellnessData);

// Function to send PUT request
async function updateWellness() {
    try {
        const response = await fetch(url, {
            method: "PUT",
            headers: {
                "Authorization": `Basic ${Buffer.from(API_KEY).toString("base64")}`,
                "Content-Type": "application/json"
            },
            body: body
        });

        // Log the status and response
        console.log("Status Code:", response.status);
        const data = await response.json();
        console.log("API Response:", data);

    } catch (error) {
        console.error("Error updating wellness data:", error);
    }
}

// Run the function
updateWellness();

and this returns:

Status Code: 401
API Response: { status: 401, error: 'Unauthorized' }

Here is my python code:

import requests
from datetime import datetime

ATHLETE_ID = 0  # Change this if necessary
API_KEY = "myapikeyhere"
DATE = datetime.today().strftime('%Y-%m-%d')

url = f'https://intervals.icu/api/v1/athlete/{ATHLETE_ID}/wellness/{DATE}'

DATA = {
    "hrvSDNN": 88
}

headers = {
    "authorization": f"basic {API_KEY}",
    "content-type": "application/json"
}

response = requests.put(url, json=DATA, auth=('API_KEY', API_KEY))
if response.status_code == 200:
    print("Connection successful!")
    print(response.content)
else:
    print(f"Failed to connect. Status code: {response.status_code}")
    print(response.text)

and this outputs the following and updates my wellness data on my calendar page:

Connection successful!
b'{"wellness data": data...}'

Why the difference, going crazy trying to figure this out!

You could try something like that. I think the User API_KEY is missing.


let apiUrl = `https://intervals.icu/api/v1/athlete/${athleteId}/wellness/${formattedDate}`;

let payload = {
  "mood": moodValue,
  "motivation": motivationValue
};

// Basic Auth Informationen
let username = "API_KEY";  // you need this!
let password = "yoursecretkey";
let credentials = `${username}:${password}`;
let encodedCredentials = btoa(credentials); // Base64-Encodierung der Anmeldedaten

let request = new Request(apiUrl);
request.method = "PUT";
request.headers = {
  "accept": "*/*",
  "Content-Type": "application/json",
  "Authorization": `Basic ${encodedCredentials}`
};
request.body = JSON.stringify(payload);

let putResponse = await request.load();

I have no knowledge of Python but also think that you´re making a mistake with basic authehtication. The user is always and for everyone the string ´API_KEY´. The pw is your api-key when using basic auth.

Are you referring to me the OP or @R2Tom?

We were both answering to you :slight_smile:
In Python you have the user ‘API_KEY’ (first argument)

auth=('API_KEY', API_KEY))

While you don’t have it in js anywhere. The username is required and always API_KEY

It’s in this line right here:

“Authorization": Basic ${Buffer.from(API_KEY).toString("base64")},

You probably don’t understand this right.
“API_KEY” is the string what you have to use for the user name. It doesn’t matter how you name your variables.
This is my code

// Basic Auth Informationen
let username = "API_KEY";  // you need this!
let password = "yoursecretkey";
let credentials = `${username}:${password}`;

This is your code without the string

API_KEY = "myapikeyhere"

In Python you have this string

auth=('API_KEY',
1 Like

@R2Tom It seems you have this working but for clarities sake, your API_KEY is absolutely not your user name or your user id. If you look on your settings page there is an “Athlete ID” and “API KEY”. There is then apart from those two values your user name which is used to login into intervals through the browser, which is typically or always an email address I assume. Finally there is of course your password used with your user name to login to intervals from the browser.

Are you using your user name and password like you would use to log in from the browser?

The username is “API_KEY” and the password your API key

username = “API_KEY”

that’s not, replace “API_KEY” with your api key, it is verbatim, don’t change and use direct.

1 Like

Holly shit finally!!! Thank you!!! @Ben

You´re not the first one struggling with this. But it took some time to get the message through :smile:.
Have fun now!

I realize too that I didn’t even need the header in python code. I see what’s happening now.

1 Like

For propserity here is my connection class using requests library

from requests.auth import HTTPBasicAuth
class Auth:
    token: None
    def interval_api_call(
        self,
        url: str,
        method: str = "GET",
        headers=None,
        data: list | dict[typing.Any, typing.Any] | None = None,
    ) -> dict:
        if data is None:
            data = {}
        if headers is None:
            headers = {}
        auth = HTTPBasicAuth("API_KEY", self.token)
        # make sure we strip initial slash
        if url and url[0] == "/":
            url = url[1:]
        if url:
            url = "/" + url

        url = f"https://intervals.icu/api/v1/athlete/{self.aid}{url}"
        if data:
            headers["Content-Type"] = "application/json"
        if self.verbose:
            click.echo(f"URL: {url}")
            click.echo(f"Method: {method}")
            click.echo(f"Headers: {headers}")
            click.echo(f"Data: {data}")

        ret = requests.request(method, url, json=data, headers=headers, auth=auth)
        if ret.status_code != 200:
            raise click.ClickException(
                f"error {method.upper()}ing on {url}: {ret.text}"
            )
        if ret.text:
            return ret.json()
        return {}