Clean and organic API
Clean-ingredient verdicts, profiles, swaps and organic advice. 6 ops: check_clean, get_clean_rubric, get_clean_profile, set_clean_profile, clean_summary, suggest_swaps.
Clean-ingredient verdicts, profiles, swaps and organic advice.
Check if food is clean · POST /api/v1/check_clean · scope clean:read or GET …?input=
Judge ingredients against Gutlog's published clean-ingredient rubric: pass exactly one of ingredients_text (a label as printed), ingredients (names), barcode (a product in the index) or meal_id (a logged meal). Returns verdict (clean, mostly_clean, not_clean), a 0–100 score, flags (ingredient, category, evidence level: regulatory, strong, mixed or preference, the penalty, a one-line why and a citation), organic advice for Dirty Dozen and Clean Fifteen produce (buy_organic, conventional_ok), and names it did not recognize. Uses the person's profile (Strict, Balanced or Custom) unless profile is given. Tell the person the verdict and the top flags with their evidence level; preference flags are not proven harms. Free and unlimited.
curl -X POST https://getgutlog.com/api/v1/check_clean \ -H "Authorization: Bearer gutlog_…"npx @meterapp/gutlog check_clean{
"type": "object",
"properties": {
"ingredients_text": {
"description": "An ingredient list as printed on a label, e.g. \"Corn, vegetable oil, salt, natural flavors.\"",
"type": "string",
"minLength": 1,
"maxLength": 5000
},
"ingredients": {
"description": "Ingredient names or canonical ids, one per entry (instead of ingredients_text).",
"minItems": 1,
"maxItems": 200,
"type": "array",
"items": {
"type": "string",
"minLength": 1,
"maxLength": 200
}
},
"barcode": {
"type": "string",
"pattern": "^\\d{8,14}$",
"description": "The product's barcode digits (UPC-A, EAN-13, GTIN-8 or GTIN-14)."
},
"meal_id": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
"description": "Meal id (meals.id)."
},
"profile": {
"description": "Judge with this profile instead of the person's saved one: strict, balanced or custom (custom uses their saved category levels).",
"type": "string",
"enum": [
"strict",
"balanced",
"custom"
]
}
}
}Get the clean rubric · POST /api/v1/get_clean_rubric · scope clean:read or GET …?input=
The clean-ingredient rubric as data: version, scoring rules, profiles (strict, balanced, custom) with their thresholds, evidence levels, categories, every rule (ingredient, aliases, category, evidence, penalty, why, citation), additives the EU bans or restricts, and this year's EWG Dirty Dozen and Clean Fifteen with the citation. Filter by category or ingredient id. Use it to explain why something was flagged.
curl -X POST https://getgutlog.com/api/v1/get_clean_rubric \ -H "Authorization: Bearer gutlog_…"npx @meterapp/gutlog get_clean_rubric{
"type": "object",
"properties": {
"category": {
"description": "Only this category's rules.",
"type": "string",
"enum": [
"natural_flavors",
"artificial_flavors",
"artificial_sweeteners",
"artificial_colors",
"seed_oils",
"hydrogenated_fats",
"added_sugars",
"preservatives",
"emulsifiers_gums",
"flavor_enhancers",
"refined_starches",
"bleached_enriched_flours",
"eu_restricted"
]
},
"ingredient": {
"description": "Only the rules on this canonical ingredient id (e.g. natural-flavors).",
"type": "string",
"minLength": 1,
"maxLength": 100
}
}
}Get the clean profile · POST /api/v1/get_clean_profile · scope clean:read or GET …?input=
The person's clean profile: strict, balanced (the default) or custom, and for custom the level per category (off, balanced or strict). Verdicts from check_clean and clean_summary use it.
curl -X POST https://getgutlog.com/api/v1/get_clean_profile \ -H "Authorization: Bearer gutlog_…"npx @meterapp/gutlog get_clean_profile{
"type": "object",
"properties": {}
}set_clean_profile
writeSet the clean profile · POST /api/v1/set_clean_profile · scope clean:write
Choose how strictly food is judged: strict (every rule, including gums, starches and all added sugars), balanced (the default) or custom. With custom, `categories` sets a level per category (off, balanced or strict); categories not given keep their saved level, or balanced. Returns the saved profile. Ask the person before changing it.
curl -X POST https://getgutlog.com/api/v1/set_clean_profile \ -H "Authorization: Bearer gutlog_…" \ -H "Content-Type: application/json" \ -d '{"profile":"strict"}'npx @meterapp/gutlog set_clean_profile --json '{"profile":"strict"}'{
"type": "object",
"properties": {
"profile": {
"type": "string",
"enum": [
"strict",
"balanced",
"custom"
],
"description": "strict, balanced or custom."
},
"categories": {
"description": "Custom only: a level per category, e.g. {\"natural_flavors\": \"off\", \"added_sugars\": \"strict\"}.",
"type": "object",
"propertyNames": {
"type": "string",
"enum": [
"natural_flavors",
"artificial_flavors",
"artificial_sweeteners",
"artificial_colors",
"seed_oils",
"hydrogenated_fats",
"added_sugars",
"preservatives",
"emulsifiers_gums",
"flavor_enhancers",
"refined_starches",
"bleached_enriched_flours",
"eu_restricted"
]
},
"additionalProperties": {
"type": "string",
"enum": [
"off",
"balanced",
"strict"
]
}
}
},
"required": [
"profile"
]
}clean_summary
readSummarize clean eating · POST /api/v1/clean_summary · scope clean:read or GET …?input=
How clean the person ate from `from` to `to` (diary days, inclusive, at most 92 days), or over the last `days` days up to today in their timezone (default 7): the overall verdict and mean score, one entry per day with its meals' verdicts and scores, verdict counts, the worst offenders (flagged ingredients by how often and how heavily they appeared, with evidence and why) and Dirty Dozen or Clean Fifteen produce eaten. Use it for weekly check-ins and to suggest one change at a time.
curl -X POST https://getgutlog.com/api/v1/clean_summary \ -H "Authorization: Bearer gutlog_…"npx @meterapp/gutlog clean_summary{
"type": "object",
"properties": {
"from": {
"type": "string",
"format": "date",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$",
"description": "A calendar day, YYYY-MM-DD, in the person's timezone."
},
"to": {
"type": "string",
"format": "date",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$",
"description": "A calendar day, YYYY-MM-DD, in the person's timezone."
},
"days": {
"description": "The last N diary days ending at `to` (or today); ignored when `from` is given. Default 7.",
"type": "integer",
"minimum": 1,
"maximum": 92
},
"profile": {
"description": "Judge with this profile instead of the person's saved one: strict, balanced or custom (custom uses their saved category levels).",
"type": "string",
"enum": [
"strict",
"balanced",
"custom"
]
}
}
}Suggest cleaner swaps · POST /api/v1/suggest_swaps · scope clean:read or GET …?input=
Cleaner products of the same kind from the product index: for one product (barcode) or each indexed product in a logged meal (meal_id). Returns, per product, its verdict, score and flags, and up to `limit` swaps that score higher (brand, name, barcode, verdict, score, improvement and which flags they avoid). Empty when the index has nothing cleaner yet.
curl -X POST https://getgutlog.com/api/v1/suggest_swaps \ -H "Authorization: Bearer gutlog_…"npx @meterapp/gutlog suggest_swaps{
"type": "object",
"properties": {
"barcode": {
"type": "string",
"pattern": "^\\d{8,14}$",
"description": "The product's barcode digits (UPC-A, EAN-13, GTIN-8 or GTIN-14)."
},
"meal_id": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
"description": "Meal id (meals.id)."
},
"profile": {
"description": "Judge with this profile instead of the person's saved one: strict, balanced or custom (custom uses their saved category levels).",
"type": "string",
"enum": [
"strict",
"balanced",
"custom"
]
},
"limit": {
"default": 3,
"description": "Swaps per product, at most 10.",
"type": "integer",
"minimum": 1,
"maximum": 10
}
}
}