Deliver product data
We only recommend products whose ingredients and dosage we know. For that we fetch your product data ourselves on a schedule. You never have to report changes to us.
What to do
- Send us a URL: a product feed, a sitemap or your shop domain. Which variant fits your shop system is listed under use an existing format. Done when the URL is publicly reachable over HTTPS, or you have given us a user and password for access.
- Make sure the required fields are present: article number, name, price as a number, availability, product description as HTML. Done when the self-test below prints nothing.
- Check that ingredients and nutrients are a real table in the description, not an image. Done when the table has a header row with an amount column, see description, nutrients, dosage.
JSON feed following our schema
The most precise path, and the one with the fewest follow-up questions. One HTTPS endpoint returns all products in a single document.
product_numbervariants[].sku, pattern ^[A-Za-z0-9-]+$Fields per product
| Field | Type | Required | Meaning |
|---|---|---|---|
product_number | String, non-empty | yes | Article number, our key for the product. Must stay stable across feed generations. |
title | String, non-empty | yes | Display name. |
health_claims.status | Fixed values, see below | yes | Controls whether the product may appear in the advice. |
variants | Array, min. 1 | yes | Even a product without variants has exactly one entry. |
body_html | String with HTML | yes | Description as on the detail page, with ingredients, nutrient table and intake recommendation. |
url | Absolute HTTPS URL | yes | Product detail page on the domain you told us. |
image | HTTPS URL | recommended | One cover image. The images list alone is not enough. |
darreichungsform | String | recommended | Dosage form: Kapseln, Tabletten, Pulver, Tropfen, Spray, Sachets, Set, Testkit. |
images, tags, vendor | Array, array, string | optional | Further images, categories and brand name. |
Fields per variant
| Field | Type | Required | Meaning |
|---|---|---|---|
sku | String, only A-Z a-z 0-9 and hyphen | yes | Unique per variant. We send this value back in the cart link. |
price | Number, dot as decimal separator, gross | yes | 24.9, not "24,90" and not "24.90". |
available | Boolean | yes | true or false, not "true" and not 1. |
title | String | recommended | Size label, for example 120 Kapseln or 500 g. |
servings_per_container | Number | recommended | Servings per pack. Without this field we read the number from the product title, which fails for powders. |
serving_size, net_quantity, gtin | String, object, string | optional | Serving size, fill quantity and EAN. They make the price-per-serving comparison more precise. |
id | String | optional | Internal ID of your shop, if you have one. |
Values for health_claims.status
| Value | Meaning | Consequence |
|---|---|---|
done | Supplement, health claims reviewed | can be recommended |
keine_claims | Supplement without health claims | can be recommended |
bundle | Set of several products | in the catalogue, handled separately |
kein_nem | Not a supplement: test kits, accessories, cosmetics | stays out of the advice |
Any other value, and a missing field, is treated like kein_nem. So set the status
even when it is keine_claims. Test kits and accessories may stay in the feed, they are
then excluded from the advice.
Running the feed
- HTTPS and publicly reachable. Basic auth is possible, then send us user and password.
- One document with all products, no pagination.
- Response in under 20 seconds. For large catalogues serve a pre-generated file.
generated_atas an ISO 8601 timestamp so we can see how fresh the data is.product_numberandskumust not change on every generation.
Four rules that lose products
- No
product_numberor notitleThe product is discarded. - No variant with a positive
priceThe product is discarded. Deliver the price as a JSON number; as a safety net we also accept strings of the forms"3.95"and"3,95". Anything else (currency signs, thousands separators,0) counts as no price. This is the most common reason a feed looks formally fine and zero products arrive on our side. - Unknown
health_claims.statusThe product sits in the catalogue but is invisible in the advice. - Dot, underscore, space or slash in the
skuThe product can be recommended but drops out of the cart link, without an error message. The customer then sees an incomplete cart.
If url is not on the domain you told us, we discard the product link.
Before the start that means: no test-shop domains in the feed.
Description, nutrients, dosage
Applies to every path. From the HTML description we read ingredients and dosage, the basis of every recommendation.
- Real
<table>markup with a header row and at least two columns. Tables as an image or as adivgrid are unreadable for us. - First column: nutrient name. Sub-rows start with
davonor a hyphen. - Amount column: the header cell must contain one of the words
Tagesdosis,Mengeorje. Recognised are for examplepro Tagesdosis (3 Kapseln),je Portion (10 g)andMenge pro Kapsel. Aje 100 gcolumn is skipped because we need the per-serving basis. - NRV column, optional: header cell contains
NRVor% RM. - Intake recommendation under a subheading
<h3>Verzehrempfehlung</h3>, after the table.
<table>
<tr><th>Nährstoff</th><th>pro Tagesdosis (3 Kapseln)</th><th>% NRV</th></tr>
<tr><td>Magnesium</td><td>300 mg</td><td>80</td></tr>
<tr><td>davon aus Magnesiumcitrat</td><td>1875 mg</td><td>-</td></tr>
</table>
<h3>Verzehrempfehlung</h3>
<p>3 Kapseln täglich mit reichlich Wasser zu einer Mahlzeit.</p>
The keywords above are German because the catalogues we read are German. If your shop publishes in another language, tell us and we add the vocabulary.
If you can emit ingredients and nutrients structured, so name, amount, unit and percent NRV per row, we prefer that over HTML. Then table parsing drops out.
Use an existing format
Four paths where your shop builds nothing new.
Shopify
We read the public products.json. From you we only need the shop domain. The cart
runs over cart permalinks, the coupon over /discount/<code>.
WooCommerce, feed in Google Merchant format
If your shop already generates a feed for Google Merchant Center, we read that same feed. These are the fields we evaluate:
| Field | Required | Note |
|---|---|---|
g:id | yes | Product ID, digits only. The same value must work in the cart link. |
g:title | yes | Display name. |
g:price | yes | Exactly amount currency, e.g. 24.90 EUR: dot as decimal separator, one space, ISO currency code. Any other format drops the product. |
g:description | yes | Description as HTML, with nutrient table and intake recommendation. |
g:link | yes | Product page on the live domain. |
g:availability | yes | in_stock, out_of_stock, preorder or backorder. |
g:image_link | recommended | Cover image, more via g:additional_image_link. |
g:product_type | recommended | Category path with >. The last level becomes our category and decides whether an item counts as a supplement. |
g:sale_price, g:gtin, g:mpn, g:brand | optional | The sale price is used when set. The rest improve product matching. |
JSON-LD on product pages
No feed needed: we follow your sitemap and read the schema.org JSON-LD on each
product page (Product or DietarySupplement) with sku,
price and availability, plus the ingredient and nutrient table from the
page HTML. Requirement: the JSON-LD is on every product page and carries the article number.
PrestaShop JSON
We read id_product, name, reference,
price_current (falling back to price or price_default),
description_short, description_webshop_full, images and
link_rewrite for the product link.
Something else
Send us a sample of your export: a file, a URL or a list of the fields. We report whether we can read it directly or what we need.
Check it yourself
For the JSON feed. Every output line is a product that will not arrive, or will arrive incomplete.
npx --yes -p ajv-cli -p ajv-formats ajv validate --spec=draft2020 -c ajv-formats \
-s json-feed.schema.json -d feed.json --errors=text --all-errors
pipx run check-jsonschema --schemafile json-feed.schema.json feed.json
Without installing anything, with jq only:
jq -r '.products[] | select((.product_number|type) != "string" or (.product_number|length) == 0
or (.title|type) != "string" or (.title|length) == 0)
| "DISCARDED (product_number/title): \(.title // "?")"' feed.json
jq -r '.products[] | select([.variants[]?.price | select(type == "number")] | length == 0)
| "DISCARDED (no numeric price): \(.product_number)"' feed.json
jq -r '.products[] | .health_claims.status as $s
| select((["done","keine_claims","bundle","kein_nem"] | index($s)) == null)
| "INVISIBLE (health_claims.status): \(.product_number) = \($s|tostring)"' feed.json
jq -r '.products[] | .product_number as $p | .variants[]?
| select((.sku // "") | test("^[A-Za-z0-9-]+$") | not)
| "CART LOSS (sku): \($p) = \(.sku|tostring)"' feed.json
No output means the feed passes completely. Send us the URL and we check against it and report the product count and anything that stands out.
Next: step 2, hand over the cart.
