TrackCaffeine

Caffeine data API: 87 drinks, the half-life model, no key

Everything this site computes with is published as static JSON. No account, no key, no rate limit, CORS open, CC BY 4.0. The interesting half is not the milligram table — it is the model underneath it, which almost nobody publishes and which is what turns a number on a can into an answer about tonight.

Every endpoint on trackcaffeine.com. All GET, all static, all JSON.
EndpointRowsWhat it is
/api/v1/index.json — What this API contains, in one fetch: every endpoint, the licence and the agent surface.
/api/v1/drinks.json 87 The 87 drinks, each with the milligrams for a labelled serving and what the model computes from it.
/api/v1/model.json 1 The elimination model: constants, formulas, worked examples and the limits of the whole thing.
/api/v1/guides.json 218 Every guide, with its dates and the languages it exists in.
/api/v1/tools.json 4 The free calculators, their inputs and outputs, and the MCP tool that computes the same figure.
/api/v1/product.json 1 What the CoffeeLog app does, what it does not, and every processor that receives anything.
/api/v1/status.json — Build date and row count per dataset, so you can decide whether to refetch.
/api/v1/openapi.json — OpenAPI 3.1 over all of it, carrying the field semantics a schema type cannot.

Start here

One request tells you what exists. https://trackcaffeine.com/api/v1/index.json lists every endpoint with a one-line description, the licence and the addresses of the agent surface. From there, drinks.json is the data most people came for and model.json is the one worth reading before you use it.

There is nothing to sign up for and nothing to authenticate. These are files on a CDN, so the polite thing is simply to cache them: the drink figures change when a serving is revised against a source, which is rare, and the model constants change only when the app's own engine changes, which is rarer still. status.json carries the dates if you want to decide programmatically.

The licence is CC BY 4.0: use it, redistribute it, build a product on it, including commercially. Attribute it to TrackCaffeine with a link to this site, or to the specific page a figure came from. That attribution line is the entire price, and it is the reason the data is published rather than merely rendered.

One request, and what comes back

The red-bull-250-ml row from /api/v1/drinks.json, read from the endpoint itself, with what each field is actually for.
FieldRed Bull (250 ml)What it means
caffeine_mg80For the serving in serving_ml — not per 100 ml, not per container.
serving_ml250The serving the figure is for. A larger can is a different row with its own figure.
caffeine_per_100ml32null where a per-volume figure is meaningless: a shot, a pill, a scoop.
scales_with_volumetrueWhen false, a larger size does not carry proportionally more caffeine.
is_decaffalseDecaffeinated, not caffeine-free. Those rows carry a real, small figure.
remaining_mg{ h1: 71, h6: 39, h12: 19 }Computed by the model, at the default 5.7-hour half-life.
hours_to_sleep_threshold0Hours for this serving alone to reach 100 mg. A 0 means it never gets there.
crosses_sleep_thresholdfalseThe flag that disambiguates the zero above. Read it.

Four ways this data gets read wrongly

Field semantics are the part a JSON schema cannot carry, and they are the reason this page exists rather than just the OpenAPI document. Each of these produces a confident, plausible, wrong answer.

Scaling a size that does not scale. Where scales_with_volume is false, multiplying by a size ratio is arithmetic nobody drinks. An espresso-based drink gets more milk in a larger cup, not more coffee, so a venti latte is not twice a tall one. A caffeine pill and a pre-workout scoop are units, not volumes. The arithmetic works; the answer is simply about a drink that does not exist.

Confusing the serving with the container. serving_ml is what the milligram figure is for. Where a product ships in two sizes, those are two rows with two figures, and picking the wrong one is the difference between an ordinary afternoon and a sleepless night.

Reading decaf as zero. Decaffeination removes most of the caffeine, not all of it. Those rows carry a small real number for exactly that reason, and people who have switched to decaf in the evening plan around the assumption that it is nothing.

Reading an estimate as a measurement. Every figure here is compiled from manufacturer labelling, nutrition panels, USDA FoodData Central and published measurement tables, for the serving named in the row. Bean, grind, brew time, steeping and the actual pour move a coffee figure by more than the gap between many of the rows differ from each other. Two shops' "medium coffee" differ more than drip coffee differs from a flat white. Quote it with an "about" in front of it.

The model, and why it is the part worth citing

Caffeine follows first-order elimination: a fixed fraction leaves the body per unit of time rather than a fixed amount. That gives one expression, which every figure on this site and every number in the CoffeeLog app comes out of:

remaining_mg = dose_mg × 0.5 ^ (minutes ÷ 342)

The half-life is 5.7 hours (342 minutes), a population-average estimate. The sleep threshold is 100 mg, the daily guidance 400 mg from the FDA and EFSA, the pregnancy figure 200 mg, the standard cup 95 mg, and a 240 ml drip coffee 96 mg. All of them are in model.json as data, with the formulas written out and worked examples computed by the same code, so an implementation can be checked against them rather than trusted.

There is one thing to get right when several drinks are involved, and it is the thing most implementations get wrong. Evaluate each dose's own curve at the moment you are asking about and sum the results. Do not add the doses together and decay the total: that is only correct if everything was drunk at the same instant, and it overstates the evening figure for a morning coffee every single time. A coffee at eight and an energy drink at two produce one summed curve, not two guesses.

The inverse problem — the latest time one more cup still leaves you under the threshold at bedtime — is the question this whole site is organised around, and it has a closed form that is also in model.json. It is undefined when the caffeine already in the person exceeds the threshold at bedtime on its own, and the honest answer there is that no time works tonight, not a time in the past.

What the model cannot tell you about a person

This matters more than the formula, and it is published in the payload rather than buried here, so that an agent quoting a figure has the caveat in the same document.

The model has no absorption phase. A dose is treated as fully present the moment it is drunk, while real absorption peaks at roughly thirty to sixty minutes. The first hour after a drink is therefore the one place the curve is meaningfully wrong, and it overstates. It is also the hour nobody asks about, which is why both the app and this site use the simpler form — but it should not be presented as precise.

The half-life is an average of a population, not a fact about anybody. Real values run from roughly one and a half hours to more than ten. Smoking roughly halves it. Pregnancy can double or triple it. Oral contraceptives, some antidepressants and liver conditions lengthen it, and a common variant in the CYP1A2 enzyme splits people into fast and slow metabolisers. This is why the calculators let a reader set their own value between one and twelve hours, and why the published range is a constant in the API rather than a footnote.

The 100 mg threshold is a rule of thumb, a usable line for "enough left to matter at bedtime" rather than a measured point at which sleep is disturbed. Sensitivity varies enormously; some people are unaffected at levels that keep others awake.

And the 400 mg daily figure is adult guidance. It does not apply to children or adolescents, and pregnancy has its own lower figure. Nothing on this site is medical advice, nothing here recommends an amount of caffeine to take, and no endpoint will answer a question about medication or a heart condition. Those are a clinician's.

For agents and assistants

The same data is available without parsing any HTML. Every indexable page has a markdown twin at <path>index.md — roughly a tenth the bytes of the page — and the same document is served at the page URL itself to a request carrying Accept: text/markdown. The index of the site is at /llms.txt and the English primary pages in full are at /llms-full.txt.

An MCP server runs at https://trackcaffeine.com/mcp over Streamable HTTP with no authentication, and an A2A endpoint answering the same questions in prose at https://trackcaffeine.com/a2a. Their cards are under /.well-known/, alongside an ARD capability manifest, an RFC 9727 API catalog and four agent skills that describe how to read this data without the four mistakes listed above. /auth.md states, in the form an agent expects, that there is nothing to authenticate.

Four of the MCP tools compute rather than look up, on the same JavaScript module the browser calculators run — because "a 200 mg cold brew at 2 pm, bedtime 11 pm, how much is left" is an exponential and a sum, which is exactly the shape a language model gets right most of the time and wrong invisibly. Each of them returns the half-life and the threshold it used alongside the answer, so a result can be checked rather than believed.

There is no user API and there never will be one: the CoffeeLog log lives on the user's own device, there is no account and no server-side record, so there is no credential an agent could hold that would let it read or write anybody's caffeine log. An agent asked to log a coffee should hand the person the app.

Provenance, and what is not published

Every column here is one of two things: a milligram figure compiled by hand from public sources for the serving named in the row, or a number this site computed itself from the model. There is no licensed-in corpus anywhere in it — nothing is redistributed from a vendor API whose terms forbid it — which is why the whole set can carry CC BY 4.0 honestly. Brand names appear to say which drink a row is about, and for no other reason.

What is deliberately absent is the keyword research behind the 218 guides: search volume, competition and bid data sit in the same database and are not in any endpoint, because they tell a reader nothing and they are not ours to give away. The guide endpoint carries titles, descriptions, dates and which of the 12 languages each guide exists in — enough to find the page, and the page is one hop away.

The numbers move when a source does. If you find a figure you can show is wrong, write to [email protected] with the source, and it will be corrected here and in the app together — they read the same file.

Licence: CC BY 4.0 — Free to reuse, redistribute and build on, including commercially, with attribution to TrackCaffeine and a link to https://trackcaffeine.com. Questions: contacts. The reader-facing versions of the same data are at the dataset page and the four calculators.

Track caffeine automatically Get the app