DietlyAPI developer guide. Last reviewed: July 2026

Python quickstart: nutrition data in 10 lines

You can search Dietly's public nutrition catalog with one GET request to https://api.getdietly.com/search. The response is a JSON array of foods, not an object with a results property. Install requests, run the example below, and you have a working nutrition lookup. The rest of this page turns that single call into something dependable: a client class, correct field handling, and retries that respect the rate limit.

Make your first request

The only required parameter is q (minimum two characters). Use limit to cap results between 1 and 50. Ten is plenty while you explore.

import requests

r = requests.get("https://api.getdietly.com/search",
    params={"q": "greek yogurt", "limit": 5}, timeout=10)
r.raise_for_status()
foods = r.json()
for food in foods:
    print(food["name"], food.get("protein_g"))

Understand the fields you get back

Each result is a flat object. Nutrient values are per 100 g of the product unless noted, and any nutrient can be null when the source label did not list it. Treat null as unknown, never as zero.

FieldMeaning
idStable Dietly food ID, used for direct lookups
name, brandProduct name and brand when known
calories_kcalEnergy per 100 g
protein_g, fat_g, carbs_gMacronutrients per 100 g
fiber_g, sugar_g, saturated_fat_g, sodium_mgDetail nutrients per 100 g, often null
serving_size_g, serving_descOne serving in grams and its label wording, when provided
source, confidenceWhere the record came from and how sure Dietly is
static_urlPath to the food's page on getdietly.com, when one exists

To convert a per-100 g value to a portion, multiply by grams and divide by 100. For a 150 g serving of a food with 10 g protein per 100 g, that is 10 * 150 / 100 = 15 g.

Read the result defensively

An empty search is [], which is a normal outcome rather than an error. Guard for it, and keep null nutrients as null in your own model.

food = foods[0] if foods else None
if food is None:
    print("No close match")
else:
    protein = food.get("protein_g")
    print(f"{food['name']}: {protein if protein is not None else 'unknown'} g/100g")

Look up a known food or barcode

Once you have an id from search, fetch that record directly with GET /food/{id}. To resolve a scanned product, call GET /barcode/{code} with an EAN or UPC. Both return a single food object, or a 404 when there is no match, which you should treat as an ordinary empty state.

detail = requests.get("https://api.getdietly.com/food/86", timeout=10)
if detail.status_code == 404:
    print("Unknown food ID")
else:
    detail.raise_for_status()
    print(detail.json()["name"])

scan = requests.get("https://api.getdietly.com/barcode/737628064502", timeout=10)
print("match" if scan.ok else "no product for that barcode")

Wrap it in a small client class

A thin client keeps the base URL, timeout and optional key in one place, and gives you a single spot to add retries. This is enough structure for a real feature without a heavy SDK.

import requests

class Dietly:
    def __init__(self, api_key=None, timeout=10):
        self.base = "https://api.getdietly.com"
        self.timeout = timeout
        self.session = requests.Session()
        if api_key:
            self.session.headers["Authorization"] = f"Bearer {api_key}"

    def search(self, query, limit=10):
        r = self.session.get(f"{self.base}/search",
            params={"q": query, "limit": limit}, timeout=self.timeout)
        r.raise_for_status()
        return r.json()

    def food(self, food_id):
        r = self.session.get(f"{self.base}/food/{food_id}", timeout=self.timeout)
        if r.status_code == 404:
            return None
        r.raise_for_status()
        return r.json()

client = Dietly()
print(len(client.search("oat milk")))

Add an API key when your app needs one

Read endpoints work anonymously at a shared per-IP limit, which is fine for prototypes. For account-level limits, usage reporting and higher throughput, create a free key in the dashboard and send it as a bearer token, exactly as the client class does above. Keep server keys out of browser bundles, public repositories and logs. If a key is ever exposed, rotate it from the dashboard rather than editing it in place.

Handle rate limits and errors with backoff

When you hit the limit the API returns 429 with a Retry-After header. Wait that long instead of retrying immediately. Retry only a small number of transient 5xx failures with growing delays, and let 404 become an empty state rather than a crash.

import time, requests

def get_with_retry(session, url, params=None, tries=3):
    for attempt in range(tries):
        r = session.get(url, params=params, timeout=10)
        if r.status_code == 429:
            time.sleep(int(r.headers.get("Retry-After", "2")))
            continue
        if 500 <= r.status_code < 600 and attempt < tries - 1:
            time.sleep(2 ** attempt)
            continue
        r.raise_for_status()
        return r.json()
    raise RuntimeError("DietlyAPI unavailable after retries")

Cache lookups where you can. Nutrition records change rarely, so a short local cache on id or query text cuts both latency and your request count. That is the same fast and cheap principle Dietly runs on: do the work once and reuse it.

Attribution

Records derived from Open Food Facts carry an open licence. When you display that data publicly, include the attribution described in the API terms. It is a short line, and it keeps the open catalog healthy for everyone building on it.

Next: inspect the complete OpenAPI specification for every field and endpoint, read the integration guide for limits and attribution, and check service status before treating an error as a bug in your app.