Glucose API v1
Continuous glucose monitor readings over HTTP. Plain text for microcontrollers, JSON for everything else. All reads are served from stored data — no request reaches the CGM vendor.
?token= or X-BG-Token. Stored in this browser only, never transmitted anywhere but davenn.com.
Four comma-separated integers on one line: mgdl, trend (0–7), minutes_ago, in_range. About 10 bytes. An mgdl of 0 means nothing is stored. With spark, a second line carries that many mg/dL values, oldest first.
Parameters
| Name | Description | Value |
|---|---|---|
| spark integer 1–60 | Append a sparkline of the last N readings | |
| low integer | Lower bound of the in-range band | |
| high integer | Upper bound of the in-range band |
Example response · text/plain
270,3,5,0
251,252,259,270
The newest stored reading. at is the reading's true timestamp and minutes_ago its age — check that before displaying anything. reading is null when nothing is stored.
Parameters
None.
Example response · application/json
{
"reading": {
"at": "2026-09-18T13:46:47+00:00",
"mgdl": 270,
"trend": "FortyFiveUp",
"minutes_ago": 5
}
}
All readings in the window plus a summary. Readings land every 5 minutes, so hours=24 is roughly 288 of them — about 18 KB. Too large for a microcontroller; use bg_embed there.
Parameters
| Name | Description | Value |
|---|---|---|
| hours integer 1–168 | Window size. Defaults to 24 | |
| low integer | Lower bound of the in-range band | |
| high integer | Upper bound of the in-range band |
Example response · application/json
{
"hours": 1,
"summary": {
"readings": 10, "avg_mgdl": 203, "min_mgdl": 132, "max_mgdl": 270,
"low_count": 0, "high_count": 6, "in_range_pct": 40,
"low_threshold": 70, "high_threshold": 180
},
"readings": [ { "at": "...", "mgdl": 132, "trend": "Flat" }, ... ]
}
Daily rollups in the server's local timezone. coverage_pct is the share of the day's 288 slots holding data — below about 70% the day has sensor gaps and its percentages should be read with that in mind. The current day is partial by definition.
Parameters
| Name | Description | Value |
|---|---|---|
| days integer 1–365 | How many days back. Defaults to 30 | |
| low integer | Lower bound of the in-range band | |
| high integer | Upper bound of the in-range band |
Example response · application/json
{
"days": 2,
"timezone": "America/New_York",
"daily": [
{
"day": "2026-09-17", "readings": 237,
"avg_mgdl": 136, "min_mgdl": 72, "max_mgdl": 269,
"low_count": 0, "high_count": 44,
"in_range_pct": 81, "coverage_pct": 82
}
]
}
Asks the poller to fetch from the CGM now, so the next read reflects live data. Rate-limited to once every 45 seconds; inside that window it returns triggered: false with a retry_after rather than calling out.
Parameters
None.
Example response · application/json
{ "triggered": true, "status": 200,
"poller": { "sent": 3, "stored": 3, "latest_observed": "..." } }
{ "triggered": false, "reason": "cooling down", "retry_after": 44 }
- Never show an old number as current. Check
minutes_ago— dim past 15 minutes, hide the number past 60. A stalled poller and a dead sensor look identical from the client. - Pin the root CA, not the leaf. The site certificate rotates on renewal, so embedding it kills a device twice a year. Embed Go Daddy Root Certificate Authority — G2.
- Poll no faster than 60 seconds. New readings only arrive every 5 minutes.
- Trend codes: 0 unknown · 1 ↑↑ · 2 ↑ · 3 ↗ · 4 → · 5 ↘ · 6 ↓ · 7 ↓↓