Install
openclaw skills install @maikimolto/mealie-recipe-importImport recipes into a self-hosted Mealie instance from a photo, text, or URL, with AI ingredient parsing and cover image upload.
openclaw skills install @maikimolto/mealie-recipe-importAdd a recipe to a self-hosted Mealie instance (v1 API, app v3.x) from a photo, plain text, or a URL. Handles AI ingredient parsing, food/unit dedup + creation, recipe metadata, and cover-image upload. Verified against Mealie v3.21.0.
pip install needed.nlp/brute parser is used.Two env vars (never hardcode instance/token):
MEALIE_URL — base URL, e.g. http://mealie.example.lan:9925MEALIE_API_KEY — long-lived Mealie API tokenGet a token: Mealie UI -> user profile -> "Manage Your API Tokens" -> create; or POST /api/users/api-tokens {"name":"agent"}. Store it in your agent's .env, never in the skill.
Verify access:
curl -s -H "Authorization: Bearer $MEALIE_API_KEY" "$MEALIE_URL/api/users/self" # 200 + user json
curl -s "$MEALIE_URL/api/app/about" # version
POST /api/recipes/create/url {"url":..,"includeTags":false} returns the slug (URL import parses ingredients weakly, so still run the parser in step 3).POST /api/recipes {"name":"<Title>"} returns the slug as a bare quoted JSON string.POST /api/parser/ingredients {"parser":"openai","ingredients":[...]}. Each result item has .ingredient = {quantity, unit|null, food|null, note, originalText, referenceId} plus .confidence.average. The AI parser auto-resolves existing foods/units to their DB ids.GET /api/foods?perPage=2000 and GET /api/units?perPage=2000. For any parsed food/unit that has no id: match by lowercased/trimmed name; if still missing POST /api/foods {"name":..} / POST /api/units {"name":..} and use the returned object. Prevents duplicate foods.GET /api/recipes/{slug} for the full object, then set recipeIngredient (array), recipeInstructions ([{title:"",text,ingredientReferences:[]}]), recipeServings (number), recipeYield (string), totalTime/prepTime (strings), then PUT /api/recipes/{slug} with the modified object.PUT /api/recipes/{slug}/image as multipart form image=@<file>;type=image/jpeg + extension=jpg -> {"image":"<n>"}. If you have both a cookbook-page scan and a finished-dish photo, use the finished dish as the cover.GET /api/recipes/{slug} (check name, servings, ingredient/instruction counts, image id) and GET /api/media/recipes/{recipe_id}/images/original.webp -> expect HTTP 200.The bundled scripts/mealie_import.py performs steps 2 + 4-7 (and step 3 if you pass raw ingredient lines) from a JSON job file.
{
"quantity": 250.0,
"unit": { "id": "...", "name": "Gramm" },
"food": { "id": "...", "name": "Tortellini" },
"note": "dry",
"isFood": true,
"disableAmount": false,
"originalText": "250 g dry tortellini",
"referenceId": "<uuid>"
}
parser:"openai" selects whichever AI provider is configured in Mealie admin (OpenAI or Gemini). If no AI parser is configured, fall back to "parser":"nlp" or "brute", or keep lines as plain notes (food/unit null, disableAmount:true).disableAmount:true when an ingredient has no quantity and no unit (e.g. Salt, Pepper) so it renders as just the name.POST /api/recipes returns the slug as a bare quoted JSON string, not an object./api/recipes/create/url) stores ingredients as weakly-parsed raw text -> re-run the parser and PUT.export MEALIE_URL=http://mealie.example.lan:9925
export MEALIE_API_KEY=****
# After transcribing the photo, write a job file:
cat > job.json <<'JSON'
{
"title": "Party Tortellini Salad",
"servings": 4,
"yield": "4 servings",
"total_time": "40 min",
"instructions": [
"Cook tortellini per package. Dice peppers, wedge tomatoes, strip the ham. Drain and cool.",
"Whisk yogurt with sour cream, balsamic and chives. Season, fold everything in, rest 10 min, serve."
],
"ingredient_lines": [
"250 g dry tortellini", "Salt", "1 yellow bell pepper", "1 green bell pepper",
"4 tomatoes", "6 slices cooked ham", "250 g low-fat yogurt", "2 tbsp sour cream",
"1 tbsp balsamic vinegar", "3 tbsp chives", "Pepper"
],
"parser": "openai",
"image_path": "/path/to/finished-dish.jpg"
}
JSON
python3 scripts/mealie_import.py job.json