Merran documentation API & integrations
Add a recipe from a page already open
const url = 'http://localhost:3000/api/recipes/import/page';const options = { method: 'POST', headers: { cookie: 'merran.session_token=<merran.session_token>', 'Content-Type': 'application/json' }, body: '{"url":"https://cooking.example/lemon-chicken","title":"Lemon Chicken | Cooking","jsonld":["{\"@type\":\"Recipe\",\"name\":\"Lemon Chicken\"}"],"images":["https://cooking.example/lemon-chicken.jpg"],"text":"Lemon Chicken. Serves 4. 4 chicken thighs, 2 lemons..."}'};
try { const response = await fetch(url, options); const data = await response.json(); console.log(data);} catch (error) { console.error(error);}curl --request POST \ --url http://localhost:3000/api/recipes/import/page \ --header 'Content-Type: application/json' \ --cookie merran.session_token=<merran.session_token> \ --data '{ "url": "https://cooking.example/lemon-chicken", "title": "Lemon Chicken | Cooking", "jsonld": [ "{\"@type\":\"Recipe\",\"name\":\"Lemon Chicken\"}" ], "images": [ "https://cooking.example/lemon-chicken.jpg" ], "text": "Lemon Chicken. Serves 4. 4 chicken thighs, 2 lemons..." }'For the browser extension: a page as the person’s own browser shows it, for sites that turn Merran’s server away or only show a recipe to someone signed in. Its recipe data and photo, or its text read by AI when the household has AI for importing. A video’s page (YouTube, TikTok, Instagram, Facebook) is imported as the video, as POST /recipes/import does. A page imported before gives back that recipe. With an API key, it needs the imports: write permission.
Authorizations
Section titled “Authorizations”Request Bodyrequired
Section titled “Request Bodyrequired”object
The page’s JSON-LD blocks, as found.
Its share images: og:image and the like.
Its text, as the browser shows it.
Example
{ "url": "https://cooking.example/lemon-chicken", "title": "Lemon Chicken | Cooking", "jsonld": [ "{\"@type\":\"Recipe\",\"name\":\"Lemon Chicken\"}" ], "images": [ "https://cooking.example/lemon-chicken.jpg" ], "text": "Lemon Chicken. Serves 4. 4 chicken thighs, 2 lemons..."}Responses
Section titled “Responses”Already in the library.
object
object
As given, or prep and cook added up.
Diet labels worked out from the ingredients, with the household’s corrections.
Where its photo is, in each size, or null. The links change with the photo, so they can be kept.
object
For the recipe page.
Cropped, for recipe cards.
Its photo was made by AI, in the household’s style. Making another replaces it; a real one always does.
Where the household corrected the labels the ingredients suggest.
object
Foods the household said it can’t do without: a dislike that’s usually left out of recipes rules this one out instead.
Per serving, when it’s known.
object
From the recipe’s site, worked out from USDA’s data, or estimated by AI.
Kcal.
Grams.
Grams.
Grams.
Grams.
Grams.
Milligrams.
Ingredient lines that couldn’t be counted.
The servings it’s for; it rescales when they change.
object
The line as written.
What it is to the dish, as tidy up marked it: needed, swappable or optional. A dislike is left out of lines that aren’t needed. None until marked.
object
True when the link was imported before: this is that recipe, unchanged.
Example
{ "recipe": { "id": "0199e8b0-5a10-7a01-8b00-000000000006", "slug": "lemon-chicken", "name": "Lemon Chicken", "description": "Crisp chicken thighs roasted with lemon, garlic and thyme.", "servings": 4, "totalMinutes": 50, "rating": 5, "tags": [ "chicken", "dinner" ], "diets": [ "gluten-free", "dairy-free", "egg-free", "nut-free" ], "photo": { "full": "/api/recipes/0199e8b0-5a10-7a01-8b00-000000000006/photo?size=full&v=4b1c2d3e", "card": "/api/recipes/0199e8b0-5a10-7a01-8b00-000000000006/photo?size=card&v=4b1c2d3e" }, "lastMadeAt": "2026-10-09T01:15:00.000Z", "createdAt": "2026-10-01T17:30:00.000Z", "updatedAt": "2026-10-15T18:30:00.000Z", "origin": "link", "photoByAi": false, "sourceUrl": "https://example.com/lemon-chicken", "yieldText": null, "prepMinutes": 10, "cookMinutes": 40, "notes": "Bone-in thighs work too: give them 10 minutes more.", "dietOverrides": {}, "needs": [], "nutrition": { "source": "calculated", "calories": 412, "protein": 31.5, "carbs": 6.2, "fat": 28.1, "fiber": 1.4, "sugar": 1.2, "sodium": 640, "missing": [], "servings": 4 }, "ingredients": [ { "id": "0199e8b0-5a10-7a01-8b00-000000000011", "section": null, "text": "2 lb chicken thighs", "quantity": 2, "quantityMax": null, "unit": "pound", "food": "chicken thighs", "note": null, "mark": "needed" }, { "id": "0199e8b0-5a10-7a01-8b00-000000000012", "section": null, "text": "1 tbsp olive oil", "quantity": 1, "quantityMax": null, "unit": "tablespoon", "food": "olive oil", "note": null, "mark": "optional" }, { "id": "0199e8b0-5a10-7a01-8b00-000000000013", "section": null, "text": "2 lemons, juiced", "quantity": 2, "quantityMax": null, "unit": null, "food": "lemons", "note": "juiced", "mark": "needed" }, { "id": "0199e8b0-5a10-7a01-8b00-000000000014", "section": null, "text": "4 cloves garlic, smashed", "quantity": 4, "quantityMax": null, "unit": "clove", "food": "garlic", "note": "smashed", "mark": "swappable" } ], "steps": [ { "id": "0199e8b0-5a10-7a01-8b00-000000000015", "section": null, "text": "Heat the oven to 425°F." }, { "id": "0199e8b0-5a10-7a01-8b00-000000000016", "section": null, "text": "Toss the chicken with the oil, lemon juice, garlic, salt and pepper." }, { "id": "0199e8b0-5a10-7a01-8b00-000000000017", "section": null, "text": "Roast skin side up for 40 minutes, until crisp." } ] }, "duplicate": false}Added.
object
object
As given, or prep and cook added up.
Diet labels worked out from the ingredients, with the household’s corrections.
Where its photo is, in each size, or null. The links change with the photo, so they can be kept.
object
For the recipe page.
Cropped, for recipe cards.
Its photo was made by AI, in the household’s style. Making another replaces it; a real one always does.
Where the household corrected the labels the ingredients suggest.
object
Foods the household said it can’t do without: a dislike that’s usually left out of recipes rules this one out instead.
Per serving, when it’s known.
object
From the recipe’s site, worked out from USDA’s data, or estimated by AI.
Kcal.
Grams.
Grams.
Grams.
Grams.
Grams.
Milligrams.
Ingredient lines that couldn’t be counted.
The servings it’s for; it rescales when they change.
object
The line as written.
What it is to the dish, as tidy up marked it: needed, swappable or optional. A dislike is left out of lines that aren’t needed. None until marked.
object
True when the link was imported before: this is that recipe, unchanged.
Example
{ "recipe": { "id": "0199e8b0-5a10-7a01-8b00-000000000006", "slug": "lemon-chicken", "name": "Lemon Chicken", "description": "Crisp chicken thighs roasted with lemon, garlic and thyme.", "servings": 4, "totalMinutes": 50, "rating": 5, "tags": [ "chicken", "dinner" ], "diets": [ "gluten-free", "dairy-free", "egg-free", "nut-free" ], "photo": { "full": "/api/recipes/0199e8b0-5a10-7a01-8b00-000000000006/photo?size=full&v=4b1c2d3e", "card": "/api/recipes/0199e8b0-5a10-7a01-8b00-000000000006/photo?size=card&v=4b1c2d3e" }, "lastMadeAt": "2026-10-09T01:15:00.000Z", "createdAt": "2026-10-01T17:30:00.000Z", "updatedAt": "2026-10-15T18:30:00.000Z", "origin": "link", "photoByAi": false, "sourceUrl": "https://example.com/lemon-chicken", "yieldText": null, "prepMinutes": 10, "cookMinutes": 40, "notes": "Bone-in thighs work too: give them 10 minutes more.", "dietOverrides": {}, "needs": [], "nutrition": { "source": "calculated", "calories": 412, "protein": 31.5, "carbs": 6.2, "fat": 28.1, "fiber": 1.4, "sugar": 1.2, "sodium": 640, "missing": [], "servings": 4 }, "ingredients": [ { "id": "0199e8b0-5a10-7a01-8b00-000000000011", "section": null, "text": "2 lb chicken thighs", "quantity": 2, "quantityMax": null, "unit": "pound", "food": "chicken thighs", "note": null, "mark": "needed" }, { "id": "0199e8b0-5a10-7a01-8b00-000000000012", "section": null, "text": "1 tbsp olive oil", "quantity": 1, "quantityMax": null, "unit": "tablespoon", "food": "olive oil", "note": null, "mark": "optional" }, { "id": "0199e8b0-5a10-7a01-8b00-000000000013", "section": null, "text": "2 lemons, juiced", "quantity": 2, "quantityMax": null, "unit": null, "food": "lemons", "note": "juiced", "mark": "needed" }, { "id": "0199e8b0-5a10-7a01-8b00-000000000014", "section": null, "text": "4 cloves garlic, smashed", "quantity": 4, "quantityMax": null, "unit": "clove", "food": "garlic", "note": "smashed", "mark": "swappable" } ], "steps": [ { "id": "0199e8b0-5a10-7a01-8b00-000000000015", "section": null, "text": "Heat the oven to 425°F." }, { "id": "0199e8b0-5a10-7a01-8b00-000000000016", "section": null, "text": "Toss the chicken with the oil, lemon juice, garlic, salt and pepper." }, { "id": "0199e8b0-5a10-7a01-8b00-000000000017", "section": null, "text": "Roast skin side up for 40 minutes, until crisp." } ] }, "duplicate": false}Not a link Merran can keep.
object
object
object
Example
{ "error": { "code": "not_found", "message": "There's nothing here.", "fields": { "name": "Give it a name." } }}Not signed in.
object
object
object
Example
{ "error": { "code": "not_found", "message": "There's nothing here.", "fields": { "name": "Give it a name." } }}Not allowed.
object
object
object
Example
{ "error": { "code": "not_found", "message": "There's nothing here.", "fields": { "name": "Give it a name." } }}The page is over 2 MB.
object
object
object
Example
{ "error": { "code": "not_found", "message": "There's nothing here.", "fields": { "name": "Give it a name." } }}The page has no recipe data and no AI to read it, or AI found no recipe on it.
object
object
object
Example
{ "error": { "code": "not_found", "message": "There's nothing here.", "fields": { "name": "Give it a name." } }}© 2026 Kyle Johnson. Documentation: CC BY 4.0. Merran: AGPL-3.0-or-later.