Monitor przetargów
Automatyczne dostarczanie nowych przetargów zgodnych z Twoimi filtrami — region, tagi branżowe, słowa kluczowe, zakres wartości. Pull API + digest e-mail + webhook.
Pojęcia
Cztery terminy, które napotkasz we wszystkich punktach końcowych:
- Tender — pojedyncze zamówienie z portalu (NEN, VVZ, E-ZAK, TenderArena itp.). Identyfikowane za pomocą numerycznego ID.
- Filter — zapisany zestaw kryteriów (region, branża, słowa kluczowe, wartość). Gdy nowe zamówienie spełnia kryteria, generowane jest dopasowanie.
- Match — nowe zamówienie, które spełniło warunki Twojego filtra. Reprezentuje powiązanie (zamówienie × filtr) + znacznik czasu + Twój stan (oznaczone gwiazdką / wykluczone / wyświetlone).
- Taxonomy — kontrolowane słowniki dla regionów (NUTS), branż (tagi branżowe) i kodów CPV używanych do klasyfikacji zamówień.
Szybki start
Filtry można skonfigurować na dwa sposoby — efekt jest identyczny, a filtr można edytować dowolną metodą w dowolnym momencie.
curl -H "X-Api-Key: mrw_live_…" \ "https://veritra.io/api/v2/leads/tenders/search?qText=rekonstrukce%20mostu&limit=5"
Powyższe to wyszukiwanie ad hoc. Do ciągłego monitorowania (nowe zamówienia spełniające Twoje kryteria) użyj filtrów + webhook/email digest — patrz poniżej.
Wyszukiwanie zamówień (ad hoc)
Do jednorazowych wyszukiwań wśród wszystkich aktywnych zamówień. Nie używa zapisanego filtra — parametry są przekazywane bezpośrednio w ciągu zapytania.
/api/v2/leads/tenders/searchParametry zapytania
| Parametr | Typ | Opis |
|---|---|---|
| qText* | string | Tekst wyszukiwania (pełnotekstowe przeszukiwanie tytułu i opisu). |
| regions | string | Kody liściowe NUTS w formacie CSV (CZ010,CZ020). |
| cpvPrefixes | string | Prefiksy CPV w formacie CSV (45,452). |
| industryTags | string | Identyfikatory tagów branżowych w formacie CSV (con_buildings,it_development). |
| minValue | number | Minimalna szacowana wartość (CZK). Zamówienia bez wartości przechodzą przez filtr. |
| maxValue | number | Maksymalna szacowana wartość (CZK). Zamówienia bez wartości przechodzą przez filtr. |
| deadlineFrom | string (YYYY-MM-DD) | Termin składania ofert >= RRRR-MM-DD. |
| deadlineTo | string (YYYY-MM-DD) | Termin składania ofert <= RRRR-MM-DD. |
| sort | "newest" | "deadline" | "value" | Sortowanie: najnowsze (domyślnie), termin (rosnąco), wartość (malejąco). |
| limit | number | Liczba wyników, maks. 1000 (domyślnie: 50). Dla >1 tys. wyników użyj paginacji nextCursor lub /matches/export. |
| cursor | string | Kursor zestawu kluczy z poprzedniej strony (pagination.nextCursor). |
Przykład
curl -H "X-Api-Key: mrw_live_…" \ "https://veritra.io/api/v2/leads/tenders/search?qText=rekonstrukce®ions=CZ010,CZ020&minValue=500000&limit=10"
/api/v2/leads/tenders/:idOdpowiedź: { data: { id, title, description, estimatedValue, deadlineAt, contractingAuthority, documents[], starred, excluded } }.
/api/v2/leads/tenders/:id/emailBody może zawierać recipientEmail w celu przekazania wiadomości na inny adres e-mail. Domyślnie użytkownik user.email.
/api/v2/leads/documents/previewZapytanie: ?url=:original&kind=docx|xlsx. Dozwolone hosty: NEN, E-ZAK, Tender Arena, Gemin, ProfilZadavatele.
GET /api/v2/leads/documents/preview?url=https://nen.nipez.cz/…/Vyzva.docx&kind=docx → 302 Location: https://rwx-storage…/Tendero/doc-cache-v8/<hash>.html (signed, TTL 10 min)
Katalog i autouzupełnianie (publiczne, bez klucza)
Do list rozwijanych interfejsu i tworzenia filtrów. Pamięć podręczna po stronie serwera: 1 godz. Publiczne, bez wymaganego uwierzytelnienia.
/api/v2/leads/zadavatele?q=praha/api/v2/leads/countries/api/v2/leads/regions?country=CZ/api/v2/leads/regions/catalog?country=CZ&locale=cs/api/v2/leads/taxonomy/industry/api/v2/leads/taxonomy/cpvPola filtrów i dozwolone wartości
Filtr składa się z poniższych pól. Wszystkie są opcjonalne z wyjątkiem `name`. Puste pole oznacza brak ograniczenia dla danego wymiaru.
regions string[]
Regiony NUTS dla wybranego kraju (?country=CZ) z etykietami uwzględniającymi język.
CZČeská republikaSKSlovenskoPLPolskoDENěmeckoATRakouskoFRFrancieESŠpanělskoITItálieNLNizozemskoBEBelgiePTPortugalskoSEŠvédskoFIFinskoDKDánskoNONorskoIEIrskoGRŘeckoRORumunskoBGBulharskoHUMaďarskoHRChorvatskoSISlovinskoLTLitvaLVLotyšskoEEEstonskoLULucemburskoCYKyprMTMaltaCHŠvýcarskoISIslandMKSeverní MakedonieGBVelká BritánieJPJaponskoAby uzyskać pełne drzewo NUTS dla konkretnego kraju, użyj:
GET /api/v2/leads/regions/catalog?country=CZ&locale=cs GET /api/v2/leads/regions/catalog?country=DE&locale=de GET /api/v2/leads/regions/catalog?country=FR&locale=en
Przykład: kody NUTS-3 dla CZ (regiony) — rozwiń ▸
CZ010Hlavní město PrahaCZ020Středočeský krajCZ031Jihočeský krajCZ032Plzeňský krajCZ041Karlovarský krajCZ042Ústecký krajCZ051Liberecký krajCZ052Královéhradecký krajCZ053Pardubický krajCZ063Kraj VysočinaCZ064Jihomoravský krajCZ071Olomoucký krajCZ072Zlínský krajCZ080Moravskoslezský krajindustryTags string[] Zalecane
Wielotagowa klasyfikacja branżowa. Przetarg może mieć wiele tagów (np. 'stav_pozemni' + 'stav_remesla' dla remontu). Filtr dopasowuje przetarg, jeśli ustawiony jest co najmniej jeden z żądanych tagów (JSON_OVERLAPS).
Dlaczego preferować industryTags zamiast CPV: Nasze industryTags łączą klasyfikację LLM tytułu/opisu przetargu z mapowaniem CPV i wskazówkami regex — wychwytują istotne przetargi nawet wtedy, gdy instytucja zamawiająca przypisała CPV niestarannie. Czysty filtr CPV jest precyzyjny, ale zależy od dyscypliny instytucji zamawiającej, która w CZ jest niska.
con_buildingsPozemní stavby a rekonstrukce budovcon_civilInženýrské stavby (silnice, mosty, voda)con_tradesStavební řemesla a dílčí prácecon_energy_efficiencyEnergetické úspory a OZEcon_materialsStavební materiáldes_documentationProjektová dokumentace a studiedes_supervision_ohsTechnický dozor a BOZPdes_surveyingGeodézie a pozemkové úpravyit_developmentVývoj SW a integraceit_licensingSW licence a předplatnéit_hardwareHW a infrastrukturait_cybersecurityKybernetická bezpečnostit_data_aiData, analytika, AI/MLtelecom_internetTelekomunikace, internet, mobilníprof_marketingMarketing, PR, reklamaprof_legalPrávní službyprof_accountingÚčetnictví, dotace, auditprof_hrHR, náborprof_translationPřeklady a tlumočeníprof_insurancePojištění a finančníops_cleaningÚklidové službyops_securityOstraha a recepčníops_maintenanceÚdržba a servis zařízeníops_wasteOdpadové hospodářstvíops_facilitySpráva nemovitostícat_cateringStravování, cateringcat_accommodationUbytování a konferencecat_foodPotraviny a nápojetrans_transportPřeprava cestujících a nákladutrans_vehiclesVozidla, díly, leasinghealth_pharmaLéčiva a farmahealth_devicesZdravotnické přístroje a materiálhealth_careZdravotní a sociální péčegoods_furnitureNábytek a vybavení interiérůgoods_clothingOděvy, OOPP, uniformygoods_electricalElektromateriálgoods_machineryPrůmyslové strojeenergy_fuelsPohonné hmoty a palivaenergy_power_heatElektřina a teploenergy_waterVodárenstvínat_forestryLesní hospodářstvínat_greeneryÚdržba zeleně a zahradnictvínat_agricultureZemědělstvídefense_safetyHasiči, vojsko, obranasci_labLaboratorní a měřicí vybavenísci_researchVýzkum a vývojedu_trainingVzdělávání a školenículture_mediaKultura, knihy, médiacategories string[] Mniej dokładne
Prefiksy CPV (Wspólny Słownik Zamówień) o dowolnej długości — dopasowywane przez `LIKE 'prefix%'`. Tak więc '45' obejmuje wszystkie działy budowlane, '4523' tylko inżynierię lądową, '45316110' tylko oświetlenie ulic.
03Rolnictwo, leśnictwo, rybołówstwo09Paliwa i energia15Żywność i napoje18Odzież, obuwie, środki ochrony indywidualnej22Materiały drukowane, książki30Maszyny biurowe, sprzęt IT31Maszyny elektryczne, kable, oświetlenie32Radio, TV, telekomunikacja33Sprzęt medyczny, farmaceutyki34Pojazdy, transport35Sprzęt ochrony, przeciwpożarowy i wojskowy38Przyrządy laboratoryjne i pomiarowe39Meble, wyposażenie wnętrz42Maszyny przemysłowe44Materiały budowlane i konstrukcje45Roboty budowlane48Oprogramowanie i licencje50Naprawa i konserwacja55Catering, zakwaterowanie60Transport (przewóz)64Usługi pocztowe i telekomunikacyjne65Media (energia elektryczna, woda)66Usługi finansowe i ubezpieczeniowe70Zarządzanie nieruchomościami i obiektami71Usługi architektoniczne, projektowe i inżynieryjne72Usługi IT (rozwój, integracja, wsparcie)73Badania i rozwój75Administracja publiczna, obronność77Usługi rolnicze, leśne i ogrodnicze79Usługi biznesowe (prawne, księgowe, HR, marketing)80Edukacja i szkolenia85Opieka zdrowotna i społeczna90Gospodarka odpadami, ochrona środowiska, sprzątanie92Kultura, sport, rekreacja98Pozostałe usługi publicznePełny katalog (9 454 kodów) jest dostępny pod adresem /docs/leads/cpv — z możliwością wyszukiwania i grupowania według działów.
keywords string[]
Dopasowanie LIKE w tytule i opisie zamówienia. Bez rozróżniania wielkości liter. Operator OR między elementami. Przydatne jako siatka bezpieczeństwa, gdy industryTags/CPV nie wychwytują wszystkiego (np. określony typ zamówienia widoczny tylko w treści).
"keywords": ["rekonstrukcja", "oświetlenie", "przedszkole"]
minValue / maxValue number | null
Zakres szacowanej wartości zamówienia w CZK. Można ustawić tylko minValue, tylko maxValue lub oba.
emailDigest boolean
Jeśli true, otrzymujesz jeden codzienny e-mail (5:00 UTC) z podsumowaniem nowych dopasowań dla tego filtra. Domyślnie true. Można wyłączyć, edytując filtr. Webhooki konfiguruje się oddzielnie, na poziomie konta (patrz poniżej).
name string · active boolean
`name` — maks. 120 znaków, wymagane. `active` — jeśli false, filtr jest wykluczony z harmonogramu cron (brak nowych dopasowań, brak podsumowania, brak webhooka). Używaj do wstrzymania bez usuwania.
Logika filtra
Pola łączą się w następujący sposób:
MATCH = (authority.NUTS3 ∈ expand(regions))
AND (minValue ≤ estimatedValue ≤ maxValue OR estimatedValue is null or 0)
AND (industry_or_cpv OR keyword_match)
# expand(regions): kody NUTS są rozwijane do liści NUTS 3
# (CZ → 14 regionów, CZ01 → CZ010, CZ010 → CZ010).
industry_or_cpv:
if industryTags set → JSON_OVERLAPS(industryTags, tender.industryTags)
elif categories set → tender.cpvCode LIKE ANY (categories + "%")
else → false (brak filtra branżowego)
keyword_match:
if keywords set → tender.title or description LIKE ANY (%kw%)
else → false
# Jeśli nie ustawiono industryTags / categories / keywords,
# zwracane są wszystkie zamówienia pasujące do regions i zakresu wartości.industryTags ma pierwszeństwo przed categories — jeśli ustawiono oba, używany jest tylko industryTags (categories jest ignorowane). keywords działają niezależnie (są połączone operatorem OR z industry_or_cpv).
Endpointy zarządzania filtrami
Użyj klucza zarządzającego (mrw_live_…) do zarządzania filtrami. Posiada osobne limity żądań i nie zużywa kredytów LEADS, dzięki czemu zarządzanie filtrami nie wpływa na dzienny limit pobierania leadów.
/api/v2/leads/filters/api/v2/leads/filters/api/v2/leads/filters/:id/api/v2/leads/filters/:id/api/v2/leads/filters/:id/api/v2/leads/filters/:id/matches/export?format=csv|xlsx|json&view=all|starred|excludedParametry body (POST / PUT) — wspólne
| Parametr | Typ | Opis |
|---|---|---|
| name* | string | Nazwa filtra (maks. 120 znaków) |
| regions | string[] | Kody NUTS (np. `CZ010`). Można mieszać dowolne poziomy. Pusta tablica = wszystkie regiony. |
| industryTags | string[] | Zalecana ścieżka. Identyfikatory tagów z naszej taksonomii (np. 'stav_pozemni', 'it_vyvoj'). Wieloznaczne — operator OR między elementami. |
| categories | string[] | Prefiksy CPV dowolnej długości (np. '45', '4523', '45316110'). Mniej precyzyjne niż industryTags. |
| keywords | string[] | Słowa kluczowe — dopasowanie LIKE w tytule i opisie zamówienia (OR między elementami). |
| minValue | number | null | Minimalna szacowana wartość (CZK). Zamówienia bez wartości przechodzą przez filtr. |
| maxValue | number | null | Maksymalna szacowana wartość (CZK). Zamówienia bez wartości przechodzą przez filtr. |
| emailDigest | boolean | Codzienne podsumowanie e-mail (domyślnie true) |
| active | boolean | Aktywny filtr (domyślnie true) |
Webhooki nie są już konfigurowane per filtr — patrz sekcja Webhook poniżej (endpointy na poziomie konta). Pole webhookUrl jest odrzucane z kodem HTTP 410 ze względów bezpieczeństwa.
Przykłady
Budownictwo w Pradze powyżej 500 tys. CZK
curl -X POST -H "X-Api-Key: mrw_live_…" \
-H "Content-Type: application/json" \
-d '{
"name": "Budownictwo w Pradze 500 tys.+",
"regions": ["CZ010"],
"industryTags": ["con_buildings", "con_trades"],
"minValue": 500000
}' \
https://veritra.io/api/v2/leads/filtersRozwój IT i licencje SW (wszystkie regiony)
curl -X POST -H "X-Api-Key: mrw_live_…" \
-H "Content-Type: application/json" \
-d '{
"name": "Rozwój IT + licencje SW",
"industryTags": ["it_development", "it_licensing", "it_data_ai"],
"keywords": ["system informatyczny", "moduł"]
}' \
https://veritra.io/api/v2/leads/filtersOświetlenie publiczne (CZ) — kombinacja tagów i słów kluczowych
curl -X POST -H "X-Api-Key: mrw_live_…" \
-H "Content-Type: application/json" \
-d '{
"name": "Oświetlenie publiczne (CZ)",
"industryTags": ["goods_electrical", "con_civil"],
"keywords": ["oświetlenie", "latarnia uliczna", "oświetlenie publiczne", "lampy"],
"minValue": 200000
}' \
https://veritra.io/api/v2/leads/filtersDezaktywuj filtr
curl -X PATCH -H "X-Api-Key: mrw_live_…" \
-H "Content-Type: application/json" \
-d '{"active": false}' \
https://veritra.io/api/v2/leads/filters/<id>Dostarczanie — webhook a polling
Dwa sposoby pobierania nowych przetargów do systemu ERP. Większość integratorów łączy oba.
Webhook (push)
Veritra wysyła żądanie POST do Twojego punktu końcowego w ciągu ~2 sekund od dopasowania. Zdarzenie leads.match.created z pełnym ładunkiem danych przetargu.
Zalety: działanie w czasie rzeczywistym, brak narzutu pollingu, filtrowanie po stronie serwera.
Wady: wymaga publicznie dostępnego punktu końcowego (HTTPS), weryfikacji HMAC oraz obsługi idempotentności.
Polling (pull)
Twój ERP cyklicznie wywołuje GET /api/v2/leads/matches?since=… (np. co 5 min). Zwraca dopasowania od podanego znacznika czasu.
Zalety: brak publicznego punktu końcowego, prosta implementacja, odporność na restarty.
Wady: opóźnienie 5–10 min, ograniczenie liczby żądań (60 żądań/min dla API zarządzającego), marnowanie zasobów na puste odpowiedzi.
Pobieranie dopasowań
/api/v2/leads/matchesParametry zapytania
| Parametr | Typ | Opis |
|---|---|---|
| filterId | string | Filtruj według określonego filtru |
| qText | string | Tekst wyszukiwania (pełnotekstowe przeszukiwanie tytułu i opisu). |
| since | string (ISO 8601 datetime) | Tylko dopasowania od tej daty (data i czas w formacie ISO) |
| delivered | boolean | false = tylko niedostarczone, true = tylko dostarczone |
| view | "starred" | "excluded" | Widok specjalny: starred (ulubione) | excluded (ukryte). |
| sort | "newest" | "deadline" | "value" | Sortowanie: najnowsze (domyślnie), termin (rosnąco), wartość (malejąco). |
| limit | number | Liczba wyników, maks. 1000 (domyślnie: 50). Dla >1 tys. wyników użyj paginacji nextCursor lub /matches/export. |
| cursor | string | Kursor zestawu kluczy z poprzedniej strony (pagination.nextCursor). |
Zwrócone dopasowania są automatycznie oznaczane delivered=true. Każde żądanie zużywa 1 kredyt.
Formaty matchId
matchId występuje w dwóch formatach zależnie od źródła:
cm…(prefiks cuid) — zwracany z wstępnie obliczonej tabeli LeadMatch (cron dzienny). Stabilny między wywołaniami, można używać do oznaczania jako wyświetlone.live-12345Prefiks syntetyczny dla dopasowań wykrytych na żywo przez wyszukiwanie / tryb przeglądania (parametr qText, view=starred/excluded). Nie znajduje się w tabeli LeadMatch → oznaczenie jako wyświetlone nie wywołuje żadnej akcji. tenderId to sufiks po myślniku.
Przykład
curl -H "X-Api-Key: mrw_leads_…" \ "https://veritra.io/api/v2/leads/matches?delivered=false&limit=10"
Odpowiedź
{
"data": [
{
"matchId": "cm…",
"filterId": "cm…",
"filterName": "Budownictwo w Pradze",
"matchedAt": "2026-04-03T06:00:00.000Z",
"viewedAt": null,
"delivered": true,
"tender": {
"id": 12345,
"title": "Przebudowa mostu nr ref. 123",
"estimatedValue": 12500000,
"deadlineAt": "2026-05-15T22:00:00.000Z",
"publishedAt": "2026-04-01T08:00:00.000Z",
"firstSeenAt": "2026-04-01T08:30:00.000Z",
"url": "https://nen.nipez.cz/...",
"portalType": "NEN",
"cpvCode": "45000000",
"tenderType": "OFFERS",
"contractingAuthority": {
"ico": "12345678",
"name": "Město Praha",
"region": "Praha",
"district": "Praha 1"
},
"documents": [
{ "name": "Výzva.pdf", "url": "https://nen.nipez.cz/…", "fileType": "pdf", "fileSizeBytes": 320000 }
],
"starred": false,
"excluded": false
}
}
],
"pagination": { "nextCursor": "eyJmaXJzdFNlZW5BdC…", "totalCount": 42 }
}Aby przejść do następnej strony, przekaż pagination.nextCursor jako parametr ?cursor=.
Akcje dopasowań
Oznaczenie gwiazdką (ulubione), wykluczenie (ukrycie) i wyświetlenie (oznaczenie jako przeczytane) to preferencje per-przetarg przechowywane w UserTenderPreference.
/api/v2/leads/matches/:matchIdOdpowiedź: taka sama struktura jak element listy dopasowań.
/api/v2/leads/matches/:matchId/star{ "starred": true }/api/v2/leads/matches/:matchId/exclude{ "excluded": true }/api/v2/leads/matches/:matchId/viewBrak operacji dla syntetycznych dopasowań live-:id (brak wiersza do oznaczenia).
/api/v2/leads/preferences{
"data": {
"starred": [12345, 67890],
"excluded": [54321]
}
}Taksonomia
Statyczne katalogi referencyjne dla wartości filtrów. Etykiety uwzględniające ustawienia regionalne.
/api/v2/leads/taxonomy/industry?locale=cs{
"data": {
"locale": "cs",
"areas": [{ "id": "construction", "icon": "🏗️", "label": "Stavebnictví" }, …],
"tags": [
{ "id": "con_buildings", "area": "construction", "label": "Pozemní stavby", "cpvPrefixes": ["452"] },
…
]
}
}/api/v2/leads/taxonomy/cpv?locale=csOdpowiedź: struktura drzewiasta: działy → grupy → klasy → kategorie → podkategorie.
Webhook
Punkty końcowe webhooków (CRUD, rotacja klucza tajnego) są na poziomie konta — udokumentowane raz w Account API → Webhooks. Ta sekcja dotyczy wyłącznie ładunku zdarzenia specyficznego dla leadów (leads.match.created) oraz weryfikacji HMAC.
Dostarczanie zdarzeń
Po nowym dopasowaniu wysyłamy zdarzenie typu leads.match.created z podpisem HMAC-SHA256 w nagłówku X-Signature-256 klucz idempotentności w X-Idempotency-Key, a typ w X-Event-Type. W przypadku błędu ponawiamy próbę z wykładniczym opóźnieniem przez maksymalnie ~33 godziny.
Polityka ponownych prób
Jeśli Twój punkt końcowy zwróci kod spoza zakresu 2xx (lub nie odpowie w ciągu 10 s), Veritra ponawia próbę z wykładniczym opóźnieniem: 1 min, 5 min, 30 min, 2 h, 12 h, 24 h. Po 6 nieudanych próbach webhook jest oznaczany jako niedziałający i sygnalizowany w panelu. Klucz idempotentności pozostaje taki sam dla każdej próby — Twój punkt końcowy MUSI deduplikować żądania (w przeciwnym razie to samo dopasowanie zostanie przetworzone 6×).
Zarządzanie punktami końcowymi przez API
Punktami końcowymi webhooka można zarządzać bez wchodzenia do panelu. Limit: 5 punktów końcowych na konto.
/api/v2/account/webhooks/api/v2/account/webhooks/api/v2/account/webhooks/:id/api/v2/account/webhooks/:id/api/v2/account/webhooks/:id/api/v2/account/webhooks/:id/rotate-secret/api/v2/account/webhooks/:id/test/api/v2/account/webhooks/:id/deliveries/api/v2/account/webhooks/:id/deliveries/:deliveryId/replayWeryfikacja podpisu HMAC
Serwer podpisuje ładunek jako HMAC-SHA256(secret, raw_body) i przesyła go w nagłówku X-Signature-256 w formacie sha256=hex. Weryfikuj w czasie stałym — w przeciwnym razie integracja jest podatna na ataki czasowe.
// Node.js (Express)
import crypto from "node:crypto";
const WEBHOOK_SECRET = process.env.MRICKWOOD_WEBHOOK_SECRET!;
function verify(rawBody: string, sig: string | undefined): boolean {
if (!sig) return false;
const expected = "sha256=" + crypto
.createHmac("sha256", WEBHOOK_SECRET)
.update(rawBody)
.digest("hex");
// Constant-time comparison (timing-safe)
const a = Buffer.from(sig);
const b = Buffer.from(expected);
return a.length === b.length && crypto.timingSafeEqual(a, b);
}
app.post("/webhooks/veritra",
express.raw({ type: "application/json" }),
async (req, res) => {
const raw = req.body.toString("utf8");
if (!verify(raw, req.header("X-Signature-256"))) {
return res.status(401).send("Invalid signature");
}
const idempKey = req.header("X-Idempotency-Key")!;
const evt = JSON.parse(raw);
// Idempotency: store idempKey, skip if already processed
if (await alreadyProcessed(idempKey)) return res.status(200).send("ok");
await processEvent(evt);
await markProcessed(idempKey);
res.status(200).send("ok"); // Must be 2xx within 10s
},
);# Python (Flask)
import hmac, hashlib, os
from flask import Flask, request, abort
WEBHOOK_SECRET = os.environ["MRICKWOOD_WEBHOOK_SECRET"]
def verify(raw: bytes, sig: str | None) -> bool:
if not sig: return False
expected = "sha256=" + hmac.new(
WEBHOOK_SECRET.encode(), raw, hashlib.sha256
).hexdigest()
return hmac.compare_digest(sig, expected)
@app.post("/webhooks/mrickwood")
def webhook():
raw = request.get_data()
if not verify(raw, request.headers.get("X-Signature-256")):
abort(401)
# ... idempotency check + process
return "ok", 200{
"id": "evt_…",
"type": "leads.match.created",
"createdAt": "2026-04-03T06:00:00.000Z",
"data": {
"filterId": "cm…",
"filterName": "Budownictwo w Pradze",
"matchId": "cm…",
"tender": { "id": "12345", "title": "…", "estimatedValue": 12500000 }
}
}Codzienny digest e-mail
Dzięki emailDigest: true otrzymujesz codzienny e-mail z podsumowaniem nowych przetargów. Digest jest wysyłany rano (5:00 UTC) na adres e-mail przypisany do konta. Możesz go wyłączyć, edytując filtr.
Limity
| Parametr | Typ | Opis |
|---|---|---|
| Wersja próbna | 500 żądań/miesiąc | 7 dni bezpłatnie. Nie wymaga karty ani profilu rozliczeniowego. Domyślne ApiKey.requestsLimit. |
| Plan płatny | bez limitu | Miesięczny limit zostaje zniesiony. Obowiązuje wyłącznie techniczny limit 100 żądań/h/klucz. |
| Filtry | 20 | Maksymalna liczba aktywnych filtrów na konto. |
| Punkty końcowe webhooków | 5 | Maksymalna liczba aktywnych adresów URL webhooków na konto. |
Po przekroczeniu miesięcznego limitu API zwraca kod 429 z nagłówkiem Retry-After. Limit jest resetowany 1. dnia miesiąca.