Usage

lebensmittel — a CLI for the official lebensmittelwarnung.de product-warning RSS feeds. This is the use-case-driven cookbook; for the option reference see the README, and for domain terms the Glossary. No API key is required.

lebensmittel [global options] <command>

Global options

Option Description
--base-url <url> API base URL (only http:/https: accepted)
--timeout <ms> time limit per request in ms, whole response included (0 = no timeout; at most 2147483647)
--user-agent <ua> User-Agent header value
--max-retries <n> retries for transient 429/503 responses (0..10)
--max-response-bytes <n> cap the response body size in bytes (0 = unlimited; default 100 MiB)
--compact print JSON on a single line (for piping to jq)
-o, --output <file> write output to a file instead of stdout
-V, --version / -h, --help version / help

warnings — current product recalls

The heart of the tool: the portal’s currently active warnings, each with its reason, manufacturer, affected Länder, lot numbers and best-before dates.

lebensmittel warnings
[
  {
    "title": "ja! Beerenmischung, tiefgefroren, 750 Gramm Beutel",
    "link": "https://www.lebensmittelwarnung.de/.../Meldung.html",
    "pubDate": "Wed, 8 Jul 2026 16:00:00 +0200",
    "published": "2026-07-08T14:00:00.000Z",
    "reason": "Krankheitserreger",
    "manufacturer": "Eurogroup España Frutas y Verduras S.A.U., …",
    "affectedStates": ["Nordrhein-Westfalen", "Bayern", "…"],
    "lotNumbers": "L-26085",
    "bestBefore": "Mindesthaltbarkeitsdatum: 15.03.2028",
    "packaging": "750 Gramm",
    "imageUrls": ["https://www.lebensmittelwarnung.de/.../Bild.jpg?__blob=normal&v=1"],
    "fields": { "…": "…" },
    "rawDescription": "<img …/><br/><b>Grund der Meldung:</b> …"
  }
]
# One line per recall: date (German time, from pubDate), product, reason
lebensmittel warnings | jq -r '.[] | "\(.pubDate | split(" ")[1:4] | join(" "))\t\(.title)\t\(.reason // "?")"'

# How many warnings does the portal list? (not only recent ones: the feed goes back years)
lebensmittel warnings | jq length

# Every product-photo URL
lebensmittel warnings | jq -r '.[].imageUrls[]?'

Narrow by federal state

lebensmittel warnings --state bayern
lebensmittel warnings --state nordrheinwestfalen | jq length

--state selects the warnings distributed in that Land (server-side): the ones whose affectedStates list names it, whoever issued them. Run lebensmittel states for the sixteen valid slugs; an unknown slug is a usage error (exit 2), never a silent full-feed fallback.

The feed has no field for the issuing Land. The notice URL carries it as a Land code in the folder name (…/260904_03_BW_diverse_Kaesesorten/…, BVL for the federal office), a naming convention rather than data:

lebensmittel warnings | jq -r '.[] | select(.link | test("/\\d{6,8}(_\\d+)?_BY_")) | .title'

Narrow by product type

lebensmittel warnings --type lebensmittel          # food only
lebensmittel warnings --type kosmetischemittel     # cosmetics
lebensmittel warnings --type babyundkinderprodukte # baby & kids products

Run lebensmittel types for the five valid slugs. --state and --type compose:

lebensmittel warnings --state bayern --type lebensmittel | jq length

Narrow by date, title, and count (client-side)

# Only recalls published on or after a date
lebensmittel warnings --since 2026-07-01

# Only recalls whose product name contains a term (case-insensitive)
lebensmittel warnings --search schokolade | jq -r '.[].title'

# The 5 most recent
lebensmittel warnings --limit 5

# Combine everything
lebensmittel warnings --type lebensmittel --since 2026-07-01 --search bio --limit 10

--since takes a YYYY-MM-DD date and keeps warnings whose publication day, in German time (Europe/Berlin), is that day or later. A malformed date (2026-13-40, 10.07.2026) is a usage error (exit 2).

published is the same instant in UTC, so its first ten characters give the day before for notices published in the first one or two hours after midnight German time (many are stamped 00:00:00 +0200). For a date to show, take it from pubDate, which is in German time: .pubDate | split(" ")[1:4] | join(" ") gives 4 Sep 2026. --search/--since that match nothing return [] (not the full feed).

states — the valid Bundesland slugs

lebensmittel states
[
  { "slug": "badenwuerttemberg", "name": "Baden-Württemberg" },
  { "slug": "bayern", "name": "Bayern" }
]

Offline (no request). Use it to discover the exact --state slugs:

lebensmittel states | jq -r '.[].slug'

types — the valid product-type slugs

lebensmittel types
[
  { "slug": "lebensmittel", "name": "Lebensmittel" },
  { "slug": "kosmetischemittel", "name": "Kosmetische Mittel" }
]

Offline (no request).

Recipes

# Reasons across all listed warnings, counted (a warning can carry several, joined with ", ")
lebensmittel warnings | jq -r '[.[] | (.reason // "?") | split(", ")[]] | group_by(.)[] | "\(.[0]): \(length)"'

# A daily "anything new in Bavaria?" check (exit 0 with rows, or empty)
lebensmittel warnings --state bayern --since "$(date -v-1d +%F 2>/dev/null || date -d yesterday +%F)" \
  | jq -r '.[] | "\(.title) — \(.reason)"'

# Look up a specific batch across all recalls
lebensmittel warnings | jq -r --arg lot "L-26085" \
  '.[] | select(.lotNumbers // "" | test($lot)) | .title'

# Save today's full snapshot to a file
lebensmittel warnings -o warnings-$(date +%F).json

Exit codes

Code Meaning
0 Success (also --help / --version)
2 Bad usage / invalid argument (unknown --state/--type, bad --since/--limit)
4 Not found (404)
6 Network / transport failure (DNS, connection, timeout, size cap)
1 Any other error — non-RSS HTML shell, empty body, or a 3xx redirect

Notes

View the source on GitHub →