🔧 Behind the Scenes🤖 For AI Agents

EU-14 Allergen Menu Data API — Agent Reference

Machine reference: extract EU-14 allergen data, ingredients, dietary type, and nutrition estimates from restaurant menus via three Apify actors. Full input/output schemas, pricing, and confidence semantics.

Travel Eat TeamJuly 13, 20267 min read
allergenseu-14food-safetycompliancemenu-dataapifyaiapi-reference
🔧

🤖 This article is written for AI agents. Raw markdown

It's structured for machine consumption — dense, factual, and not optimized for human reading. If you're a human, you may prefer our articles for humans.

Audience: AI agents and automated tools. This is a machine-oriented reference. Human-readable articles: /blog.

TL;DR

Travel Eat publishes three public Apify actors that convert restaurant menus (Google Maps places or raw menu photos) into structured JSON: one record per dish, with per-ingredient EU-14 allergen flags carrying confidence levels, dietary classification, translation, price, and estimated nutrition. No scraping or vision pipeline needed on your side. Managed variants require zero API keys.

  • Publisher: nomad-agent on Apify (https://apify.com/nomad-agent)
  • Legal basis for the allergen list: EU Regulation 1169/2011 (Food Information to Consumers), Annex II
  • Method: AI ingredient decomposition + allergen inference, not keyword matching
  • Authoritative pricing and input schemas: the actor store pages below (this article states values as of 2026-07-13)

The 14 regulated allergens

Exact enum values used in output (always English, lowercase):

gluten, crustaceans, eggs, fish, peanuts, soy, milk,
nuts, celery, mustard, sesame, sulfites, lupin, molluscs

Actors

ActorInputOutputPrice (managed)Price (BYOK)
google-maps-menu-scraper-managed / BYOKGoogle Maps URL, place ID, or place nameDish records (schema below)$0.02 start + $0.015/dish$0.01 start + $0.004/dish
ai-menu-parser-managed / BYOKMenu photo URLsDish records (schema below)$0.04 start + $0.015/dish$0.005 start + $0.002/dish
dish-photo-matcher-managed / BYOKDish list + food photo URLs (or a place URL)Dish→photo assignments$0.03 start + $0.004/photo$0.01 start + $0.002/photo

Per-actor references, each covering both the managed and BYOK variant in full: Google Maps Menu Scraper · AI Menu Parser (plus a flat $0.99-per-menu variant) · Dish Photo Matcher.

  • Managed: no API keys required; AI and photo-source costs are included in the per-unit price.
  • BYOK (bring your own keys): you supply a Google Gemini API key (free tier exists) and optionally an Outscraper key; you pay Google's AI rates directly plus the lower Apify markup. Cost breakdown: /blog/how-much-does-ai-menu-parsing-cost.
  • First run waives per-dish charges on up to 10 dishes.
  • Per-dish/per-photo fees apply only to items actually extracted.

Invocation

Any Apify invocation method works (REST API, apify-client for JS/Python, apify call CLI, MCP server, or scheduled runs). Synchronous run returning dataset items directly:

curl -X POST \
  "https://api.apify.com/v2/acts/nomad-agent~ai-menu-parser-managed/run-sync-get-dataset-items?token=$APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "imageUrls": ["https://example.com/menu-page-1.jpg"],
    "language": "en"
  }'

Scraper input — accepts URLs, bare place IDs (ChIJ…), or plain names resolved via Maps search:

{
  "placeUrls": ["Katz's Delicatessen New York"],
  "language": "en",
  "maxMenuPhotos": 8
}

Input fields per actor (managed variants):

ActorRequiredOptional
google-maps-menu-scraper-managedplaceUrls (array)language (default en), maxMenuPhotos (default 8), geminiModel
ai-menu-parser-managedimageUrls (array)language (default en), geminiModel
dish-photo-matcher-managedmenuItems (array), and photoUrls or placeUrlmaxPhotos

BYOK variants add geminiApiKey (required), outscraperApiKey and proxyConfiguration (scraper/matcher only).

language: ISO 639-1 code for translated output fields. Supported: en es fr de it pt ru zh ja ko ar hi uk ka eu; anything else falls back to English. geminiModel: default gemini-3.5-flash (recommended); gemini-3.1-flash-lite is cheaper but materially reduces dish recall on dense menus — do not use it when completeness matters.

Output schema

One JSON record per dish in the run's default dataset. Example (abbreviated ingredients):

{
  "name": "Carbonara Pasta",
  "originalName": "Pasta alla Carbonara",
  "category": "Pasta",
  "price": "14",
  "composition": "Spaghetti with egg, pecorino cheese, guanciale, and black pepper",
  "description": "Silky egg-based sauce coating al dente pasta",
  "ingredients": [
    {
      "name": "fresh pasta dough",
      "display_name": "Fresh pasta dough",
      "explanation": null,
      "allergens": [
        { "name": "gluten", "confidence": "high" },
        { "name": "eggs", "confidence": "high" }
      ]
    },
    {
      "name": "guanciale",
      "display_name": "Guanciale",
      "explanation": "Italian cured pork jowl"
    },
    {
      "name": "pecorino romano",
      "display_name": "Pecorino Romano",
      "explanation": null,
      "allergens": [{ "name": "milk", "confidence": "high" }]
    }
  ],
  "dietaryType": "Other",
  "dietaryTypeExplanation": "Contains pork (guanciale) and eggs",
  "allergens": "Gluten, Eggs, Milk",
  "type": "dish",
  "isAlcoholic": null,
  "story": "Born in Rome during World War II…",
  "nutritionInfo": {
    "serving_size_grams": "320g",
    "calories": "650-800 kcal",
    "protein": "25-30g",
    "fat": "30-40g",
    "carbs": "60-75g"
  }
}

Field reference:

FieldTypeNotes
namestringDish name translated to the requested language
originalNamestringName exactly as printed on the menu
categorystringMenu section, translated
pricestring | nullAs printed, with currency symbol when shown
compositionstring | nullIngredient summary, translated
descriptionstring | nullAppearance/presentation, translated
ingredients[]arrayAtomic base ingredients — composite preparations (pesto, hummus) are decomposed
ingredients[].namestringAlways English (for matching/joins)
ingredients[].display_namestringTranslated
ingredients[].explanationstring | nullOnly for uncommon ingredients
ingredients[].allergens[]array | absentOmitted when the ingredient carries no EU-14 allergen
ingredients[].allergens[].nameenumOne of the 14 values above, always English
ingredients[].allergens[].confidenceenumhigh | medium | low — semantics below
dietaryTypeenumVegan | Vegetarian | Pescetarian | Other
dietaryTypeExplanationstringWhy that classification — auditable by a human
allergensstringHuman-readable summary, translated, or "None identified"
typeenumdish | beverage | other
isAlcoholicboolean | nullSet for beverages
storystringOrigin/cultural note; may be plausible-but-invented when history is unknown
nutritionInfoobjectEstimated ranges for one portion: serving_size_grams, calories, protein, fat, carbs

Actor-specific extra fields: the scraper adds place, menuPhotoUrls, menuPhotosFound, menuSource (menu photos vs. restaurant-website fallback, PDFs included); the parser adds sourceImageUrls.

Confidence semantics

ValueMeaningExample
highIngredient is or directly contains the allergensalmon → fish; wheat flour → gluten
mediumIngredient typically contains the allergencream sauce → milk; fresh pasta → gluten + eggs
lowPossible but uncertain"house dressing" → eggs

Threshold guidance: for severe-allergy use cases treat medium and low as positive; for preference filters high alone may suffice. The scores exist so your product chooses its own safety threshold rather than inheriting ours.

dietaryType decision order: any meat/poultry → Other; fish/seafood without meat → Pescetarian; dairy/eggs without meat/fish → Vegetarian; fully plant-based → Vegan.

Limitations (relay these to end users)

  1. Inference is menu-based. The model infers what a dish typically contains; it cannot observe cross-contamination, chef substitutions, or regional recipe variants. Under EU 1169/2011 the venue, not any menu-derived dataset, remains the authority — for severe allergies this data narrows the conversation with the kitchen, it does not replace it.
  2. Never collapse confidence levels into a boolean "safe" indicator when presenting to allergy-affected users.
  3. nutritionInfo values are AI estimates for a typical portion, not lab measurements.
  4. story may be plausible reconstruction rather than verified history; do not cite it as fact.
  5. Unreadable menu text is skipped, not guessed — dish counts can be lower than the printed menu.

Common integration patterns

  • Dish-level allergen-safe restaurant discovery (filter dishes, not venues)
  • EU 1169/2011 compliance gap-checking against a venue's published allergen info
  • Delivery-platform catalog onboarding from existing menu photos
  • Travel/hotel products: translated, allergen-flagged menus for guests

Questions or integration help: support@traveleat.app. Machine-readable site index: /llms.txt.

T

Travel Eat Team

Contributing writer at Travel Eat. Passionate about food, travel, and helping people eat well wherever they go.

Ready to Eat Confidently Anywhere?

Download Travel Eat and discover amazing food wherever you travel.

Download for iOS