API reference
Recipes
Read, write and delete recipes, with their ingredients parsed into something answerable.
Paths below are relative to
https://recipejam.com/api/v1
/recipes
List recipes
Every recipe in the accounts your token can reach, most recently changed first.
Parameters
| Field | Type | Notes |
|---|---|---|
search
|
string · query | Matches ingredient lines, names and descriptions. Knows ingredient synonyms, so "aubergine" finds a recipe that says eggplant. |
tag
|
string · query | Only recipes carrying this exact tag. |
perPage
|
integer · query | 1 to 100. Defaults to 25. Anything larger is silently reduced to 100. |
page
|
integer · query | Which page, from 1. |
since
|
string · query | An ISO 8601 timestamp. Returns only what has changed since then, deleted records included, so a synced device can catch up without refetching everything. Url-encode it — a raw `+` in a query string arrives as a space. |
deleted
|
string · query | `only` lists the wastebasket — what was deleted and can still be restored, each with a `deletedAt`. `include` lists both. Left out, you get the ordinary listing. |
Response
200
with a page of Recipe objects in the pagination envelope.
- A listing never carries steps or images — ask for one recipe to get those.
- Search runs one term at a time. To require two ingredients, filter the second yourself.
/recipes/{recipe}
Get a recipe
One recipe in full, with its prep, method, cover and photographs.
Parameters
| Field | Type | Notes |
|---|---|---|
recipe
|
string · path | The id from a listing. |
Response
200
with a single Recipe object under a `data` key.
/recipes
Create a recipe
Add a recipe to the account. It is private until you publish it.
Request body
| Field | Type | Notes |
|---|---|---|
name
required
|
string max 255 | What the recipe is called. |
description
|
string, or null max 2000 | A sentence or two. |
isPublic
|
boolean | Defaults to false. |
tags
|
array of string each max 50 | Short labels. |
ingredients
|
array of object | Each entry needs a `name`, which is the line as written: "2 tbsp olive oil". Everything else on the line is worked out for you — do not send `parsed`, it is overwritten. |
ingredients[].name
required
|
string max 255 | The whole line, amount included. |
equipment
|
array of object | Each entry needs a `name`. |
notes
|
array | Free-form notes. |
metadata
|
object | `servings`, `prepTime`, `cookTime`, each as written. |
prepSteps
|
array of object | Work done before cooking starts. Each entry needs a `content`. Omit it if the recipe does not separate prep from method. |
steps
|
array of object | The method. Each entry needs a `content`. Order is the order you send them. |
steps[].content
required
|
string max 5000 | One instruction. Same shape for `prepSteps[].content`. |
Example request body
{
"name": "Kimchi pancakes",
"description": "Crisp at the edges.",
"ingredients": [
{
"name": "200g kimchi"
},
{
"name": "150g plain flour"
},
{
"name": "1 egg"
}
],
"prepSteps": [
{
"content": "Chop the kimchi roughly."
}
],
"steps": [
{
"content": "Mix everything to a loose batter."
},
{
"content": "Fry until crisp."
}
],
"tags": [
"korean",
"quick"
],
"metadata": {
"servings": "2",
"cookTime": "15 minutes"
}
}
Response
201
with a single Recipe object under a `data` key.
- `prepSteps` and `steps` are separate sections and are written independently. Sending one never disturbs the other.
/recipes/{recipe}
PUT
Update a recipe
Change one. Anything you leave out is left alone; a list you send replaces that list entirely.
Parameters
| Field | Type | Notes |
|---|---|---|
recipe
|
string · path | The recipe to change. |
Request body
| Field | Type | Notes |
|---|---|---|
name
|
string max 255 | A new name. |
description
|
string, or null max 2000 | A new description. |
isPublic
|
boolean | Publish or unpublish. |
tags
|
array of string | Replaces the whole tag list. |
ingredients
|
array of object | Replaces every ingredient line. Send the complete list, not the one line you are changing. |
prepSteps
|
array of object | Replaces the whole prep section. Sending `[]` removes it; leaving it out keeps it. |
steps
|
array of object | Replaces the whole method. Sending `[]` removes it. The prep section is untouched either way. |
Example request body
{
"name": "Kimchi pancakes, properly",
"tags": [
"korean"
]
}
Response
200
with a single Recipe object under a `data` key.
- PUT is accepted and does exactly the same thing. Despite the verb it is still a partial update — it does not blank the fields you leave out.
/recipes/{recipe}
Delete a recipe
Moves it to the bin. Recoverable for 30 days, then removed for good along with its photographs.
Parameters
| Field | Type | Notes |
|---|---|---|
recipe
|
string · path | The recipe to delete. |
Response
204
with an empty body.
- A recipe reached through a collection somebody shared with you can be read and edited, never deleted. That comes back 403.
- It disappears from every collection and every book that held it, and from every listing — unless you ask for changes `since` a timestamp, which includes it so a synced device knows to drop it.
- Its steps and photographs go with it and come back with it. The files stay on disk until the retention window closes.
/recipes/{recipe}/restore
Restore a recipe
Takes one back out of the bin, with the steps and photographs it went down with.
Parameters
| Field | Type | Notes |
|---|---|---|
recipe
|
string · path | The deleted recipe. Ordinary listings will not show it; find it with `since`, which reports it with a `deletedAt`. |
Response
200
with a single Recipe object under a `data` key.
- Only what was deleted with the recipe comes back. A photograph you removed on purpose a week earlier stays removed.
- Past the retention window there is nothing left to restore and this answers 404, the same as an id that never existed.
- It rejoins the collections and books it was in — membership survives the deletion, so a restore puts it back where it was rather than leaving you to file it again.
/recipes/{recipe}/reader
Read a recipe
The recipe already turned into pages: ingredients chunked, amounts linked into the method, timers parsed. Render these and hold no recipe logic of your own.
Parameters
| Field | Type | Notes |
|---|---|---|
recipe
|
string · path | The recipe to read. |
Response
200
with a single reader object under a `data` key.
- This is the same computation the website's own reader uses, so what you render matches what a cook sees on recipejam.com. Reimplementing the chunking or the ingredient linking on a client will drift from it, and the drift shows up as a wrong amount in somebody's kitchen.
- A recipe with no ingredients, equipment or steps returns no pages at all — an empty `pages` array rather than a cover with nothing behind it.
- Photograph URLs need your token, like every other image in this API.
- Cache against `subject.updatedAt`. If it has not moved, neither have the pages.
Try these
The endpoints on this page, sent from your browser with your own token. Nothing is proxied through us.
Kept in this browser only, so it survives a reload. It is never sent to RecipeJam except as the header on the request you ask for.
The same request, as curl
The recipe object
| Field | Type | Notes |
|---|---|---|
id
|
string | Opaque identifier. The same one the app uses in its own URLs; never a database number. |
name
|
string | What the recipe is called. |
description
|
string, or null | A sentence or two about the dish. |
isPublic
|
boolean | Whether the recipe answers on its public share link. |
tags
|
array of string | Short labels. Empty rather than null when there are none. |
ingredients
|
array of ingredient | One entry per line, in the order they are written. See below. |
equipment
|
array of object | Each entry has a `name`. |
notes
|
array of object | Free-form notes kept against the recipe. |
metadata
|
object | Timings and yield as written: `servings`, `prepTime`, `cookTime`. Any of them may be absent. |
source
|
object | Where it came from: `author`, `name`, `url`. Absent keys mean nobody recorded one. |
links
|
array | Links the owner attached to the recipe. |
prepSteps
|
array of step | Only on a single recipe, never in a listing. |
steps
|
array of step | The method. Only on a single recipe. |
coverImage
|
image, or absent | Only when the recipe has one. |
images
|
array of image | Only on a single recipe. |
createdAt
|
string | ISO 8601, with an offset. |
updatedAt
|
string | ISO 8601, with an offset. |
An ingredient line
Every ingredient carries both what was written and what the parser made of it. The parsed half is why this is worth calling rather than scraping — it is what makes "does this use pork" answerable without reading English.
| Field | Type | Notes |
|---|---|---|
name
|
string | The line exactly as the cook wrote it: "2 tbsp olive oil". |
parsed
|
object | The parser's reading of that line. Never authoritative over `name`. |
parsed.ingredient
|
string, or null | The ingredient in its display form: "Olive oil". |
parsed.ingredient_key
|
string, or null | Its canonical identity: "oil". Two lines sharing a key mean the same thing. |
parsed.ingredient_id
|
integer, or null | The catalogue row, where one is known. |
parsed.written
|
string, or null | The words the line used for the ingredient, before generalising: "sesame oil". |
parsed.quantity
|
number, string, or null | A whole number comes back as a number; anything else keeps its text ("2–3"). |
parsed.quantity_min
|
number, or null | The near end of a range. Equal to `quantity_max` when it is not a range. |
parsed.quantity_max
|
number, or null | The far end of a range. |
parsed.unit
|
string, or null | Normalised: "tbsp", "g", "ml". |
parsed.size
|
string, or null | "large", "medium" — the word that sizes the thing rather than counting it. |
parsed.additional
|
string, or null | What came after the ingredient: "finely chopped". |
parsed.to_taste
|
boolean | True for "salt, to taste" and its kin. |
parsed.confidence
|
string | `high`, `medium` or `low`. Treat anything below high as a guess. |
parsed.raw
|
string | The line the parser was given, unchanged. |
parsed.locale
|
string | The language it was read in. |
A step
| Field | Type | Notes |
|---|---|---|
id
|
string | Opaque identifier. |
sort
|
integer | Position within its own section, from 1. |
content
|
string | The instruction. |
An image
| Field | Type | Notes |
|---|---|---|
id
|
string | Opaque identifier. |
width
|
integer | Of the stored original, in pixels. |
height
|
integer | Of the stored original, in pixels. |
links
|
object | One URL per size: `thumbnail`, `strip`, `small`, `medium`, `large`, plus `download`. Each is an application URL, authorised per request — not a storage URL, and never public because you hold it. |