Skip to content

Documentation

shallot-export/v1

This is the whole format. It is published so you can check it rather than take our word for it — read every field, write your own importer, and see for yourself that nothing is withheld. The free converter produces files in it today. In-app export is still being built, and this spec is published ahead of it deliberately: a format you can audit before you commit anything to it is worth more than one described after the fact.

What it looks like

One JSON object. No archive, no compression, no encryption. Open it in a text editor and you can read every recipe you own.

{
  "format": "shallot-export/v1",
  "exportedAt": "2026-07-25T14:32:00.000Z",
  "source": { "app": "paprika", "recipeCount": 184 },
  "recipes": [
    {
      "title": "Gochujang butter noodles",
      "ingredients": [
        "200g noodles",
        "2 tbsp gochujang",
        "30g butter"
      ],
      "instructions": [
        "Boil the noodles.",
        "Melt the butter with the gochujang.",
        "Toss together."
      ],
      "notes": "Double the gochujang.",
      "yield": "2 servings",
      "prepTime": "5 min",
      "cookTime": "15 min",
      "totalTime": "20 min",
      "sourceUrl": "https://instagram.com/p/abc",
      "sourceName": "hanabakes",
      "categories": ["Dinner", "Quick"],
      "nutrition": "540 kcal",
      "imageUrls": ["https://example.com/noodles.jpg"]
    }
  ]
}

Recipe fields

A ? means the field is omitted when there is nothing to put in it, rather than set to null or an empty string.

FieldTypeNotes
titlestringRequired. The only field we will not invent — an untitled recipe is reported, never renamed.
ingredientsstring[]One entry per line, source order preserved. Section headers are kept as their own entry.
instructionsstring[]One entry per step, source order preserved.
notesstring?Your own notes, as written.
yieldstring?Free text, e.g. "4 servings", "Makes 12". Never normalised into a number.
prepTimestring?Free text, as the source recorded it.
cookTimestring?Free text.
totalTimestring?Free text.
sourceUrlstring?Where the recipe came from originally.
sourceNamestring?Author or site name, if the export recorded one.
categoriesstring[]?Tags or categories from the source app.
nutritionstring?Free text. Not parsed or verified.
imageUrlsstring[]?Remote image URLs only. Embedded photo data is not carried across — see below.

What is deliberately not in here

  • Embedded photo data. It would make the file hundreds of times larger than the recipes. Image URLs are kept.
  • Anything only we can read. No internal identifiers you would need our software to resolve.
  • Normalisation you did not ask for. Times and yields come out as the words they went in as. We do not rewrite “a good glug” into a number.

Questions people actually ask

Can I write my own importer for this format?
Yes, and you should be able to do it in an afternoon. The format is plain JSON with a flat recipe shape. It is not compressed, not encrypted, and carries no fields that only Shallot can interpret.
Can I export from Shallot today?
Not yet — in-app export is still being built, and this specification is published ahead of it rather than describing something that already ships. The free converter at /tools/import-from produces files in this format today, with no account. When in-app export lands it will produce the same format.
Why are photos not included?
Embedded photo data would make the file enormous — often hundreds of times larger than the recipes themselves. Links to images hosted elsewhere are kept, so nothing about where a photo lives is lost.
Will this format change?
The version string exists so it can. Any breaking change gets a new version, and shallot-export/v1 files will keep being readable.

Coming from another app?

The free converter turns a Paprika, Mela, JSON or CSV export into this format. No account needed, and it works whether or not you ever use Shallot.

This spec is the checkable half of a larger promise. Your recipes are yours is the readable half — what that means in practice, and what we are not claiming.