Recipes
List Recipes
GET /api/v1/recipes
Returns a paginated list of recipes. Supports search, filtering, sorting, and ingredient search.
Query Parameters
| Parameter | Type | Description |
|---|---|---|
lang | string | PaidResponse language (en, fr, es). Defaults to en. |
search | string | Search recipes by name (case-insensitive). Searches in the requested language with English fallback. |
search_in | string | Where to search: name (default), description, or both |
ingredients | string | Filter by ingredients — comma-separated names (case-insensitive partial match) |
cuisine | string | Filter by cuisine (e.g. italian, french, asian) |
meal_type | string | Filter by meal type (e.g. main, dessert, starter) |
difficulty | string | Filter by difficulty (easy, medium, hard) |
dietary_tags | string | Filter by dietary tags (e.g. vegetarian, vegan, gluten_free). Currently supports a single value only — multiple values coming soon. |
prep_time_min | integer | Minimum preparation time (minutes) |
prep_time_max | integer | Maximum preparation time (minutes) |
cook_time_min | integer | Minimum cooking time (minutes) |
cook_time_max | integer | Maximum cooking time (minutes) |
calories_per_serving_min | integer | Minimum calories per serving |
calories_per_serving_max | integer | Maximum calories per serving |
protein_min | integer | Minimum protein (grams) |
protein_max | integer | Maximum protein (grams) |
sort | string | Sort field: name, prep_time, cook_time, calories_per_serving, protein (default: name) |
order | string | Sort order: asc or desc (default: asc) |
per_page | integer | Items per page (default: 10, max depends on your plan) |
page | integer | Page number (default: 1) |
Enum Values
Cuisine
french, italian, asian, mexican, indian, mediterranean, american, japanese, chinese, thai,
moroccan, spanish, greek, british, german, other
Meal Type
starter, main, dessert, appetizer, breakfast, brunch, snack, side_dish, soup, drink, sauce
Difficulty
easy, medium, hard
Dietary Tags
vegetarian, vegan, gluten_free, dairy_free, nut_free, halal, kosher
The dietary_tags filter currently accepts a single value only (e.g. dietary_tags=vegan). Support for filtering by
multiple values is coming soon.
Example Request
- cURL
- JavaScript
- Python
- PHP
- Ruby
- Go
curl "https://recipeapi.io/api/v1/recipes?cuisine=italian&difficulty=easy&per_page=5" \
-H "Authorization: Bearer sk_live_..."
const API_KEY = "sk_live_...";
const response = await fetch(
"https://recipeapi.io/api/v1/recipes?cuisine=italian&difficulty=easy&per_page=5",
{headers: {"Authorization": `Bearer ${API_KEY}`}}
);
const data = await response.json();
console.log(data);
import requests
API_KEY = "sk_live_..."
response = requests.get(
"https://recipeapi.io/api/v1/recipes",
params={"cuisine": "italian", "difficulty": "easy", "per_page": 5},
headers={"Authorization": f"Bearer {API_KEY}"}
)
data = response.json()
print(data)
// composer require guzzlehttp/guzzle
const API_KEY = 'sk_live_...';
$client = new GuzzleHttp\Client();
$response = $client->get('https://recipeapi.io/api/v1/recipes', [
'query' => ['cuisine' => 'italian', 'difficulty' => 'easy', 'per_page' => 5],
'headers' => ['Authorization' => 'Bearer ' . API_KEY],
]);
$data = json_decode($response->getBody(), true);
require "net/http"
require "json"
API_KEY = "sk_live_..."
uri = URI("https://recipeapi.io/api/v1/recipes?cuisine=italian&difficulty=easy&per_page=5")
req = Net::HTTP::Get.new(uri)
req["Authorization"] = "Bearer #{API_KEY}"
res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |http|
http.request(req)
}
data = JSON.parse(res.body)
puts data
package main
import (
"fmt"
"io"
"net/http"
)
const apiKey = "sk_live_..."
func main() {
req, _ := http.NewRequest("GET",
"https://recipeapi.io/api/v1/recipes?cuisine=italian&difficulty=easy&per_page=5", nil)
req.Header.Set("Authorization", "Bearer "+apiKey)
resp, _ := http.DefaultClient.Do(req)
body, _ := io.ReadAll(resp.Body)
fmt.Println(string(body))
}
Search by Ingredients
Use the ingredients parameter to find recipes that contain specific ingredients:
- cURL
- JavaScript
- Python
- PHP
- Ruby
- Go
curl "https://recipeapi.io/api/v1/recipes?ingredients=tomato,basil" \
-H "Authorization: Bearer sk_live_..."
const API_KEY = "sk_live_...";
const response = await fetch(
"https://recipeapi.io/api/v1/recipes?ingredients=tomato,basil",
{headers: {"Authorization": `Bearer ${API_KEY}`}}
);
const data = await response.json();
console.log(data);
import requests
API_KEY = "sk_live_..."
response = requests.get(
"https://recipeapi.io/api/v1/recipes",
params={"ingredients": "tomato,basil"},
headers={"Authorization": f"Bearer {API_KEY}"}
)
data = response.json()
print(data)
// composer require guzzlehttp/guzzle
const API_KEY = 'sk_live_...';
$client = new GuzzleHttp\Client();
$response = $client->get('https://recipeapi.io/api/v1/recipes', [
'query' => ['ingredients' => 'tomato,basil'],
'headers' => ['Authorization' => 'Bearer ' . API_KEY],
]);
$data = json_decode($response->getBody(), true);
require "net/http"
require "json"
API_KEY = "sk_live_..."
uri = URI("https://recipeapi.io/api/v1/recipes?ingredients=tomato,basil")
req = Net::HTTP::Get.new(uri)
req["Authorization"] = "Bearer #{API_KEY}"
res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |http|
http.request(req)
}
data = JSON.parse(res.body)
puts data
package main
import (
"fmt"
"io"
"net/http"
)
const apiKey = "sk_live_..."
func main() {
req, _ := http.NewRequest("GET",
"https://recipeapi.io/api/v1/recipes?ingredients=tomato,basil", nil)
req.Header.Set("Authorization", "Bearer "+apiKey)
resp, _ := http.DefaultClient.Do(req)
body, _ := io.ReadAll(resp.Body)
fmt.Println(string(body))
}
This returns recipes where at least one ingredient matches (case-insensitive partial match). You can combine it with any other filter.
Example Response
{
"data": [
{
"id": 449,
"name": "Beef Tostadas",
"description": "Beef tostadas are a popular Mexican dish featuring crispy fried tortillas topped with seasoned ground beef and fresh toppings for a delicious and satisfying meal.",
"difficulty": "easy",
"meal_type": "main",
"cuisine": "american",
"dietary_tags": [
"nut_free"
],
"servings": 6,
"prep_time": 15,
"cook_time": 15,
"calories_per_serving": 450,
"protein": 25,
"instructions": [
"Heat the vegetable oil in a frying pan over medium heat until hot.",
"Add the beef and cook, stirring occasionally, until browned and cooked through.",
"Add chopped onion and minced garlic to the beef, cook until the onion is translucent.",
"..."
],
"ingredients": [
{
"id": 58,
"name": "Chili powder",
"category": "spice",
"quantity": 1,
"unit": "tsp",
"optional": false
},
{
"id": 37,
"name": "Garlic",
"category": "vegetable",
"quantity": 2,
"unit": "clove",
"optional": false
},
"..."
]
},
{
"..."
},
{
"..."
}
],
"links": {
"first": "https://recipeapi.io/api/v1/recipes?page=1&per_page=3",
"last": "https://recipeapi.io/api/v1/recipes?page=10&per_page=3",
"prev": null,
"next": "https://recipeapi.io/api/v1/recipes?page=2&per_page=3"
},
"meta": {
"current_page": 1,
"last_page": 10,
"path": "https://recipeapi.io/api/v1/recipes",
"per_page": 3,
"total": 30,
"language": "en"
}
}
Localization
All text fields (name, description, instructions) are translatable. Use the lang parameter to get content in a
specific language:
GET /api/v1/recipes?lang=fr&search=poulet
- Supported languages:
en,fr,es - The
meta.languagefield in every response indicates the requested language - Passing an unsupported language returns a
422error with the list of supported languages - Free plan users passing
lang(other thanen) receive a402error with codePLAN_FEATURE_UNAVAILABLE
Search Scope
By default, search only searches recipe names. Use search_in to expand the search scope:
GET /api/v1/recipes?search=spicy&search_in=description
GET /api/v1/recipes?search=chicken&search_in=both
search_in value | Searches in |
|---|---|
name (default) | Recipe name only |
description | Recipe description only |
both | Recipe name and description |
Get Recipe
GET /api/v1/recipes/{id}
Returns a single recipe with its full details, including instructions and ingredients.
Path Parameters
| Parameter | Type | Description |
|---|---|---|
id | integer | Recipe ID |
Query Parameters
| Parameter | Type | Description |
|---|---|---|
lang | string | PaidResponse language (en, fr, es). Defaults to en. |
Example Request
- cURL
- JavaScript
- Python
- PHP
- Ruby
- Go
curl "https://recipeapi.io/api/v1/recipes/449" \
-H "Authorization: Bearer sk_live_..."
const API_KEY = "sk_live_...";
const response = await fetch(
"https://recipeapi.io/api/v1/recipes/449",
{headers: {"Authorization": `Bearer ${API_KEY}`}}
);
const data = await response.json();
console.log(data);
import requests
API_KEY = "sk_live_..."
response = requests.get(
"https://recipeapi.io/api/v1/recipes/449",
headers={"Authorization": f"Bearer {API_KEY}"}
)
data = response.json()
print(data)
// composer require guzzlehttp/guzzle
const API_KEY = 'sk_live_...';
$client = new GuzzleHttp\Client();
$response = $client->get('https://recipeapi.io/api/v1/recipes/449', [
'headers' => ['Authorization' => 'Bearer ' . API_KEY],
]);
$data = json_decode($response->getBody(), true);
require "net/http"
require "json"
API_KEY = "sk_live_..."
uri = URI("https://recipeapi.io/api/v1/recipes/449")
req = Net::HTTP::Get.new(uri)
req["Authorization"] = "Bearer #{API_KEY}"
res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |http|
http.request(req)
}
data = JSON.parse(res.body)
puts data
package main
import (
"fmt"
"io"
"net/http"
)
const apiKey = "sk_live_..."
func main() {
req, _ := http.NewRequest("GET",
"https://recipeapi.io/api/v1/recipes/449", nil)
req.Header.Set("Authorization", "Bearer "+apiKey)
resp, _ := http.DefaultClient.Do(req)
body, _ := io.ReadAll(resp.Body)
fmt.Println(string(body))
}
Example Response
{
"data": {
"id": 449,
"name": "Beef Tostadas",
"description": "Beef tostadas are a popular Mexican dish featuring crispy fried tortillas topped with seasoned ground beef and fresh toppings for a delicious and satisfying meal.",
"difficulty": "easy",
"meal_type": "main",
"cuisine": "american",
"dietary_tags": [
"nut_free"
],
"servings": 6,
"prep_time": 15,
"cook_time": 15,
"calories_per_serving": 450,
"protein": 25,
"instructions": [
"Heat the vegetable oil in a frying pan over medium heat until hot.",
"Add the beef and cook, stirring occasionally, until browned and cooked through.",
"Add chopped onion and minced garlic to the beef, cook until the onion is translucent.",
"Stir in tomato paste, cumin, chili powder, salt, and pepper, cooking for a few minutes until well combined.",
"Warm the tostada shells in a separate pan or oven until crisp.",
"Place a generous spoonful of the beef mixture onto each tostada shell.",
"Top the beef with shredded lettuce, diced tomato, sliced avocado, shredded cheese, and a dollop of sour cream.",
"Garnish with chopped cilantro and serve immediately."
],
"ingredients": [
{
"id": 58,
"name": "Chili powder",
"category": "spice",
"quantity": 1,
"unit": "tsp",
"optional": false
},
{
"id": 37,
"name": "Garlic",
"category": "vegetable",
"quantity": 2,
"unit": "clove",
"optional": false
},
{
"id": 8,
"name": "Cheddar cheese",
"category": "dairy",
"quantity": 100,
"unit": "g",
"optional": false
},
"..."
]
},
"meta": {
"language": "en"
}
}
Error Response (404)
{
"error": {
"code": "NOT_FOUND",
"message": "The requested resource was not found."
}
}
Random Recipe
GET /api/v1/recipes/random
Returns a single random recipe with full details (instructions + ingredients). Supports the same filters as
the List Recipes endpoint to narrow down the random selection, except pagination (page,
per_page) and sorting (sort, order) parameters, which are not applicable.
Example Request
- cURL
- JavaScript
- Python
- PHP
- Ruby
- Go
curl "https://recipeapi.io/api/v1/recipes/random?cuisine=italian&difficulty=easy" \
-H "Authorization: Bearer sk_live_..."
const API_KEY = "sk_live_...";
const response = await fetch(
"https://recipeapi.io/api/v1/recipes/random?cuisine=italian&difficulty=easy",
{headers: {"Authorization": `Bearer ${API_KEY}`}}
);
const data = await response.json();
console.log(data);
import requests
API_KEY = "sk_live_..."
response = requests.get(
"https://recipeapi.io/api/v1/recipes/random",
params={"cuisine": "italian", "difficulty": "easy"},
headers={"Authorization": f"Bearer {API_KEY}"}
)
data = response.json()
print(data)
// composer require guzzlehttp/guzzle
const API_KEY = 'sk_live_...';
$client = new GuzzleHttp\Client();
$response = $client->get('https://recipeapi.io/api/v1/recipes/random', [
'query' => ['cuisine' => 'italian', 'difficulty' => 'easy'],
'headers' => ['Authorization' => 'Bearer ' . API_KEY],
]);
$data = json_decode($response->getBody(), true);
require "net/http"
require "json"
API_KEY = "sk_live_..."
uri = URI("https://recipeapi.io/api/v1/recipes/random?cuisine=italian&difficulty=easy")
req = Net::HTTP::Get.new(uri)
req["Authorization"] = "Bearer #{API_KEY}"
res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |http|
http.request(req)
}
data = JSON.parse(res.body)
puts data
package main
import (
"fmt"
"io"
"net/http"
)
const apiKey = "sk_live_..."
func main() {
req, _ := http.NewRequest("GET",
"https://recipeapi.io/api/v1/recipes/random?cuisine=italian&difficulty=easy", nil)
req.Header.Set("Authorization", "Bearer "+apiKey)
resp, _ := http.DefaultClient.Do(req)
body, _ := io.ReadAll(resp.Body)
fmt.Println(string(body))
}
Example Response
Same structure as Get Recipe response.
Error Response (422)
If no recipe matches the applied filters:
{
"error": {
"code": "NO_RECIPE_FOUND",
"message": "No recipe found matching your filters."
}
}