data.foodintegrityproject.org / API
API v1
A barcode-keyed read API over packaged food: products, ingredients, certifications, and the research behind the insights. All responses are JSON.
Base URL
https://data.foodintegrityproject.org/api/v1Errors use standard HTTP status codes with a body of { "error": { "code": "...", "message": "..." } }.
Authentication
- Public reads (product, ingredient, scheme, and brand lookups): no auth required, rate-limited per IP.
- Submissions: an optional bearer token associates the submission with a submitter. Works without one.
- Contributor endpoints: a Supabase JWT in the
Authorization: Bearer <token>header.
Endpoints
The main consumer endpoint. Returns a product with its ingredients, nutrition, claims, and triggered insights.
Path params
barcode— UPC/EAN, digits only
Query params
include_findings(bool, default true) — expand research findings inlineenrich_if_thin(bool, default false) — if the data quality score is low, trigger Open Food Facts enrichment synchronously (may slow the response)
200 OK — product found
{
"variant": {
"barcode": "0123456789012",
"package_size": "12 oz",
"package_format": "bag",
"region": "US",
"data_quality_score": 0.82,
"source": "verified"
},
"line": {
"slug": "tortilla-chips-original",
"display_name": "Tortilla Chips (Original)",
"category": "snacks",
"nova_class": "nova_3"
},
"brand": { "slug": "acme", "display_name": "Acme", "parent_company": "Acme Holdings" },
"ingredients": [
{ "position": 1, "raw_text": "corn", "slug": "corn", "category": "whole_food" },
{ "position": 2, "raw_text": "soybean oil", "slug": "soybean-oil", "category": "oil_seed" }
],
"nutrition": { "calories_per_100g": 490, "sodium_mg_per_100g": 410 },
"claims": [
{
"scheme": { "slug": "non-gmo-project-verified", "name": "Non-GMO Project Verified", "authority": "Non-GMO Project" },
"status": "active",
"source_level": "line",
"evidence_sources": ["api_verified", "package_detected"]
}
],
"insights": [
{
"rule_slug": "high-gmo-risk-ingredients",
"type": "classification",
"severity": "caution",
"text": "Contains soybean oil and corn, both commonly derived from GMO crops unless certified.",
"evidence": { "matched_ingredients": ["soybean-oil", "corn"] },
"findings": [ { "slug": "...", "title": "...", "evidence_strength": "established" } ]
}
],
"meta": { "last_evaluated_at": "2026-05-19T20:00:00Z", "needs_enrichment": false }
}404 Not Found — barcode not in system
{
"error": { "code": "product_not_found", "message": "..." },
"submission_url": "/api/v1/submissions"
}Trigger an Open Food Facts lookup and merge for an existing thin product. No auth required, rate-limited. Returns the enriched product along with a trace id.
{ "status": "enriched", "trace_id": "...", "variant": { "...": "..." } }User submits photos to seed or enrich a product. Content-Type multipart/form-data.
Fields
barcode(optional) — if knownimages[]— 1–6 image files (front, back/ingredients, nutrition panel)notes(optional) — free text from the submitter
Images run through an OpenRouter vision call. The submission auto-publishes when extraction_confidence ≥ 0.85 and the brand already exists; otherwise it is marked needs_review for a contributor.
201 Created
{
"submission_id": "...",
"status": "published",
"extraction_confidence": 0.91,
"extracted": { "...": "..." },
"persisted": { "...": "..." },
"trace_id": "..."
}Single-image submission with an explicit panel type. Content-Type multipart/form-data. Same extraction and persistence logic as /submissions.
Fields
barcode(optional)image— a single image filepanel_type—front,back,ingredients,nutrition, orauto
201 Created
{ "submission_id": "...", "status": "needs_review", "panel_type": "ingredients", "extraction_confidence": 0.72 }Poll a submission’s status.
{ "submission_id": "...", "status": "published", "product_id": "...", "extraction_confidence": 0.91 }List all active certification schemes.
{
"schemes": [
{
"slug": "non-gmo-project-verified",
"name": "Non-GMO Project Verified",
"authority": "Non-GMO Project",
"scope_summary": "...",
"badge_image_url": "..."
}
]
}Search for brands by name (fuzzy, across display name and aliases).
Query params
q— search querylimit— default 20, max 100
{
"brands": [
{ "slug": "general-mills", "display_name": "General Mills", "parent_company": "General Mills, Inc." }
]
}Pull canonical info and research for one ingredient.
{
"ingredient": {
"slug": "carrageenan",
"canonical_name": "Carrageenan",
"aliases": ["E407", "Irish moss extract"],
"category": "thickener",
"description": "..."
},
"findings": [
{
"slug": "...",
"title": "...",
"summary": "...",
"evidence_strength": "emerging",
"finding_type": "review_article",
"citation": { "...": "..." }
}
]
}Evaluate an ad-hoc ingredient list without a barcode. Useful for restaurant menus, bulk bin items, or hypothetical formulations.
Body
{
"ingredients": ["corn", "soybean oil", "salt", "natural flavors", "soy lecithin"],
"nutrition": { "sodium_mg_per_100g": 600 }
}200 OK
{
"ingredients_parsed": [ { "raw_text": "...", "matched_slug": "...", "confidence": 0.95 } ],
"insights": [ /* same shape as /products/:barcode */ ]
}Planned endpoints
Specified but not yet live. Shapes may change before release.
Get a brand and its product lines.
Get a product line, its canonical recipe, claims, and variants.
Re-run all enabled scheme adapters against the certifier APIs.
Contributor endpoints
Auth required (Supabase JWT). Used by the contributor dashboard to manage submissions, rules, findings, schemes, claims, brands, and lines.
- GET /admin/submissions?status=needs_review — review queue
- POST /admin/submissions/:id/publish — approve and publish
- POST /admin/submissions/:id/reject — reject with notes
- GET /admin/rules — list all rules including inactive
- POST /admin/rules — create rule
- PATCH /admin/rules/:id — update (bumps version)
- POST /admin/rules/:id/reevaluate — enqueue reevaluation
- GET /admin/findings — list all findings including drafts
- POST /admin/findings — create finding
- PATCH /admin/findings/:id — update
- POST /admin/findings/:id/publish — publish (reviewer must differ from author)
- GET /admin/schemes — list all schemes including inactive
- POST /admin/schemes — create scheme
- PATCH /admin/schemes/:id — update
- GET /admin/claims/disputed — list disputed claims
- POST /admin/claims/:id/resolve — resolve a disputed claim
- GET /admin/brands — list/search brands
- POST /admin/brands — create brand
- PATCH /admin/brands/:id — update aliases, owner brand, parent company
- POST /admin/brands/merge — merge two brands
- GET /admin/lines — list/search product lines
- POST /admin/lines — create line under a brand
- PATCH /admin/lines/:id — update
- POST /admin/lines/:id/canonicalize — set canonical ingredients from a variant
- GET /admin/variants/unlinked — variants not yet linked to a line
- POST /admin/variants/:id/link — link a variant to a line
- GET /admin/variants/drift — variants whose ingredients differ from canonical
Rate limits
- Public reads: 60 req/min per IP
- Submissions: 10 per hour per IP / submitter
- Contributor endpoints: 600 req/min per token
Versioning
The path includes /v1/. Breaking changes get a new major version. Additive changes do not.