Skip to content

Instantly share code, notes, and snippets.

@khskekec
Last active November 5, 2025 10:58
Show Gist options
  • Select an option

  • Save khskekec/6c13ba01b10d3018d816706a32ae8ab2 to your computer and use it in GitHub Desktop.

Select an option

Save khskekec/6c13ba01b10d3018d816706a32ae8ab2 to your computer and use it in GitHub Desktop.
HTTP dump of Libre Link Up used in combination with FreeStyle Libre 3
@sgmoore
Copy link

sgmoore commented Dec 23, 2024

when i use https://api-eu.libreview.io/llu/connections i get a empty date []

I presume you mean you get empty data. That usually means that you do not have any followers/connections which in turn means you have not followed yourself. (This confusing step is required as LibreLinkUp was designed for other people to see your data, not for you to see your own data, so the workaround is to add yourself as a follower to your own main account)

and i tried https://api-eu.libreview.io/llu/connections/(user_id as sha256)/graph get message MissingCachedUser

It should be patientId (not user id) and you get this information from data returned by https://api-eu.libreview.io/llu/connections. So if you don't have any followers/connections I don't know what id you are using.

what is headers required

This is a snippet of my C# code. This works for me even though it has not been changed to include the header mentioned by gui-dos , namely

A new "Account-Id" HTTP header is now required, passing the SHA256 digest of a user's id as a 64-char hexadecimal string.

Also I'm still using version 4.7 but others have said they need to use version 4.12.0

    var request = new HttpRequestMessage
	{
		Method = method,
		RequestUri = new Uri(url),
		Headers =
		{
			{ "product"         , "llu.android"         },
			{ "version"         , "4.7"                 },
			{ "Accept"          , "application/json"    },
			{ "cache-control"   , "no-cache"            },
			{ "accept-encoding" , "gzip"                },
			{ "connection"      , "Keep-Alive"          },
			{ "user-agent"      , "Mozilla/5.0 (Windows NT 10.0; rv:129.0) Gecko/20100101 Firefox/129.0" }
		}
	};

	if (!String.IsNullOrEmpty(token))
		request.Headers.Add("Authorization", $"Bearer {token}");

@Mohmedsabry
Copy link

Mohmedsabry commented Dec 23, 2024

when i use https://api-eu.libreview.io/llu/connections i get a empty date []

I presume you mean you get empty data. That usually means that you do not have any followers/connections which in turn means you have not followed yourself. (This confusing step is required as LibreLinkUp was designed for other people to see your data, not for you to see your own data, so the workaround is to add yourself as a follower to your own main account)

and i tried https://api-eu.libreview.io/llu/connections/(user_id as sha256)/graph get message MissingCachedUser

It should be patientId (not user id) and you get this information from data returned by https://api-eu.libreview.io/llu/connections. So if you don't have any followers/connections I don't know what id you are using.

what is headers required

This is a snippet of my C# code. This works for me even though it has not been changed to include the header mentioned by gui-dos , namely

A new "Account-Id" HTTP header is now required, passing the SHA256 digest of a user's id as a 64-char hexadecimal string.

Also I'm still using version 4.7 but others have said they need to use version 4.12.0

    var request = new HttpRequestMessage
	{
		Method = method,
		RequestUri = new Uri(url),
		Headers =
		{
			{ "product"         , "llu.android"         },
			{ "version"         , "4.7"                 },
			{ "Accept"          , "application/json"    },
			{ "cache-control"   , "no-cache"            },
			{ "accept-encoding" , "gzip"                },
			{ "connection"      , "Keep-Alive"          },
			{ "user-agent"      , "Mozilla/5.0 (Windows NT 10.0; rv:129.0) Gecko/20100101 Firefox/129.0" }
		}
	};

	if (!String.IsNullOrEmpty(token))
		request.Headers.Add("Authorization", $"Bearer {token}");

thank you i tried your code and it fails after login just add Account-id header it works fine and upgrade version to 4.12 it must

@samuele4414
Copy link

hi i have some problem while getting the data from this code
async function getConnections() {
try {
console.log("DIO DI CHIAMATA COLLECTIONS CHE NON FUNZIONA", ACCOUNT_ID);
const response = await fetch(${API_BASE_URL}/llu/connections, {
method: "GET",
headers: {
...HEADERS,
Authorization: Bearer ${AUTH_TOKEN},
"account-id": ACCOUNT_ID
}
});

const data = await response.json();

if (data.message && data.message === "AccountIdMismatch") {
  throw new Error("Account ID does not match");
}

} catch (error) {
console.error("❌ Errore", error.message);
return null;
}
}

i'm able to login with this code

async function login() {
try {
const response = await fetch(${API_BASE_URL}/llu/auth/login, {
method: "POST",
headers: HEADERS,
body: JSON.stringify({
email: CREDENTIALS.email,
password: CREDENTIALS.password
})
});

const text = await response.text();
console.log("🔍 Risposta ricevuta:", text);

const data = JSON.parse(text);

if (data.status !== 0 || !data.data.authTicket) {
  throw new Error("Login fallito. Controlla le credenziali.");
}

AUTH_TOKEN = data.data.authTicket.token;
ACCOUNT_ID = data.data.user.id;
console.log("✅ Token ottenuto:", AUTH_TOKEN);
console.log("✅ Account ID:", ACCOUNT_ID);

} catch (error) {
console.error("❌ Errore nel login:", error.message);
}
}

@Kanomade1
Copy link

I was able to log in and get data using a script on one server. But on a different server , I got a status code 476. What does this mean? Here is the login POST response statusCode, body and header:
{
"statusCode": 476,
"body": "6ZAr80PColog2gvxxlfRUEZMITB2vCQUmFQlPOmfW3u6Ov3lJbcNm0eCPDJU7851qOf9BjnQXqRdUsP3busSDoiavUoRhZLIAWXQvgDqsRGReXboIKOe3EPsuuOgsLMfCErCus22uRaR7iq3LkMF01Z2Id5nc9mdJgm36a4CUlEOBI8pDQ58UGWQnCsSe9cuRjoFZ1ltvTHm9WLFc7q2x7l1CJnEJj55q6T5Qgq7GLKN3GfYmYmSaRsHRA7loeBEXxyndDmPeWUCuO1vzypuB5R2MKkhADE8mtx8lvz2PoyMJqoskSDCWjnDCERWw606N4sn6x8yZdeky2mFH1ZFBo2oAcZLk5MD8vQFzbAAdZLxz2DvgX4wBzrzmFL7WFlIzgU32fWK3VtRxBRTgcA0MErG23LiNGfAVWZKOwXHhAZX1qOCG1gTcWrb8iRpeEK8P693cxgieoMeKS46O252EBrQMAer80NeMnQRIFM6LbUQwLkFjm64YTh7RcESZIJSr5t3ziXAoK1F8UZRu8yNnoLNp1W2e8ubdmlGURANm7JmeIB5ia9yczFevfXHcvKhD62b30nXXdGBo9LLTdSIhsr2osujFcBoJfZADfA9dZSVQxBf96i3U9Q7v3gr0t920uZZjljP8OHEr4sXgNKOoupBzLx1aaJDGH9TTpv12kr7WvWSRchhChy5eHLQzBOp6ih5ygBIdcN19BVMnvyqZTXJbVok97d5wfXPB8q6ztZSn4C0BeUATtNSWVWlt08OOHxr0KTlbz2S0b743xjqRB0pVY0toCQDjHYptLxuCsc97W6lMndFDrr8j60eXZUqMyV8xMOV8EpFGoelL96K4ePf4zxe7Oi0FNfZYQJDqAxrESBhQGiq0YDsKVRMTrHtRQ5C4KydUwGbPReHL2CT19UoalUIWTWMPeczsjK3nf26pGRwFDKrRZqeAKxaGa8wAC9aka0oy30t4AwLXpOc41MMn48yPmZkeUi4vudbuwBdw1nk9HX7NmgC5bAzl5gF71gC75w2ZE8BJ3HN2M26CV13MDC09ffsYkG7cCjkKo1",
"headers": {
"date": "Mon, 16 Jun 2025 21:38:00 GMT",
"content-type": "text/plain",
"content-length": "1067",
"connection": "close",
"retry-after": "65620",
"set-cookie": [
"__cf_bm=LbkPulD4yJEZdYnMaJeTYTzxAycDTI7JR8z8OfHwYDY-1750109880-1.0.1.1-gVJOKjdzDE7pJ9i1iKrk5PDP2ZDKAnO3Fmajl7RBgNHohol6iMFvuAejNk.jFcID.CF7V0m4ku3ZmYIENDt__W_8uw3oAS7NiABYhhqDOF4; path=/; expires=Mon, 16-Jun-25 22:08:00 GMT; domain=.libreview.io; HttpOnly; Secure; SameSite=None"
],
"cache-control": "no-store",
"server": "cloudflare",
"cf-ray": "950d73a39cb246de-DFW",
"alt-svc": "h3=":443"; ma=86400"
},

@sgmoore
Copy link

sgmoore commented Jun 17, 2025

476 isn't a standard statusCode, but may be used by Cloudflare which is where the error comes from.

"retry-after": "65620",

AFAIK this means 65620 seconds or over 18 hours, which is a long time to wait before retrying!

I could be an Intermittent Cloudflare server problem or it could be you are using an IP Address that cloudflare don't like.

@nawazmohammed-prosperts

Any idea how I can get the historic data for the last 90 days and also the reports such as daily patterns, time in range etc which are shown in the LibreLink app?

@adamlounds
Copy link

Quirk of the day: today I got {"message":"invalid or expired jwt"} back from POST /llu/auth/login. My credentials were correct, but I needed to accept terms and conditions in the librelinkup app ¯\_(ツ)_/¯

@sgmoore
Copy link

sgmoore commented Sep 6, 2025

Quirk of the day: today I got {"message":"invalid or expired jwt"} back from POST /llu/auth/login. My credentials were correct, but I needed to accept terms and conditions in the librelinkup app ¯_(ツ)_/¯

If you haven't stored all your responses then you can't verify what I am about to say, but I don't think the message was as a response from the `POST /llu/auth/login' , but the next stage.

It seems to be a common issue and most of the third party routines I've seen don't seem to handle the login process correctly, but assume that if you get an HttpStatus of OK when you can proceed as normal, but you are supposed to look at the status inside the response message and if this is 4, look to see if you need to perform an extra step.

So you should have gotten a response which looks something like


{
  "status": 4,
  "data": {
    "step": {
      "type": "pp",
      "componentName": "AcceptDocument",
      "props": {
        "reaccept": true,
        "titleKey": "Common.privacyPolicy",
        "type": "pp"
      }
    },

   ...

  }
}

If you get a status 4 and a step in your response, you are supposed to post a request to auth/continue/xxxx where xxxx is the type (so in this case the request would go to auth/continue/pp .

You might have several of these steps until you get a response with status 0 which means you can continue as normal.

@adamlounds
Copy link

Yeah, part of me is really annoyed at not capturing everything - but the other half is happy to have fixed my nightscout feed after a power outage.
As it says in an earlier comment, the authtoken is valid for 6 months, so my code almost never calls the earlier stages.

@MrAda
Copy link

MrAda commented Sep 12, 2025

What a great thread and useful information! Thanks to all that contribute. I have been looking to get instant access to my own glucose data as well as a range of values and it seems daunting. All of the data is not "now". I would like to know if anyone has been able to download the "csv" file programmatically? I want to integrate all relevant information into my app. The CSV file is what I want to use. I just don't want to have to download it manually and have my app post process it. I would like for it to work automatically. I was wondering if anybody is successful in getting data from "notifications" request and "config" request. "notifications" seems to want a "connectionId" which I cannot find from the data I receive from abbott. I am not sure if I am running the "config" request right. The URL is "https://api.libreview.io/llu/config/country" Maybe it needs to be "https://api.libreview.io/llu/config/us"? Any help would be greatly appreciated. By the way I have done an app in C++ using Qt.

@sgmoore
Copy link

sgmoore commented Sep 12, 2025

I would like to know if anyone has been able to download the "csv" file programmatically?

I looked at this but the problem is that the api call (an export post request) requires a captcha response - in other words you have to solve the captcha before you can request the csv.

@MrAda
Copy link

MrAda commented Sep 12, 2025

@sgmoore That is true.

@sgmoore
Copy link

sgmoore commented Sep 12, 2025

I am not sure if I am running the "config" request right. The URL is "https://api.libreview.io/llu/config/country"

https://api.libreview.io/llu/config/country?country=us

That's a simple get request without validation etc, so you can just click on that link to test it.

notifications" seems to want a "connectionId" which I cannot find from the data I receive from abbott

I have not tried that, but I would assume this is a patientID from one of the connections you get from back when you call get llu/connections (which should be documented at the start of this gist).

@MrAda
Copy link

MrAda commented Sep 14, 2025

@sgmoore thank you for the first one. It works now. Don't know how I missed that. I will try the second one in a sec...

@MrAda
Copy link

MrAda commented Sep 14, 2025

I used my patient id for the notifications request and got "Content Not Found Error" from Qt's error response class.

@MrAda
Copy link

MrAda commented Sep 14, 2025

I would like to know if anyone has been able to download the "csv" file programmatically?

I looked at this but the problem is that the api call (an export post request) requires a captcha response - in other words you have to solve the captcha before you can request the csv.

I get this but maybe I can intercept this in my app and then pass the "proof" back to libreview?

@knolleary
Copy link

I have been using this successfully for some time now, although I've had to set the version header to 4.10.0 to work. I've received notifications from the LibreLink app that as of October 8th, version 4.15 and below will no longer receive data. I've tried updating the version header to 4.16.0 , but then get the 'missing header' error. Does anyone know what the latest working headers are?

@MrAda
Copy link

MrAda commented Oct 3, 2025

@knolleary I just checked the app I created and I am still using 4.7 version with no issue. I am on Android so I don't know if that matters.

@SteveCEvans
Copy link

Up until today I'd been using version 4.7, but now see the same issue as reported above:

{"data":{"minimumVersion":"4.16.0"},"status":920}

If I set the version to 4.16, I get:

{"message":"RequiredHeaderMissing"}

This is in the -eu2 region.

@knolleary
Copy link

@SteveCEvans likewise. As per my previous message, I have been receiving in-app warnings to upgrade client version before the 8th October... ie today. And sure enough, access to the API has stopped working today.

Hopefully someone can figure out the necessary headers to keep this working

@SteveCEvans
Copy link

Fixed it! I'm using micro python.

Prior to 4.16 my header for fetching graph data was:

        auth_header = {
            'Accept'        : 'application/json',
            'Authorization' : 'Bearer ' + self._user_token,
       }

And now I have:

        auth_header = {
            'Accept'        : 'application/json',
            'Authorization' : 'Bearer ' + self._user_token,
            'Account-Id'    : self._account_id
        }

Where:

            digest = uhashlib.sha256(self._user_id.encode('utf-8')).digest()
            self._account_id = ubinascii.hexlify(digest).decode('utf-8')

I hope that makes sense.

@knolleary
Copy link

@SteveCEvans well done - can confirm that works for me (intrigued to know how you figured that out...)

For reference, in node.js land, this translates to:

const { createHash } = require('crypto');
const AccountIdHeader = createHash('sha256').update(userId).digest('hex');

userId can be obtained from the response of the initial login HTTP request.

@sgmoore
Copy link

sgmoore commented Oct 9, 2025

(intrigued to know how you figured that out...)

Perhaps by reading this gist? This change has been rolling out slowly since last year - See https://gist.github.com/khskekec/6c13ba01b10d3018d816706a32ae8ab2?permalink_comment_id=5330300#gistcomment-5330300

@knolleary
Copy link

@sgmoore ha - good point. So many replies and collapsed comments, I had missed that.

@SteveCEvans
Copy link

@SteveCEvans well done - can confirm that works for me (intrigued to know how you figured that out...)

For reference, in node.js land, this translates to:

const { createHash } = require('crypto');
const AccountIdHeader = createHash('sha256').update(userId).digest('hex');

userId can be obtained from the response of the initial login HTTP request.

It was actually mentioned above at https://gist.github.com/khskekec/6c13ba01b10d3018d816706a32ae8ab2?permalink_comment_id=5358133#gistcomment-5358133

@MrAda
Copy link

MrAda commented Oct 10, 2025

@SteveCEvans "I hope that makes sense." Not really. In my version when I get "https://api.libreview.io/llu/connections/" + PATIENT_ID + "/graph", my headers have "Accept", "Authorization", "product" and "version". I changed version to 4.16.0 and it gave me a protocol error. So as I understand it you had to add "Account-Id". The value you are sending out is an sha256 checksum of the PATIENT_ID?

That worked for me. Thanks!!

@SteveCEvans
Copy link

I only send the version header for the login. Once I have the user id and token it’s no longer needed.

@MrAda
Copy link

MrAda commented Oct 11, 2025

@SteveCEvans I'll give that a try.

@xfoguet
Copy link

xfoguet commented Oct 13, 2025

For all those GOphers:

// getAccountID returns the account ID for the given user ID
func getAccountID(userID string) string {
	hash := sha256.Sum256([]byte(userID))
	return hex.EncodeToString(hash[:])
}

@gjkoolen
Copy link

I struggled with URL's, but I got it to work.
So I summarize here.
(I live in Europe)
To request the jwt token and Patient_ID, I use POST to "https://api-eu.libreview.io/llu/auth/login"
For client info including latest glucose level, I use GET to "https://api-eu.libreview.io/llu/connections/"
where I use headers:
headers = {
"Accept": "application/json",
"product": "llu.android",
"version": "4.16",
"Authorization": f"Bearer {jwt_token}",
"Account-Id" : Account_ID
}

Where Account_ID is the SHA256 digest of the Patient_ID.
I am very happy with the contributions from this community!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment