The recipe data API for Portugal & UK food apps.

460+ recipes (available in Portuguese and UK English), 290+ ingredients with locale names, real-time supermarket prices, wine & beer pairings, chef-curated content, and meal planning. The data infrastructure Bourmet runs internally — now exposed.

See pricing Get an API key
460
Recipes
2
Locales (PT/UK)
30
Chef-curated
294
Ingredients
7
Supermarkets
11
Guides

What you get

A single REST API exposing the data that powers bourmet.pt and bourmet.co.uk.

🌍

Multi-locale (PT & UK)

One key, two markets. Switch with ?locale=pt or ?locale=en-gb. Recipes, ingredients, supermarkets and prices all locale-scoped.

👨‍🍳

Chef-curated content Premium

Profiles, recipes, sponsor partnerships from real working chefs. Currently live: Hélio Loureiro (30 recipes, full bio, social links).

🛒

Real-time supermarket prices

Continente, Auchan, Pingo Doce, Aldi (PT) plus Tesco, Sainsbury's, Asda, Lidl, Waitrose (UK). Normalized per gram/ml. 100-day price history.

🗓️

Meal planning Premium

Generate optimized weekly plans by budget, servings, difficulty and cooking mode. Swap recipes in place. Shareable via short token.

🍷

Wine & beer pairings

Curated wine and beer types matched to recipes. Sommelier-style pairings without paying a sommelier.

🔁

Substitutions & conversions

Swap suggestions with ratios (vegan, gluten-free, allergy-aware) and unit conversions (ml ↔ g ↔ cup) per ingredient.

What's new

Recent shipped work — April 2026.

Base URL

https://bourmet.dev/v1

All endpoints require an API key via the X-API-Key header. Responses are JSON with UTF-8 encoding.

Authentication

Required: Include your API key in every request as a header.
curl -H "X-API-Key: YOUR_API_KEY" https://bourmet.dev/v1/recipes

API keys are tied to a locale (pt or en-gb). The locale determines which data you receive by default. Override with ?locale=en-gb.

Rate Limit Headers

HeaderDescription
X-RateLimit-LimitDaily request limit
X-RateLimit-RemainingRequests remaining today
X-RateLimit-ResetUnix timestamp when limit resets
X-PlanYour plan name

Recipes

460+ recipes available in Portuguese and UK English (some UK translations still in progress) with ingredients, step-by-step instructions, cooking modes, pairings, FAQs, and ratings.

GET /recipes/index Lightweight recipe list

Paginated recipe list with sorting and filters. Returns 20 items per page by default.

GET /v1/recipes/index
GET /v1/recipes/index?page=2&per_page=10
GET /v1/recipes/index?category=peixe&sort=rating&order=desc
GET /v1/recipes/index?search=bacalhau&max_time=30
GET /v1/recipes/index?mode=kitchen-robot&difficulty=easy
GET /v1/recipes/index?ingredients=1,25,14

Pagination

ParamDefaultDescription
page1Page number
per_page20Items per page (max 100)

Sorting

ParamDefaultOptions
sortdatedate, title, difficulty, time, cost, rating, calories
orderdescasc, desc

Filters

ParamExampleDescription
categorypeixeFilter by category slug
difficultyeasyeasy, medium, hard
modekitchen-robotkitchen-robot, airfryer, mc
mealdinnerbreakfast, lunch, dinner, snack
min_time30Minimum total_time in minutes
max_time60Maximum total_time in minutes
max_cost3.00Maximum cost_per_serving
searchbacalhauSearch in title and description
ingredients1,25,14Recipes containing any of these ingredient IDs
nutritionhigh-proteinhigh-protein (≥25g), low-carb (≤15g), light (≤250cal), low-cal (≤300cal)
moodcomfortcomfort, quick-and-easy, impress, family, traditional, kids

Sample Response

{
  "data": [
    {
      "id": "147",
      "slug": "bacalhau-a-bras",
      "title": "Bacalhau à Brás",
      "image": "147",
      "total_time": "35",
      "difficulty": "easy",
      "servings": "4",
      "cost_per_serving": "2.06",
      "calories": "420",
      "date": "2026-02-24",
      "categories": ["peixe"],
      "tags": ["bacalhau", "rapido"],
      "meals": ["lunch", "dinner"],
      "modes": ["kitchen-robot"],
      "ingredient_ids": [25, 51, 14, 3, 7, 1, 5, 9, 70, 56]
    }
  ],
  "pagination": {
    "page": 1,
    "per_page": 20,
    "total": 460,
    "total_pages": 23,
    "has_next": true,
    "has_prev": false
  }
}
GET /recipes/facets All filter counts in one request

Returns counts for all filterable dimensions — categories, modes, meals, difficulty, nutrition labels, and time ranges. Use this to populate filter UIs without loading recipes.

GET /v1/recipes/facets

Sample Response

{
  "total": 460,
  "categories": [
    {"slug": "carne", "label": "Carne", "count": "116"},
    {"slug": "peixe", "label": "Peixe e Marisco", "count": "98"}
  ],
  "modes": [
    {"mode": "kitchen-robot", "count": "134", "label": "Bimby"},
    {"mode": "airfryer", "count": "130", "label": "Airfryer"},
    {"mode": "mc", "count": "45", "label": "Mc"}
  ],
  "meals": [
    {"meal": "dinner", "count": "320"},
    {"meal": "lunch", "count": "280"}
  ],
  "difficulty": {"easy": 289, "medium": 98, "hard": 19},
  "nutrition": {
    "high_protein": 45,
    "low_carb": 32,
    "light": 28,
    "low_cal": 67
  },
  "time_ranges": {
    "under_15": 25,
    "under_30": 89,
    "under_60": 245,
    "over_60": 161
  },
  "moods": {
    "comfort": 85,
    "quick_and_easy": 89,
    "impress": 32,
    "family": 45,
    "traditional": 120,
    "kids": 28
  }
}
Nutrition thresholds: high_protein = protein ≥ 25g, low_carb = carbs ≤ 15g, light = calories ≤ 250, low_cal = calories ≤ 300.
Mode labels: kitchen-robot shows as "Bimby" (PT) or "Thermomix" (UK) based on locale.
GET /recipes/{slug} Full recipe with all details

Returns complete recipe data including steps, ingredients with localized names, cooking modes, pairings, FAQs, and ratings.

GET /v1/recipes/bacalhau-a-bras

Sample Response

{
  "id": "147",
  "locale": "pt",
  "slug": "bacalhau-a-bras",
  "title": "Bacalhau à Brás",
  "description": "O clássico português de bacalhau desfiado...",
  "image": "147",
  "prep_time": "15",
  "cook_time": "20",
  "total_time": "35",
  "servings": "4",
  "difficulty": "easy",
  "seasons": "all-year",
  "cost_per_serving": "2.06",
  "calories": "420",
  "protein": "28",
  "carbs": "22",
  "fat": "24",
  "steps": [
    {
      "mode": "normal",
      "step_order": "0",
      "text": "Desfie o bacalhau em lascas finas...",
      "time_minutes": null,
      "tip": "Compre bacalhau já demolhado..."
    }
  ],
  "ingredients": [
    {
      "ingredient_id": "25",
      "name": "bacalhau desfiado e demolhado",
      "ingredient_slug": "bacalhau-desfiado-e-demolhado",
      "qty": "400.00",
      "unit": "g",
      "scalable": "1"
    }
  ],
  "categories": [{"slug": "peixe", "label": "Peixe e Marisco"}],
  "tags": ["bacalhau", "rapido", "familia"],
  "meals": ["lunch", "dinner"],
  "modes": [
    {"mode": "kitchen-robot", "note": "...", "description": "..."}
  ],
  "pairings": [...],
  "pairing_types": [
    {"category": "wine", "type_key": "vinho_branco"},
    {"category": "beer", "type_key": "weiss"}
  ],
  "faqs": [{"question": "...", "answer": "..."}],
  "rating": {"count": "30", "average": "4.7"},
  "kitchen_robot_name": "Bimby"
}
Kitchen robot: The mode kitchen-robot resolves to "Bimby" for PT and "Thermomix" for UK. The display name is included in kitchen_robot_name.
GET /recipes/{slug}/links Same recipe in other locales

Returns linked recipes in other languages. Useful for hreflang tags and locale switching.

GET /v1/recipes/bacalhau-a-bras/links

Sample Response

[
  {
    "slug": "shredded-cod-with-eggs",
    "locale": "en-gb",
    "title": "Shredded Cod with Eggs"
  }
]
GET /recipes/trending Most viewed this week

Top 20 recipes by session count for the current week.

Sample Response

[
  {
    "slug": "bacalhau-com-natas",
    "title": "Bacalhau com Natas",
    "image": "146",
    "total_time": "50",
    "difficulty": "easy",
    "sessions": "219",
    "week_start": "2026-03-30"
  },
  {
    "slug": "polvo-a-lagareiro",
    "title": "Polvo à Lagareiro",
    "image": "21",
    "total_time": "55",
    "sessions": "181",
    "week_start": "2026-03-30"
  }
]
GET /recipes/by-category/{slug} Filter by category

Returns recipes in a specific category. Category slugs are locale-specific.

GET /v1/recipes/by-category/peixe        // PT: fish recipes
GET /v1/recipes/by-category/fish?locale=en-gb  // UK: fish recipes
GET /recipes/by-mode/{mode} Filter by cooking mode

Returns recipes compatible with a specific cooking mode.

Modes: kitchen-robot, airfryer, mc

GET /recipes/ratings All ratings aggregated

Returns average rating and count for all recipes.

POST /recipes/rate Submit a rating (1-5)
POST /v1/recipes/rate
{"slug": "bacalhau-a-bras", "rating": 5}

One rating per IP per recipe (upsert).

Ingredients

350 ingredients with localized names, unit conversions, SEO data, and supermarket search terms.

GET /ingredients All ingredients with locale names

Returns ingredient ID, localized name, slug, pack size, and pack unit.

Ingredient IDs are shared across locales. ID 25 is "bacalhau desfiado" in PT and "shredded desalted salt cod" in UK.

Sample Response

[
  {
    "id": "167",
    "name": "abacate",
    "slug": "abacate",
    "pack_size": "1",
    "pack_unit": "un"
  },
  {
    "id": "109",
    "name": "abóbora hokkaido",
    "slug": "abobora-hokkaido",
    "pack_size": "1000",
    "pack_unit": "g"
  }
]
GET /ingredients/{id} Single ingredient with recipes

Returns ingredient details, unit conversions, and all recipes using this ingredient.

Sample Response

{
  "id": "25",
  "name": "bacalhau desfiado e demolhado",
  "slug": "bacalhau-desfiado-e-demolhado",
  "pack_size": "400",
  "pack_unit": "g",
  "canonical_name": "bacalhau desfiado e demolhado",
  "conversions": [],
  "recipes": [
    {"slug": "bacalhau-a-bras", "title": "Bacalhau à Brás", "image": "147", "total_time": "35"},
    {"slug": "bacalhau-com-natas", "title": "Bacalhau com Natas", "image": "146", "total_time": "50"}
  ]
}
GET /ingredients/recipe-index Ingredients used in recipes (for search)

Returns only ingredients that appear in at least one recipe. Ideal for "search by ingredient" features.

Sample Response

[
  {"id": "1", "name": "azeite virgem extra", "slug": "azeite-virgem-extra"},
  {"id": "14", "name": "ovos", "slug": "ovos"},
  ...
]
GET /ingredients/conversions Unit conversion factors

Returns conversion factors (e.g., 1 tablespoon of flour = 10g).

GET /ingredients/seo SEO content (how to choose, store, tips)

Returns how_to_choose, how_to_store, tip, and FAQs for each ingredient.

Prices

Real supermarket prices updated regularly. PT: Continente, Auchan, Pingo Doce, Aldi. UK: Tesco, Waitrose.

GET /prices All prices for locale

Returns current prices for all ingredients across all supermarkets in the locale.

Sample Response

[
  {
    "ingredient_id": "167",
    "ingredient_name": "abacate",
    "ingredient_slug": "abacate",
    "supermarket": "continente",
    "supermarket_name": "Continente",
    "price": "2.05",
    "product": "Abacate Biológico Continente Bio",
    "packaging": "emb. 300 gr (2 un)",
    "normalized_price": "1.03",
    "date": "2026-03-15"
  }
]
GET /prices/{supermarket} Prices for a specific store

Filter by supermarket code.

PT codes: continente, auchan, pingodoce, aldi-pt

UK codes: tesco, sainsburys, asda, waitrose, aldi-uk, lidl-uk

GET /prices/history/{ingredient_id} Price history for an ingredient

Returns up to 100 historical price entries, ordered by date descending.

Categories

GET /categories All categories with recipe count

Returns locale-specific categories with slug, label, icon, color, and recipe count.

PT: carne, peixe, vegetariano, sobremesas, sopas, lanches, tradicional, rapidas

UK: meat, fish, vegetarian, vegan, desserts, soups, snacks, bbq, traditional, quick, pasta, breakfast, salads, oven, sauces

Sample Response

[
  {
    "id": "2",
    "slug": "carne",
    "label": "Carne",
    "description": "Pratos de carne para todos os gostos",
    "icon": "meat",
    "color": "red",
    "sort_order": "0",
    "recipe_count": "116"
  },
  {
    "id": "1",
    "slug": "churrasco",
    "label": "Churrasco",
    "icon": "fire",
    "color": "orange",
    "sort_order": "0",
    "recipe_count": "19"
  }
]

Substitutions

GET /substitutions All ingredient substitutions

Returns substitution rules with ratio, notes, and tags (vegan, lactose-free, etc.).

GET /substitutions/{ingredient_slug} Substitutes for a specific ingredient

Returns all substitution options for a given ingredient, with keywords and tags.

GET /v1/substitutions/manteiga

Sample Response

{
  "ingredient_slug": "manteiga",
  "keywords": ["manteiga"],
  "substitutions": [
    {
      "ingredient_name": "Manteiga",
      "category": "lacteos",
      "substitute_slug": "azeite",
      "substitute_name": "Azeite",
      "substitute_ingredient_id": "1",
      "ratio": "3/4 da quantidade",
      "ratio_value": "0.75",
      "note": "Funciona em refogados e massas salgadas",
      "tags": ["mais-saudavel", "sem-lactose", "vegano"]
    },
    {
      "substitute_slug": "margarina-vegetal",
      "substitute_name": "Margarina vegetal",
      "ratio": "1:1",
      "ratio_value": "1.00",
      "note": "Verificar se é sem lactose",
      "tags": ["mais-barato"]
    }
  ]
}

Local Stores

GET /stores All local stores with locations

Returns local stores (grocers, butchers, markets, etc.) with addresses and coordinates.

Sample Response

[
  {
    "id": "1",
    "name": "Mercearia do Bairro",
    "type": "mercearia",
    "color": "#78716c",
    "rank": "3",
    "active": "1",
    "locations": [
      {
        "city": "Lisboa",
        "address": "Rua da Prata 42",
        "phone": "+351 21 123 4567",
        "lat": "38.7100000",
        "lng": "-9.1370000"
      }
    ]
  }
]
GET /stores/city/{city} Stores in a specific city
GET /v1/stores/city/lisboa
GET /v1/stores/city/london?locale=en-gb
POST /stores/recipe Best stores for a recipe

Given a city and ingredient IDs, returns stores ranked by ingredient coverage.

POST /v1/stores/recipe
{"city": "lisboa", "ingredient_ids": [1, 25, 48, 14]}

Sample Response

[
  {
    "id": "1",
    "name": "Mercearia do Bairro",
    "type": "mercearia",
    "color": "#78716c",
    "rank": "3",
    "address": "Rua da Prata 42",
    "phone": "+351 21 123 4567",
    "covered_ingredients": "1,25,14",
    "coverage": "3"
  }
]

Supermarkets

Active supermarket list for the authenticated locale, with brand colour and search URL — useful when rendering price comparison charts or "buy at" CTAs.

GET /supermarkets List supermarkets for current locale

Returns the supermarkets that are active for the locale tied to your API key (override with ?locale=en-gb). PT returns Continente, Auchan, Pingo Doce, Aldi. UK returns Tesco, Sainsbury's, Asda, Aldi, Lidl, Waitrose.

Sample Response

[
  {
    "code": "continente",
    "name": "Continente",
    "color": "#c4354a",
    "search_url": "https://www.continente.pt/pesquisa/?q="
  },
  {
    "code": "auchan",
    "name": "Auchan",
    "color": "#b07040",
    "search_url": "https://www.auchan.pt/pesquisa?q="
  }
]

Shopping Lists

POST /lists Create a new shopping list

Creates a new list and returns UUID + short code for sharing.

Sample Response

{
  "uuid": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "short_code": "f8e2a1b3"
}
GET /lists/{uuid} Get shopping list

Returns list with recipes, servings, and pantry items. Accepts UUID or short code.

Sample Response

{
  "id": "1",
  "locale": "pt",
  "uuid": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "short_code": "f8e2a1b3",
  "recipes": [
    {"slug": "bacalhau-a-bras", "title": "Bacalhau à Brás", "image": "147", "servings": "4"}
  ],
  "pantry": [
    {"ingredient_id": "48", "name": "água", "qty": "1.00", "unit": "L"}
  ]
}
POST /lists/{uuid} Update shopping list
POST /v1/lists/a1b2c3...
{
  "recipes": [{"slug": "bacalhau-a-bras", "servings": 4}],
  "pantry": [{"ingredient_id": 48, "qty": 1, "unit": "L"}]
}

Locales

GET /locales Available locales

Returns all active locales with currency, country, domain, and kitchen robot brand name.

Sample Response

[
  {
    "code": "pt",
    "name": "Português",
    "currency": "EUR",
    "country": "Portugal",
    "domain": "bourmet.pt",
    "kitchen_robot_name": "Bimby",
    "active": "1"
  },
  {
    "code": "en-gb",
    "name": "English (UK)",
    "currency": "GBP",
    "country": "United Kingdom",
    "domain": "bourmet.co.uk",
    "kitchen_robot_name": "Thermomix",
    "active": "1"
  }
]
GET /locales/{code} Locale details with stats

Returns locale info plus supermarkets, recipe count, ingredient count, and category count.

Sample Response

{
  "code": "pt",
  "name": "Português",
  "currency": "EUR",
  "country": "Portugal",
  "domain": "bourmet.pt",
  "kitchen_robot_name": "Bimby",
  "supermarkets": [
    {"code": "continente", "name": "Continente", "active": "1"},
    {"code": "auchan", "name": "Auchan", "active": "1"},
    {"code": "pingodoce", "name": "Pingo Doce", "active": "1"},
    {"code": "aldi-pt", "name": "Aldi", "active": "1"}
  ],
  "recipe_count": 460,
  "ingredient_count": 294,
  "category_count": 8
}

Wine Pairings

Portuguese wine recommendations matched to recipes by type (tinto, branco, verde, rosé, moscatel, porto, espumante).

GET /wine/types Available wine types

Returns distinct wine types with the number of wines available for each.

Sample Response

[
  {"wine_type": "espumante", "count": "2"},
  {"wine_type": "moscatel", "count": "2"},
  {"wine_type": "porto", "count": "1"},
  {"wine_type": "rose", "count": "2"},
  {"wine_type": "vinho_branco", "count": "3"},
  {"wine_type": "vinho_tinto", "count": "4"},
  {"wine_type": "vinho_verde", "count": "2"}
]
GET /wine/pairing/{type} Wines for a specific type

Returns wines available for the given type. Use with the pairing_types field from recipe responses.

GET /v1/wine/pairing/vinho_branco

Sample Response

[
  {
    "wine_name": "Quinta da Aveleda Vinho Verde",
    "wine_type": "vinho_branco",
    "description": "Branco fresco e mineral do Minho...",
    "url": "https://example.com/wine",
    "brand_name": "Aveleda"
  }
]
Tip: A recipe's pairing_types field tells you which wine type to query. E.g., {"category":"wine","type_key":"vinho_branco"} means call /wine/pairing/vinho_branco.

Beer Pairings

Portuguese craft beer recommendations matched to recipes by style (IPA, APA, pilsner, lager, stout, porter, weiss, sour, belgian ale, trigo).

GET /beer/types Available beer styles

Returns distinct beer styles with the number of beers available for each.

Sample Response

[
  {"beer_type": "apa", "count": "2"},
  {"beer_type": "belgian_ale", "count": "2"},
  {"beer_type": "ipa", "count": "2"},
  {"beer_type": "lager", "count": "2"},
  {"beer_type": "pilsner", "count": "2"},
  {"beer_type": "porter", "count": "2"},
  {"beer_type": "sour", "count": "2"},
  {"beer_type": "stout", "count": "2"},
  {"beer_type": "trigo", "count": "2"},
  {"beer_type": "weiss", "count": "2"}
]
GET /beer/pairing/{type} Beers for a specific style

Returns craft beers available for the given style.

GET /v1/beer/pairing/ipa

Sample Response

[
  {
    "beer_name": "Dois Corvos Matiné IPA",
    "beer_type": "ipa",
    "description": "IPA fresca e cítrica, equilibrada entre lúpulos e maltes...",
    "url": null,
    "brand_name": "Dois Corvos"
  },
  {
    "beer_name": "Letra G IPA",
    "beer_type": "ipa",
    "description": "IPA intensa e aromática, com amargor marcado...",
    "url": null,
    "brand_name": "Letra"
  }
]

Chefs Premium

Profiles of chefs with whom Bourmet has signed content partnerships. Each chef brings exclusive recipes, sponsor approvals, social links and a featured slot for the consumer apps. Currently active: Hélio Loureiro (PT).

Plan: requires Pro or above. Free / Basic plans get a 403.
GET /chefs All active chefs

Lightweight list of active chefs (id, slug, name, title, photo, tagline, accent colour). Use this as the index for picker UIs.

Sample Response

[
  {
    "id": 1,
    "slug": "helio-loureiro",
    "name": "Hélio Loureiro",
    "title": "Chef Executivo",
    "photo": "chefs/helio-loureiro.jpg",
    "tagline": "Cozinha portuguesa contemporânea",
    "signature_color": "#0F4C5C",
    "accent_color": "#E36414"
  }
]
GET /chefs/featured Featured chef + stats + achievements + recipes

Returns the chef currently flagged as is_featured=1, with stats (years cooking, awards, books), achievements, social links and all published recipes. Designed for "Chef em destaque" homepage blocks.

GET /chefs/{slug} Full chef profile

Full profile: bio (short + long), title, hero image, signature/accent colours, contact email, plus stats, achievements, books, and socials.

GET /v1/chefs/helio-loureiro
GET /chefs/{slug}/recipes All recipes by chef

Recipes where chef_id matches the slug. Same shape as the regular recipe list (id, slug, title, image, total_time, difficulty, categories).

GET /v1/chefs/helio-loureiro/recipes
GET /chefs/{slug}/sponsors Brands approved by the chef

List of sponsor brands the chef has personally approved (or flagged as official partner). Each entry includes brand name, image, partner flag and whether the approval is active.

GET /v1/chefs/helio-loureiro/sponsors

Meal Plan Premium

Generate budget-optimized weekly meal plans, swap individual recipes without rebuilding the whole plan, and share results via short token. Built on top of the recipes + ingredients + prices catalog.

Plan: requires Pro or above. Free / Basic plans get a 403.
POST /meal-plan/generate Generate plan from constraints

Returns an optimized plan that fits the constraints (budget, meals, servings, difficulty, cooking modes). Output includes the picked recipes, total cost, ingredient roll-up and a per-supermarket coverage estimate.

Request Body

{
  "budget": 50.00,
  "meals": 7,
  "servings": 2,
  "meal_types": ["almoco", "jantar"],
  "exclude_categories": ["sobremesa"],
  "modes": ["airfryer"],
  "max_difficulty": "medium",
  "fixed_store": "continente"
}

Parameters

FieldDefaultNotes
budgetrequiredTotal budget for the plan (locale currency)
meals71–21
servings21–10
meal_types["almoco","jantar"]Filter by meal slot
modesanykitchen-robot, airfryer, mc
max_difficultyanyeasy, medium, hard
fixed_storecheapestForce a single supermarket for cost calc
POST /meal-plan/swap Swap one recipe in an existing plan

Replace one slot in an existing plan with a different recipe that respects the same constraints. Useful for "I don't feel like fish today" UX.

{
  "plan": { ... existing plan object ... },
  "swap_index": 2,
  "exclude_recipe_ids": [142, 87]
}
POST /meal-plan/save Persist plan and get share token

Saves a generated plan to the database and returns a short share token plus a long UUID. The token is the path component used by the GET endpoint below.

GET /meal-plan/{token} Retrieve saved plan by share token

Public-by-token retrieval — anyone with the token can see the plan, no API key restriction beyond the per-key rate limit. Designed to be shareable.

Guides

Long-form lifestyle and how-to content (e.g. "Tipos de sal e quando usar"). Every guide is locale-scoped, has a hero image, sections, FAQs and related recipes.

GET /guides Paginated list of published guides

Returns guide cards (id, slug, category, title, subtitle, intro, hero_image, reading_time_min, published_at). Default 20 per page, max 100.

GET /v1/guides
GET /v1/guides?category=ingredientes&sort=published_at&order=desc
GET /v1/guides?search=sal&per_page=10

Filters

ParamExampleDescription
categoryingredientesFilter by category slug
searchsalSearch title/subtitle/intro
sortpublished_atpublished_at, title, category, created_at
orderdescasc, desc
GET /guides/by-category/{category} Guides scoped to one category

Convenience endpoint for category landing pages. Equivalent to /guides?category=... but with a cleaner URL.

GET /guides/by-recipe/{slug} Guides linked to a recipe (reverse cross-link)

Returns published guides whose related_recipes contain the given recipe slug. Used for the "Saiba mais" section on recipe pages — the inverse of the recipe list embedded in each guide.

GET /v1/guides/by-recipe/bacalhau-assado-com-batatas
GET /guides/{category}/{slug} Full guide

Returns the full guide payload: hero, intro, ordered sections (heading + body + optional images), FAQs and related recipes (linked by ingredient overlap).

GET /v1/guides/ingredientes/sal-tipos-quando-usar

Images & Assets

Recipe images are named by PT recipe ID and served as static files.

TypePathFormat
Original/assets/recipes/{id}.jpgFull quality JPG
Mobile/assets/mobile/{id}.webp768px WebP (~50-80KB)
Audio/assets/audio/{id}/step-{n}.mp3TTS narration (PT only)
Video/assets/videos/{id}.mp4Recipe video
Image ID: The image field in recipe responses contains the ID. Build the full URL as: https://bourmet.dev/assets/recipes/{image}.jpg

UK recipes share images with their PT counterparts. The image field for linked UK recipes contains the PT recipe ID.

Assets are served with Cache-Control: public, max-age=31536000, immutable and CORS headers.

Pricing

Pick the tier that matches your usage. Endpoints marked Premium in the reference above require Pro or above.

Free

0/mo
  • 100 requests/day
  • 5 requests/minute
  • Recipes (read-only)
  • Ingredients (read-only)
  • Categories & locales

Basic

€9.99/mo
  • 5,000 requests/day
  • 30 requests/minute
  • Everything in Free
  • Prices & price history
  • Supermarkets & stores
  • Substitutions

Pro

€29.99/mo
  • 50,000 requests/day
  • 100 requests/minute
  • Everything in Basic
  • Wine & beer pairings
  • Guides
  • Shopping lists
  • Chefs Premium
  • Meal plan Premium

Enterprise

Custom
  • 500,000+ requests/day
  • All public endpoints
  • Priority support
  • Custom integrations
  • SLA & bulk export

To request an API key, contact miguel.teixeira@bourmet.pt. Free keys are issued automatically.

Errors

CodeMeaningResponse
401Missing or invalid API key{"error": "Missing API key"}
403Key expired or endpoint restricted{"error": "API key expired"}
404Resource not found{"error": "Recipe not found"}
405Method not allowed{"error": "Method not allowed"}
429Rate limit exceeded{"error": "Daily rate limit exceeded", "limit": 100, "used": 101}

All error responses include an error field with a human-readable message.