A separate package, separate keys. The news API and the MCP have their own documentation and their own keys.
Radar API documentation
Every public Adios Radar notice as data: recalls and safety alerts (HAPIH, EU RASFF, Safety Gate), products with EAN/GTIN codes, batches and dates, companies and stores, a durable change feed, publishers' recall articles and signed webhooks.
| Base URL | https://adios.hr/api/v1/radar |
| Key | A Radar key adios_radar_… · more |
| Format | JSON (UTF-8), snake_case names, UTC times (ISO 8601) |
| OpenAPI | /api/v1/openapi.json (Radar endpoints carry x-adios-package: radar) |
| Package | 7 days free, then €19 a month. Unlimited requests, the whole archive. |
| Archive | 870 public notices · 4 sources · 93 stores |
Quickstart
- Start the trial
Sign in and open the Radar console. Your first Radar key comes with the 7-day trial; it is shown only once. - Call the API
export ADIOS_RADAR_KEY="adios_radar_…" curl "https://adios.hr/api/v1/radar/notices?limit=5&lang=en" -H "Authorization: Bearer $ADIOS_RADAR_KEY"
- Read data
Every answer is {"data": …, "meta": …}. A notice comes with every field at once, no extra calls.
Try it without a key: in the Radar console you pick an endpoint, filters from the catalogue and click Send. It runs as your account, and you see the answer as cards with every field or as the whole JSON.
Radar keys
Radar has keys of its own. Send the key in a header, either way:
Authorization: Bearer adios_radar_AbC123… X-Api-Key: adios_radar_AbC123…
- A Radar key is "adios_radar_" and 40 letters and digits. It works only for /api/v1/radar.
- Keys are not shared: an API key (adios_…) on Radar returns 403 radar_key_required, and a Radar key on the news API or the MCP returns 403 api_key_required.
- Up to 10 active Radar keys per account, in the Radar console. We keep only a hash (SHA-256): revoke a lost key and make a new one. A revoked key stops working at once.
- Don't put a key in code that runs in a browser or a mobile app: call Radar from your server.
Package and requests
| State | What works |
|---|---|
| trial | 7 days from starting it in the console: everything, unlimited. One trial per user. |
| active | Paid access (€19 a month, arranged by e-mail, no automatic charge): everything, unlimited. |
| expired | Calls return 403 radar_expired, deliveries pause and wait. Only subscription, the webhook list, deliveries, pausing and deleting work. |
| suspended | As expired, with 403 radar_suspended. |
- Radar uses none of the news plan's calls and has no per-minute or daily limit, so there are no X-RateLimit headers.
- Lists return up to 100 items a page; the export up to 5,000 notices in one answer. The archive has no age limit.
- Every call is in the console's log (the console's own calls too, as the console channel).
Responses
{
"data": [ … ],
"meta": {
"count": 25,
"has_more": true,
"next_cursor": "eyJ0aW1l…", # send it as cursor
"snapshot_at": "2026-10-14T09:30:00Z",
"sync_cursor": "1842", # changes?after= after an import
"package": "radar",
"took_ms": 14
}
}- data is a list or an object. Empty fields are left out; an empty list means the source didn't publish it.
- Paging is by cursor: while has_more=true, send next_cursor as cursor with exactly the same filters and limit. The cursor keeps snapshot_at, so new notices don't shift the pages.
- Filters combine (AND). from is inclusive, to exclusive; a date (2026-10-01) or an ISO 8601 time.
Errors
{ "error": { "code": "radar_key_required", "message": "Radar takes a Radar key (adios_radar_…), not an API key. …", "docs": "https://adios.hr/developers/radar/docs#errors" } }| HTTP | code | Meaning |
|---|---|---|
| 400 | invalid_parameter, missing_parameter, invalid_body, invalid_url | A parameter, cursor, body or HTTPS address is wrong; the message says which. |
| 401 | key_required, invalid_key | No key, or a wrong or revoked one. |
| 403 | radar_key_required, api_key_required | A key of the other package: Radar takes a Radar key, the news API an API key. |
| 403 | radar_not_started, radar_expired, radar_suspended, account_suspended | Check the package in the console. |
| 404 | not_found, unknown_endpoint | No such public notice or article, the webhook isn't yours, or no such endpoint. |
| 429 | too_many_wrong_keys | Too many wrong keys from one address; wait a minute. |
| 500 / 503 | internal_error, unavailable | A passing problem; try again shortly. |
Notices
Notices
Radar keyPublic notices with every field: text and translations, products, EAN/GTIN, batches, dates, companies, stores, images, source and links. The whole archive; filters combine (AND). Up to 100 a page: the next one by offset (offset=100) or, for syncing, meta.next_cursor as cursor with the same filters.
| Parameter | Type | Description |
|---|---|---|
| lang | string |
Language of the main text (default hr). Translations are always in translations[].
hr · en |
| limit | integer |
Items per page, 1–100 (default 25).
e.g. 25 |
| offset | integer |
Skips the first N items, 0–10,000 (default 0): a page without a cursor (offset=100 with limit=100 is the second hundred). Ignored with cursor.
e.g. 100 |
| tracker | string |
A tracker key from the catalogue (trackers[].key).
e.g. product-recalls |
| source | string |
A source key from the catalogue (sources[].key).
e.g. hapih-obavijesti |
| category | string |
A category key from the catalogue (categories[].key).
e.g. food |
| hazard | string |
A hazard key from the catalogue (hazards[].key).
e.g. microbial |
| store | string |
A store slug from the catalogue (stores[].slug).
e.g. lidl |
| gtin | string |
An exact EAN/GTIN; leading zeros are kept.
e.g. 3830000000000 |
| lot | string |
An exact batch (LOT). With gtin, the same product.
e.g. L2410 |
| brand | string |
A brand, any letter case.
e.g. Primjer |
| country | string |
Country of origin, ISO 3166-1 alpha-2.
e.g. HR |
| risk | string |
Risk level.
serious · high · medium · low · unknown |
| notice_type | string |
Notice type.
recall · withdrawal · warning · update · cancellation · other |
| children | boolean |
Children's products only (categories with concerns_children).
e.g. true |
| q | string |
Text in the notice's title and text (up to 200 characters).
e.g. salmonela |
| from | datetime |
Published since, inclusive (UTC, ISO 8601).
e.g. 2026-01-01T00:00:00Z |
| to | datetime |
Published before, exclusive (UTC).
e.g. 2026-10-01T00:00:00Z |
| updated_since | datetime |
Updated since, inclusive (UTC).
e.g. 2026-10-01T00:00:00Z |
| sort | string |
Order (default newest).
newest · oldest · updated |
| cursor | string | The previous page's meta.next_cursor; same filters and limit. |
curl "https://adios.hr/api/v1/radar/notices?lang=en&limit=5" \ -H "Authorization: Bearer $ADIOS_RADAR_KEY"
One notice
Radar keyEvery public field of one notice, with links to its earlier notice, updates and cancellation.
| Parameter | Type | Description |
|---|---|---|
| id required | string · path | The notice's, article's or webhook's UUID. |
| lang | string |
Language of the main text (default hr). Translations are always in translations[].
hr · en |
curl "https://adios.hr/api/v1/radar/notices/{id}" \
-H "Authorization: Bearer $ADIOS_RADAR_KEY"Export the whole archive
Radar keyEvery public notice matching the filters, with every field, in one answer (up to 5000), newest first. For a first import; then follow changes from meta.sync_cursor.
| Parameter | Type | Description |
|---|---|---|
| lang | string |
Language of the main text (default hr). Translations are always in translations[].
hr · en |
| tracker | string |
A tracker key from the catalogue (trackers[].key).
e.g. product-recalls |
| source | string |
A source key from the catalogue (sources[].key).
e.g. hapih-obavijesti |
| category | string |
A category key from the catalogue (categories[].key).
e.g. food |
| hazard | string |
A hazard key from the catalogue (hazards[].key).
e.g. microbial |
| store | string |
A store slug from the catalogue (stores[].slug).
e.g. lidl |
| gtin | string |
An exact EAN/GTIN; leading zeros are kept.
e.g. 3830000000000 |
| lot | string |
An exact batch (LOT). With gtin, the same product.
e.g. L2410 |
| brand | string |
A brand, any letter case.
e.g. Primjer |
| country | string |
Country of origin, ISO 3166-1 alpha-2.
e.g. HR |
| risk | string |
Risk level.
serious · high · medium · low · unknown |
| notice_type | string |
Notice type.
recall · withdrawal · warning · update · cancellation · other |
| children | boolean |
Children's products only (categories with concerns_children).
e.g. true |
| q | string |
Text in the notice's title and text (up to 200 characters).
e.g. salmonela |
| from | datetime |
Published since, inclusive (UTC, ISO 8601).
e.g. 2026-01-01T00:00:00Z |
| to | datetime |
Published before, exclusive (UTC).
e.g. 2026-10-01T00:00:00Z |
| updated_since | datetime |
Updated since, inclusive (UTC).
e.g. 2026-10-01T00:00:00Z |
curl "https://adios.hr/api/v1/radar/export?lang=en" \ -H "Authorization: Bearer $ADIOS_RADAR_KEY"
Recall articles
Recall articles
Radar keyCroatian publishers' articles about recalls (the theme Povlačenje proizvoda), newest first, the whole archive. Headline, description, Adios address and source; text stays in its original language. One result per story.
| Parameter | Type | Description |
|---|---|---|
| limit | integer |
Items per page, 1–100 (default 25).
e.g. 25 |
| offset | integer |
Skips the first N items, 0–10,000 (default 0): a page without a cursor (offset=100 with limit=100 is the second hundred). Ignored with cursor.
e.g. 100 |
| provider | string |
A news source's slug.
e.g. index |
| q | string |
Text in the headline or description.
e.g. salmonela |
| from | datetime |
Published since, inclusive (UTC).
e.g. 2026-01-01T00:00:00Z |
| to | datetime |
Published before, exclusive (UTC).
e.g. 2026-10-01T00:00:00Z |
| cursor | string | The previous page's meta.next_cursor; same filters and limit. |
curl "https://adios.hr/api/v1/radar/articles?limit=5" \ -H "Authorization: Bearer $ADIOS_RADAR_KEY"
One article
Radar keyThe public data of one recall article.
| Parameter | Type | Description |
|---|---|---|
| id required | string · path | The notice's, article's or webhook's UUID. |
curl "https://adios.hr/api/v1/radar/articles/{id}" \
-H "Authorization: Bearer $ADIOS_RADAR_KEY"Catalogue and changes
Catalogue
Radar keyEverything a filter or a field can be: trackers, sources (with their last check), categories, hazards and stores with their public notice counts, and every fixed value (enums) with its meaning: risk, notice type, company role, change type, webhook events, image kinds, languages and orders.
| Parameter | Type | Description |
|---|---|---|
| lang | string |
Language of the main text (default hr). Translations are always in translations[].
hr · en |
curl "https://adios.hr/api/v1/radar/catalogue?lang=en" \ -H "Authorization: Bearer $ADIOS_RADAR_KEY"
Changes
Radar keyA durable feed of publications, updates, cancellations and removals, oldest first. after=latest gives a starting point, after=0 reads from the start of the feed. Each change carries the notice's current public state (not a historical copy).
| Parameter | Type | Description |
|---|---|---|
| lang | string |
Language of the main text (default hr). Translations are always in translations[].
hr · en |
| limit | integer |
Items per page, 1–100 (default 25).
e.g. 25 |
| after | string |
The last processed change's ID, or latest (default 0).
e.g. 0 |
curl "https://adios.hr/api/v1/radar/changes?after=0&limit=20" \ -H "Authorization: Bearer $ADIOS_RADAR_KEY"
Package
My Radar package
Radar keyThe package's state (trial, active, expired, suspended), the trial's end and paid access. Works after expiry too.
curl "https://adios.hr/api/v1/radar/subscription" \ -H "Authorization: Bearer $ADIOS_RADAR_KEY"
Webhook calls
My Radar webhooks
Radar keyThe account's Radar webhooks, without secrets.
curl "https://adios.hr/api/v1/radar/webhooks" \ -H "Authorization: Bearer $ADIOS_RADAR_KEY"
Create a webhook
Radar keyAn HTTPS webhook for notice changes (radar.change, default) or articles (radar.article). Filters are optional: tracker, category, hazard, store and gtin for notices, provider for articles. The signing secret comes back only here.
| Parameter | Type | Description |
|---|---|---|
| url required | string · body |
A public HTTPS URL.
e.g. https://example.com/radar |
| name | string · body |
Name.
e.g. My shop |
| event | string · body |
Subscription.
radar.change · radar.article |
| lang | string · body |
Language of the main text (default hr). Translations are always in translations[].
hr · en |
| tracker | string · body |
A tracker key from the catalogue (trackers[].key).
e.g. product-recalls |
| category | string · body |
A category key from the catalogue (categories[].key).
e.g. food |
| hazard | string · body |
A hazard key from the catalogue (hazards[].key).
e.g. microbial |
| store | string · body |
A store slug from the catalogue (stores[].slug).
e.g. lidl |
| gtin | string · body |
An exact EAN/GTIN; leading zeros are kept.
e.g. 3830000000000 |
| provider | string · body |
A news source's slug, radar.article only.
e.g. index |
curl -X POST "https://adios.hr/api/v1/radar/webhooks" \
-H "Authorization: Bearer $ADIOS_RADAR_KEY" \
-H "Content-Type: application/json" \
-d '{"name":"My shop","url":"https://example.com/radar","event":"radar.change","category":"food","lang":"en"}'Test a webhook
Radar keySends a signed POST (test=true) to the saved URL: a sample, a chosen public notice or article, or your own JSON in data. Returns your server's HTTP status, the time, the error and the payload sent. Live deliveries are untouched.
| Parameter | Type | Description |
|---|---|---|
| id required | string · path | The notice's, article's or webhook's UUID. |
| type | string · body |
The test event.
radar.created · radar.updated · radar.cancelled · radar.removed · radar.article |
| notice_id | string · body | A public notice's UUID (radar.change). |
| article_id | string · body | A public article's UUID (radar.article). |
| data | object · body | Your own JSON object (up to 16,000 characters) instead of an ID. |
curl -X POST "https://adios.hr/api/v1/radar/webhooks/{id}/test" \
-H "Authorization: Bearer $ADIOS_RADAR_KEY" \
-H "Content-Type: application/json" \
-d '{"type":"radar.updated","data":{"message":"Hello from Adios Radar"}}'Pause or resume
Radar keyactive=false pauses, active=true resumes from the undelivered change.
| Parameter | Type | Description |
|---|---|---|
| id required | string · path | The notice's, article's or webhook's UUID. |
| active required | boolean · body |
On.
e.g. true |
curl -X POST "https://adios.hr/api/v1/radar/webhooks/{id}/state" \
-H "Authorization: Bearer $ADIOS_RADAR_KEY" \
-H "Content-Type: application/json" \
-d '{"active":false}'Deliveries
Radar keyRecent delivery attempts: event, HTTP status, time and error.
| Parameter | Type | Description |
|---|---|---|
| id required | string · path | The notice's, article's or webhook's UUID. |
| limit | integer |
Items per page, 1–100 (default 25).
e.g. 25 |
curl "https://adios.hr/api/v1/radar/webhooks/{id}/deliveries" \
-H "Authorization: Bearer $ADIOS_RADAR_KEY"Delete a webhook
Radar keyDeletes your own Radar webhook.
| Parameter | Type | Description |
|---|---|---|
| id required | string · path | The notice's, article's or webhook's UUID. |
curl -X DELETE "https://adios.hr/api/v1/radar/webhooks/{id}" \
-H "Authorization: Bearer $ADIOS_RADAR_KEY"Notice: every field
The same shape in notices, notices/{id}, export, changes and webhooks.
| Field | Type | Meaning |
|---|---|---|
| id | uuid | The notice's stable ID. |
| tracker | string | The tracker's key (trackers[].key in the catalogue). |
| language | hr | en | The main text's language. Without the asked translation, it says which one came. |
| title, summary, body | string | The title, a short summary and the whole public text. |
| reason | string? | Why the product was taken off the market. |
| advice | string? | What buyers should do. |
| url | url | The notice on Adios Radar. |
| translations[] | object[] | Every language: language, title, summary, body, reason, advice, url. |
| category, hazard | object? | key, name, slug, concerns_children. The keys are in the catalogue and below, in Values. |
| risk | enum | serious, high, medium, low, unknown. |
| notice_type | enum | recall, withdrawal, warning, update, cancellation, other. |
| cancelled | boolean | The notice was cancelled. Not proof that the product is safe. |
| brand | string? | The brand. |
| origin_country | string? | Country of origin, ISO 3166-1 alpha-2. |
| issued_by | string? | The authority that issued the notice. |
| published_at | datetime | Published by the source (UTC). |
| detected_at | datetime | When Radar found it. An old notice found today has an old published_at and a new detected_at. |
| updated_at | datetime | The latest change on Radar. |
| source_url | url | The original notice. |
| source | object | key, name, publisher, publisher_short_name, publisher_url, language, checking_enabled, check_interval_minutes, last_checked_at, last_success_at. |
| image_url | url? | The main image, at its original address. |
| images[] | object[] | url, alt_text, kind (product, label, document). |
| products[] | object[] | name, brand, variant, gtin, lots[], best_before (as the source writes it), best_before_date (a date, when readable). GTIN and LOT are strings; leading zeros stay. |
| companies[] | object[] | role (producer, importer, distributor, retailer, other), name, address, country_code. |
| stores[] | object[] | Stores: name, slug (the slug is the store filter). |
| earlier_notice_id | uuid? | The earlier notice this one updates or cancels. |
| cancelled_by_id | uuid? | The notice that cancelled this one. |
| update_ids[] | uuid[] | Later updates of this notice. |
| editorial | boolean | Published by Adios's editors, not by a source. |
Example: GET /api/v1/radar/notices?limit=1 (a made-up product)
{
"data": [
{
"id": "6f1c2a4e-8b3d-4c5e-9a7f-1d2e3f4a5b6c",
"tracker": "product-recalls",
"language": "en",
"title": "Recall: Primjer fresh cheese 250 g due to Listeria monocytogenes",
"summary": "Mljekara Primjer recalls its fresh cheese 250 g, batch L26281, due to Listeria monocytogenes.",
"body": "The Croatian Agency for Agriculture and Food informs consumers of the recall of Primjer fresh cheese 250 g …",
"reason": "Listeria monocytogenes was found in the product.",
"advice": "Do not eat the product; return it to the shop where it was bought.",
"url": "https://adios.hr/en/radar/recalls/recall-primjer-fresh-cheese-250-g-due-to-listeria-monocytogenes-6f1c2a4e",
"translations": [
{
"language": "en",
"title": "Recall: Primjer fresh cheese 250 g due to Listeria monocytogenes",
"summary": "Mljekara Primjer recalls its fresh cheese 250 g, batch L26281, due to Listeria monocytogenes.",
"body": "The Croatian Agency for Agriculture and Food informs consumers of the recall of Primjer fresh cheese 250 g …",
"reason": "Listeria monocytogenes was found in the product.",
"advice": "Do not eat the product; return it to the shop where it was bought.",
"url": "https://adios.hr/en/radar/recalls/recall-primjer-fresh-cheese-250-g-due-to-listeria-monocytogenes-6f1c2a4e"
},
{
"language": "hr",
"title": "Opoziv: svježi sir Primjer 250 g zbog Listeria monocytogenes",
"summary": "Mljekara Primjer opoziva svježi sir 250 g, serija L26281, zbog bakterije Listeria monocytogenes.",
"body": "Hrvatska agencija za poljoprivredu i hranu obavještava potrošače o opozivu proizvoda svježi sir Primjer 250 g …",
"reason": "U proizvodu je utvrđena Listeria monocytogenes.",
"advice": "Proizvod ne konzumirati i vratiti ga u prodavaonicu u kojoj je kupljen.",
"url": "https://adios.hr/radar/opozivi/opoziv-svjezi-sir-primjer-250-g-zbog-listeria-monocytogenes-6f1c2a4e"
}
],
"category": {
"key": "food",
"name": "Food",
"slug": "food",
"concerns_children": false
},
"hazard": {
"key": "microbial",
"name": "Microbial contamination",
"slug": "microbial-contamination",
"concerns_children": false
},
"risk": "serious",
"notice_type": "recall",
"cancelled": false,
"brand": "Primjer",
"origin_country": "HR",
"issued_by": "Hrvatska agencija za poljoprivredu i hranu",
"published_at": "2026-10-12T09:30:00Z",
"detected_at": "2026-10-12T09:37:00Z",
"updated_at": "2026-10-12T09:39:00Z",
"source_url": "https://www.hapih.hr/opoziv-svjezi-sir-primjer-250-g/",
"source": {
"key": "hapih-obavijesti",
"name": "HAPIH – obavijesti za potrošače",
"publisher": "Hrvatska agencija za poljoprivredu i hranu",
"publisher_short_name": "HAPIH",
"publisher_url": "https://www.hapih.hr/kategorija/obavijesti-za-potrosace/",
"language": "hr",
"checking_enabled": true,
"check_interval_minutes": 10,
"last_checked_at": "2026-10-14T09:34:00Z",
"last_success_at": "2026-10-14T09:34:00Z"
},
"image_url": "https://www.hapih.hr/wp-content/uploads/2026/10/svjezi-sir-primjer.jpg",
"images": [
{
"url": "https://www.hapih.hr/wp-content/uploads/2026/10/svjezi-sir-primjer.jpg",
"alt_text": "Primjer fresh cheese, front of the pack",
"kind": "product"
}
],
"products": [
{
"name": "Fresh cheese 250 g",
"brand": "Primjer",
"variant": "250 g",
"gtin": "3850000000017",
"lots": [
"L26281",
"L26282"
],
"best_before": "20.10.2026.",
"best_before_date": "2026-10-20"
}
],
"companies": [
{
"role": "producer",
"name": "Mljekara Primjer d.o.o.",
"address": "Primjerska 1, 10000 Zagreb",
"country_code": "HR"
}
],
"stores": [
{
"name": "Trgovina Primjer",
"slug": "trgovina-primjer"
}
],
"update_ids": [],
"editorial": false
}
],
"meta": {
"next_cursor": "eyJ0aW1lIjoiMjAyNi0xMC0xMlQwOTozMDowMFoiLCJpZCI6IjZmMWMyYTRlIn0",
"has_more": true,
"snapshot_at": "2026-10-14T09:30:00Z",
"sync_cursor": "1842",
"count": 1,
"package": "radar",
"took_ms": 14
}
}Recall article: fields
Croatian publishers' articles with the theme Povlačenje proizvoda: news coverage of recalls, apart from the official notices. One result per story.
| Field | Type | Meaning |
|---|---|---|
| id | uuid | The article's ID on Adios. |
| title, description | string | The publisher's headline and description, in the original language (no full text). |
| url | url | The article on Adios. |
| source | object | name, slug and the article's url on the publisher's site. |
| published_at, discovered_at | datetime | Published by the publisher, and when Adios read it. |
| language | string | The article's language. |
| theme, theme_url | string, url | The theme Povlačenje proizvoda and its page. |
{
"id": "77ff2052-07e8-45d9-a5cb-be861cc084c1",
"title": "Inspektorat se oglasio o rajčicama: \u0027Nabavljali su ih sezonski ugostiteljski objekti...\u0027",
"description": "Državni inspektorat potvrdio je da su cherry rajčice iz Italije, u kojima je otkrivena salmonela, nabavljene od sezonskih ugostiteljskih objekata.",
"url": "https://adios.hr/inspektorat-se-oglasio-o-rajcicama-nabavljali-su-ih-sezonski-ugostiteljski-objekti-77ff2052",
"source": {
"name": "24sata",
"slug": "24sata",
"url": "https://www.24sata.hr/news/inspektorat-se-oglasio-o-rajcicama-nabavljali-su-ih-sezonski-ugostiteljski-objekti-1160564"
},
"published_at": "2026-10-12T16:00:00Z",
"discovered_at": "2026-10-12T16:04:00Z",
"language": "hr",
"theme": "Povlačenje proizvoda",
"theme_url": "https://adios.hr/tema/povlacenje-proizvoda"
}Change: fields
| Field | Type | Meaning |
|---|---|---|
| id | string | The change's ID: it grows and may have gaps. Keep it as your cursor. |
| type | enum | radar.created, radar.updated, radar.cancelled, radar.removed. |
| notice_id | uuid | The notice it is about. |
| occurred_at | datetime | When the change happened. |
| notice | object? | The notice's current public state (every field above); left out on radar.removed. |
Catalogue: fields
GET /api/v1/radar/catalogue gives everything a filter or a field can be, with the count of public notices, and every fixed value with its meaning.
| Field | Type | Meaning |
|---|---|---|
| notices, latest_published_at | int, datetime | Public notices in the archive and the latest publication. |
| trackers[] | object[] | key, name, description, url, notices, latest_published_at. |
| sources[] | object[] | key, tracker, name, publisher, publisher_short_name, publisher_url, language, checking_enabled, check_interval_minutes, last_checked_at, last_success_at, notices. |
| categories[], hazards[] | object[] | tracker, key, name, slug, url, concerns_children, notices. |
| stores[] | object[] | name, slug, notices (only stores with public notices). |
| enums | object | Every fixed value: risk, notice_type, company_role, change_type, webhook_event, image_kind, language, sort; each as { value, name, description }. |
Values (enums)
The fixed values of fields and filters. The catalogue has the same ones (enums), with names in the asked language.
Risk level risk
| Value | Name | Meaning |
|---|---|---|
| serious | Serious risk | Pathogenic bacteria (Listeria, Salmonella, STEC), an undeclared major allergen, glass or metal, a toxic substance, choking of children, electric shock or fire. |
| high | High risk | Other contamination with a stated health risk: aflatoxins, ethylene oxide, pesticides over the limit, mould. |
| medium | Medium risk | Unapproved additives or ingredients, limits exceeded without an acute risk. |
| low | Low risk | Labelling or quality only. |
| unknown | Risk not stated | The notice gives no reason. |
Notice type notice_type
| Value | Name | Meaning |
|---|---|---|
| recall | Recall | Consumers are asked to return the product or not to use it. |
| withdrawal | Withdrawal | The product is only taken off the shelves. |
| warning | Warning | A public warning about a product. |
| update | Update | Adds batches or products to an earlier notice, or corrects it (earlier_notice_id). |
| cancellation | Notice withdrawn | An earlier notice is withdrawn, usually because the product is safe. Not a product recall. |
| other | Notice | Anything else, e.g. a border rejection (RASFF). |
Company role companies[].role
| Value | Name | Meaning |
|---|---|---|
| producer | Producer | Makes or packs the product. |
| importer | Importer | Brings the product into the country. |
| distributor | Distributor | Wholesale and distribution. |
| retailer | Retailer | A shop that sold the product. |
| other | Company | Another role, or none given. |
Change type changes[].type
| Value | Name | Meaning |
|---|---|---|
| radar.created | New notice | The notice was published on Radar. |
| radar.updated | Update | Its text, a product, a batch, a store or another public value changed. |
| radar.cancelled | Cancelled | The notice was cancelled (cancelled=true); not proof that the product is safe. |
| radar.removed | Removed | The notice is no longer public; only notice_id is sent, without content. |
Webhook subscription event
| Value | Name | Meaning |
|---|---|---|
| radar.change | Notices | Every notice change (radar.created, radar.updated, radar.cancelled, radar.removed). Filters: tracker, category, hazard, store, gtin. |
| radar.article | Recall articles | A new publisher article tagged as a product recall. Filter: provider. |
Image kind images[].kind
| Value | Name | Meaning |
|---|---|---|
| product | Product | A photo of the product or its pack. |
| label | Label | A close-up of the label, batch or best-before date. |
| document | Document | A scanned notice or table. |
Language lang
| Value | Name | Meaning |
|---|---|---|
| hr | Croatian | The main text in Croatian. |
| en | English | The main text in English; without a translation, language says which one came. |
Order sort
| Value | Name | Meaning |
|---|---|---|
| newest | Newest | By the source's publication date, newest first (default). |
| oldest | Oldest | By publication date, oldest first. |
| updated | Recently updated | By the latest update, newest first. |
Trackers, sources, categories, hazards and stores
Live from the catalogue (refreshed every 10 minutes). The keys are the values of the tracker, source, category, hazard and store filters.
| Filter | Key | Name | Notices |
|---|---|---|---|
| tracker | product-recalls | Product recalls | 870 |
| source | adios-urednistvo | Adios uredništvo | 0 |
| source | hapih-obavijesti | HAPIH – obavijesti za potrošače | 736 |
| source | eu-rasff | RASFF – EU upozorenja o hrani | 106 |
| source | eu-safety-gate | Safety Gate – EU upozorenja o proizvodima | 28 |
| category | baby-food | Baby food · children | 19 |
| category | baby-products | Baby products · children | 1 |
| category | toys | Toys and children's products · children | 3 |
| category | food | Food | 650 |
| category | drinks | Drinks | 22 |
| category | supplements | Food supplements | 37 |
| category | pet-food | Pet food and feed | 49 |
| category | cosmetics | Cosmetics and hygiene | 0 |
| category | kitchenware | Kitchenware and food contact | 64 |
| category | household | Household and chemicals | 0 |
| category | electrical | Electrical appliances | 1 |
| category | vehicles | Vehicles and equipment | 23 |
| category | clothing | Clothing and footwear | 1 |
| category | medical | Medicines and medical devices | 0 |
| category | other | Other | 0 |
| hazard | microbial | Microbial contamination | 215 |
| hazard | allergen | Undeclared allergens | 78 |
| hazard | foreign-body | Foreign body | 74 |
| hazard | chemical | Chemical contamination | 376 |
| hazard | unauthorised | Unauthorised ingredients | 48 |
| hazard | quality-labelling | Quality and labelling | 20 |
| hazard | choking | Choking and small parts | 7 |
| hazard | injury | Injury | 20 |
| hazard | electrical-fire | Electric shock or fire | 5 |
| hazard | other | Other | 27 |
| store | muller | Müller | 49 |
| store | spar | Spar | 40 |
| store | lidl | Lidl | 35 |
| store | konzum | Konzum | 27 |
| store | kaufland | Kaufland | 23 |
| store | metro | Metro | 22 |
| store | tedi | TEDi | 16 |
| store | dm | dm | 14 |
| store | plodine | Plodine | 14 |
| store | eurospin | Eurospin | 10 |
| store | pekinska-patka | Pekinska patka | 10 |
| store | decathlon | Decathlon | 6 |
| store | pan-asia | Pan Asia | 6 |
| store | bipa | Bipa | 5 |
| store | ntl | NTL | 5 |
| store | ikea | IKEA | 4 |
| store | pepco | Pepco | 4 |
| store | boso | Boso | 3 |
| store | kik | KiK | 3 |
| store | osmi-kontinent | Osmi kontinent | 3 |
And 73 more stores in the catalogue (stores[]).
Full archive and sync
For your own copy of every notice that stays right:
- Import the archive
GET /export gives every notice in one answer (or page through notices?limit=100 by cursor). Keep meta.sync_cursor. - Read the changes
GET /changes?after=SYNC_CURSOR. Upsert the notice by notice_id, and delete it on radar.removed. - Move the cursor
Only after the whole page is done, keep meta.next_cursor and continue from it. With no changes it stays the same. - Repeat, or add a webhook
Check every few minutes, or let a radar.change webhook tell you each change.
- The change feed starts with the Radar API; older notices come from the archive. Change IDs are strings and may have gaps.
- A change carries the current public state, not a historical version: several edits of one notice may give the same state.
- A hidden or deleted notice gives only radar.removed with notice_id.
Webhooks: events
A webhook is your HTTPS address Adios sends a signed POST to when something happens. Make one in the Radar console or with a Radar key (POST /api/v1/radar/webhooks).
| Subscription | What it sends | Settings |
|---|---|---|
| radar.change | radar.created, radar.updated, radar.cancelled and radar.removed, with the whole notice. | tracker, category, hazard, store, gtin, lang |
| radar.article | radar.article: a new publisher article with the theme Povlačenje proizvoda. | provider |
The delivery's body:
POST https://example.com/radar
Content-Type: application/json
Adios-Event: radar.created
Adios-Delivery: radar_1842
Adios-Signature: t=1791797400,v1=5b2e…
{
"id": "radar_1842",
"type": "radar.created",
"created_at": "2026-10-12T09:38:00Z",
"webhook_id": "9a1d4c2b-3e5f-4a6b-8c7d-0e1f2a3b4c5d",
"test": false,
"data": {
"change": {
"id": "1842",
"type": "radar.created",
"notice_id": "6f1c2a4e-8b3d-4c5e-9a7f-1d2e3f4a5b6c",
"occurred_at": "2026-10-12T09:37:00Z",
"notice": {
"id": "6f1c2a4e-8b3d-4c5e-9a7f-1d2e3f4a5b6c",
"tracker": "product-recalls",
"language": "en",
"title": "Recall: Primjer fresh cheese 250 g due to Listeria monocytogenes",
"summary": "Mljekara Primjer recalls its fresh cheese 250 g, batch L26281, due to Listeria monocytogenes.",
"body": "The Croatian Agency for Agriculture and Food informs consumers of the recall of Primjer fresh cheese 250 g …",
"reason": "Listeria monocytogenes was found in the product.",
"advice": "Do not eat the product; return it to the shop where it was bought.",
"url": "https://adios.hr/en/radar/recalls/recall-primjer-fresh-cheese-250-g-due-to-listeria-monocytogenes-6f1c2a4e",
"translations": [
{
"language": "en",
"title": "Recall: Primjer fresh cheese 250 g due to Listeria monocytogenes",
"summary": "Mljekara Primjer recalls its fresh cheese 250 g, batch L26281, due to Listeria monocytogenes.",
"body": "The Croatian Agency for Agriculture and Food informs consumers of the recall of Primjer fresh cheese 250 g …",
"reason": "Listeria monocytogenes was found in the product.",
"advice": "Do not eat the product; return it to the shop where it was bought.",
"url": "https://adios.hr/en/radar/recalls/recall-primjer-fresh-cheese-250-g-due-to-listeria-monocytogenes-6f1c2a4e"
},
{
"language": "hr",
"title": "Opoziv: svježi sir Primjer 250 g zbog Listeria monocytogenes",
"summary": "Mljekara Primjer opoziva svježi sir 250 g, serija L26281, zbog bakterije Listeria monocytogenes.",
"body": "Hrvatska agencija za poljoprivredu i hranu obavještava potrošače o opozivu proizvoda svježi sir Primjer 250 g …",
"reason": "U proizvodu je utvrđena Listeria monocytogenes.",
"advice": "Proizvod ne konzumirati i vratiti ga u prodavaonicu u kojoj je kupljen.",
"url": "https://adios.hr/radar/opozivi/opoziv-svjezi-sir-primjer-250-g-zbog-listeria-monocytogenes-6f1c2a4e"
}
],
"category": {
"key": "food",
"name": "Food",
"slug": "food",
"concerns_children": false
},
"hazard": {
"key": "microbial",
"name": "Microbial contamination",
"slug": "microbial-contamination",
"concerns_children": false
},
"risk": "serious",
"notice_type": "recall",
"cancelled": false,
"brand": "Primjer",
"origin_country": "HR",
"issued_by": "Hrvatska agencija za poljoprivredu i hranu",
"published_at": "2026-10-12T09:30:00Z",
"detected_at": "2026-10-12T09:37:00Z",
"updated_at": "2026-10-12T09:39:00Z",
"source_url": "https://www.hapih.hr/opoziv-svjezi-sir-primjer-250-g/",
"source": {
"key": "hapih-obavijesti",
"name": "HAPIH – obavijesti za potrošače",
"publisher": "Hrvatska agencija za poljoprivredu i hranu",
"publisher_short_name": "HAPIH",
"publisher_url": "https://www.hapih.hr/kategorija/obavijesti-za-potrosace/",
"language": "hr",
"checking_enabled": true,
"check_interval_minutes": 10,
"last_checked_at": "2026-10-14T09:34:00Z",
"last_success_at": "2026-10-14T09:34:00Z"
},
"image_url": "https://www.hapih.hr/wp-content/uploads/2026/10/svjezi-sir-primjer.jpg",
"images": [
{
"url": "https://www.hapih.hr/wp-content/uploads/2026/10/svjezi-sir-primjer.jpg",
"alt_text": "Primjer fresh cheese, front of the pack",
"kind": "product"
}
],
"products": [
{
"name": "Fresh cheese 250 g",
"brand": "Primjer",
"variant": "250 g",
"gtin": "3850000000017",
"lots": [
"L26281",
"L26282"
],
"best_before": "20.10.2026.",
"best_before_date": "2026-10-20"
}
],
"companies": [
{
"role": "producer",
"name": "Mljekara Primjer d.o.o.",
"address": "Primjerska 1, 10000 Zagreb",
"country_code": "HR"
}
],
"stores": [
{
"name": "Trgovina Primjer",
"slug": "trgovina-primjer"
}
],
"update_ids": [],
"editorial": false
}
}
}
}A radar.article delivery has data.article (the article's fields); a test has test=true and an ID starting with test_.
Verifying signatures
Every delivery is signed with the webhook's secret (see the console): v1 is HMAC-SHA256 of "{t}.{body}", over the body's original bytes. Check the signature, and that t is no older than 5 minutes.
import crypto from "node:crypto";
function verify(secret, header, rawBody) {
const parts = Object.fromEntries(header.split(",").map(p => p.split("=")));
if (Math.abs(Date.now() / 1000 - Number(parts.t)) > 300) return false;
const expected = crypto.createHmac("sha256", secret).update(`${parts.t}.${rawBody}`).digest("hex");
return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(parts.v1 ?? ""));
}import hmac, hashlib, time
def verify(secret: str, header: str, raw_body: bytes) -> bool:
parts = dict(p.split("=", 1) for p in header.split(","))
if abs(time.time() - int(parts["t"])) > 300:
return False
signed = parts["t"].encode() + b"." + raw_body
expected = hmac.new(secret.encode(), signed, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, parts.get("v1", ""))static bool Verify(string secret, string header, string rawBody)
{
var parts = header.Split(',').Select(p => p.Split('=', 2)).ToDictionary(p => p[0], p => p[1]);
long t = long.Parse(parts["t"]);
if (Math.Abs(DateTimeOffset.UtcNow.ToUnixTimeSeconds() - t) > 300) return false;
byte[] hash = HMACSHA256.HashData(Encoding.UTF8.GetBytes(secret), Encoding.UTF8.GetBytes($"{t}.{rawBody}"));
return CryptographicOperations.FixedTimeEquals(
Encoding.ASCII.GetBytes(Convert.ToHexStringLower(hash)), Encoding.ASCII.GetBytes(parts["v1"]));
}function verify(string $secret, string $header, string $rawBody): bool {
parse_str(str_replace(',', '&', $header), $parts);
if (abs(time() - (int)$parts['t']) > 300) return false;
$expected = hash_hmac('sha256', $parts['t'] . '.' . $rawBody, $secret);
return hash_equals($expected, $parts['v1'] ?? '');
}Delivery and retries
- Answer 2xx within 10 seconds, once the event is safely stored; process it in your own queue.
- Delivery is at least once: duplicates can happen; spot them by id (the same as the Adios-Delivery header).
- A failure is retried after 10, 20, 40… seconds, at most an hour apart. After 20 failures in a row the webhook pauses; resuming in the console sends the same change again, and later ones wait.
- The queue is checked every 5 seconds once Radar has processed a source; sources are checked at their own intervals (source.check_interval_minutes).
- A new webhook starts from when it was made, without the old archive. Progress survives restarts; package expiry pauses deliveries, and renewal resumes from the saved point.
- Removals go to every radar.change webhook, with the ID only: the old content and filters are gone.
- Public https:// addresses only; redirects and private network addresses are refused.
Test on demand
In the Radar console or with POST /webhooks/{id}/test you send a sample, a chosen public notice or article (by ID), or your own JSON in data (up to 16,000 characters). You see your server's HTTP status, the time, the error and the exact payload sent.
- A test goes to the saved URL with a new test_ ID, test=true and a valid signature; it works while the webhook is paused too (with an active package).
- The chosen item needn't match the webhook's filters. A test changes neither the cursor, the failure count nor the queue.