The Food Value API

Every priced food row we've ever published, as JSON. Free, no key, no signup, no rate limit. Build whatever you want with it.

We spent a lot of hours pricing groceries and dividing them by USDA nutrient values. 474 rows across 22 datasets, all of it original, all of it dated, none of it scraped from someone else's blog. It's been sitting on this site as CSV files for a while now, which is great if you like CSV files and less great if you're trying to build something.

So here's the same data as an API. Two endpoints, open CORS, no authentication of any kind. You don't email us for a key. There's no key.

The one thing we ask

Credit us with a live link. That's it. Every single response carries the exact string we'd like you to use, in meta.attribution, so you never have to guess:

Data: Daily Life Hacks, https://www.daily-life-hacks.com/data/

There's an HTML version in meta.attribution_html if you're dropping it under a chart. Reuse terms are on the methodology page, along with exactly how every number was calculated. If you're publishing our numbers, read that page. It'll save you from misdescribing what we measured.

Start here: what's in the box

The discovery endpoint lists all 22 datasets with row counts, the full column schema of each one, the raw CSV, and the study article the numbers came from.

curl "https://www.daily-life-hacks.com/api/v1/datasets"

Filter it with ?nutrient=fiber or ?nutrient=protein, or pull a single one with ?dataset=. Each entry hands you a ready-made api_url for the rows.

Then get the rows

curl "https://www.daily-life-hacks.com/api/v1/foods?nutrient=fiber&dataset=fiber-per-dollar-2026&limit=3"

Which gives you this. It's the real response with two of the three rows cut out, and the meta block shortened to the four fields you'll actually reach for:

{
  "data": [
    {
      "dataset": "fiber-per-dollar-2026",
      "food": "Whole wheat flour",
      "category": "Whole grains",
      "nutrients": ["fiber"],
      "value": 77.8,
      "value_metric": "fiber_g_per_dollar",
      "unit": "grams of dietary fiber per US dollar",
      "metrics": { "fiber_g_per_dollar": 77.8 },
      "package": "5 lb bag",
      "price_usd": 3.12,
      "price_basis": "Walmart GV",
      "rank": 1,
      "study_url": "https://www.daily-life-hacks.com/fiber-per-dollar-cheapest-high-fiber-foods/",
      "csv_url": "https://www.daily-life-hacks.com/data/fiber-per-dollar-2026.csv",
      "fields": {
        "rank": 1,
        "food": "Whole wheat flour",
        "category": "Whole grains",
        "fiber_g_per_100g": 10.7,
        "package": "5 lb bag",
        "package_price_usd": 3.12,
        "package_weight_g": 2268,
        "edible_fraction": 1,
        "price_per_100g_usd": 0.138,
        "fiber_g_per_dollar": 77.8,
        "price_basis": "Walmart GV"
      }
    }
  ],
  "total": 53,
  "count": 3,
  "meta": {
    "api_version": "v1",
    "data_version": "2026.1",
    "methodology_url": "https://www.daily-life-hacks.com/methodology/",
    "attribution": "Data: Daily Life Hacks, https://www.daily-life-hacks.com/data/"
  }
}

value is the number you probably came for. fields is the original CSV row, typed, so nothing is hidden behind our normalization. And study_url points at the article where a human explained what the number means.

Parameters for /api/v1/foods

ParameterAcceptsWhat it does
nutrient fiber or protein Keeps only the rows that publish that nutrient's grams-per-dollar number, and ranks on it.
dataset a dataset id or a study slug One study at a time. Both fiber-per-dollar-2026 and fiber-per-dollar-cheapest-high-fiber-foods work.
q any text Case-insensitive substring match on the food name. q=beans finds canned black beans.
sort value, food, price, rank, dataset Defaults to value.
order asc or desc Defaults to desc when you sort on value, asc otherwise.
limit 1 to 500 Defaults to 50.
offset 0 or more Defaults to 0. Page with it.

Bad parameters get a 400 with a hint field telling you what's actually allowed. Unknown datasets get a 404. Nothing fails silently and returns an empty array while pretending everything's fine.

A one-liner that does something useful

The five cheapest protein sources we've priced, formatted for a terminal:

curl -s "https://www.daily-life-hacks.com/api/v1/foods?nutrient=protein&dataset=protein-per-dollar-2026&limit=5" \
  | jq -r '.data[] | "\(.value)\tg protein per $1\t\(.food)"'
97.9	g protein per $1	Pinto beans (dry)
96	g protein per $1	Whole wheat flour
81	g protein per $1	Black beans (dry)
77.7	g protein per $1	Brown lentils (dry)
75.9	g protein per $1	Navy beans (dry)

Yes, dry pinto beans really are the answer. They're the answer to a lot of these queries. Search for something specific instead:

curl "https://www.daily-life-hacks.com/api/v1/foods?q=lentils&nutrient=protein&sort=value&order=desc&limit=10"

From the browser

CORS is open to every origin, so you can call this straight from a page with no proxy in the middle:

const res = await fetch(
  "https://www.daily-life-hacks.com/api/v1/foods?nutrient=fiber&limit=10"
);
const { data, total, meta } = await res.json();

console.log(total, "rows match");
console.log(data[0].food, data[0].value, meta.attribution);

Two things that'll confuse you if nobody warns you

The same food shows up more than once. Whole wheat flour appears in the flagship fiber index, in the grains cut, and in the breakfast staples study. Those are separate published studies, so they're separate rows, and we're not going to quietly delete four of them to make a list look tidier. Pin yourself to one study with ?dataset= when you want a clean ranking. Results are ordered so the flagship index row comes first among duplicates.

Some rows have a null value. The two daily-menu datasets price complete days of eating, so their rows carry a nutrient amount and a portion cost instead of a grams-per-dollar figure. We could divide one by the other and hand you a number, but that number was never audited, so you get null and the raw columns in fields. Null values always sort last.

Limits, rates, and other things we don't have

There's no rate limit. There's no quota, no tier, and nothing to sign up for. Responses are cached at the edge for a day, so if your project hammers the same query it mostly isn't touching our servers at all. The only ceiling is limit=500 per request, and offset gets you the rest.

If someone eventually abuses it hard enough to cost us money, we'll put a rate limit in front of it and say so here. That hasn't happened, and we'd rather build for the people using it properly.

Versions

The URL carries /v1/, and we won't break the shape of a v1 response. New fields can show up; existing ones won't change meaning or disappear. That's the deal.

The data version is separate and lives in meta.data_version. Prices get a full re-audit every quarter, and when they move, that string moves with them. If you cache our numbers, cache that string next to them so you can tell when you're stale.

For machines

Where the numbers come from

Nutrients are USDA FoodData Central. Prices are US national figures: BLS Average Price data where the item is tracked, Walmart national listings where it isn't, observed July 2026. Everything is calculated as-purchased, with USDA refuse percentages taken out so you're not paying for banana peel. The methodology page has the whole thing, including what we got wrong and corrected.

Built something with it? Tell us. We'd genuinely like to see it, and if you've found a row that looks wrong, we want to know that even more.