https://api.debtstrike.eu odpowiadają
produkcyjnie. Cennik i regulamin są jeszcze przed publikacją, więc
warunki handlowe ustalamy indywidualnie — klucz wydajemy ręcznie po
kontakcie.
Rejestry API
Jedno API do siedmiu polskich rejestrów publicznych. Jedna koperta odpowiedzi, jedno uwierzytelnianie, jeden sposób obsługi błędów — zamiast siedmiu integracji, siedmiu formatów dat i siedmiu sposobów mówienia „nie wiem”.
| Rejestr | Co daje | Endpoint | Stan |
|---|---|---|---|
| KRS | odpis aktualny i pełny, reprezentacja, kapitał | /v1/krs/{identifier} |
GA |
| Biała lista VAT | status VAT, rachunki, weryfikacja przelewu | /v1/vat/{nip} |
GA |
| REGON / GUS | tożsamość, adres, PKD (fallback CEIDG) | /v1/regon/{identifier} |
GA |
| CRBR | beneficjenci rzeczywiści | /v1/crbr/{nip} |
GA |
| BZP | zamówienia publiczne (wykonawca i zamawiający) | /v1/bzp/{nip} |
GA |
| SUDOP | pomoc publiczna i de minimis | /v1/sudop/{nip} |
GA |
| Agregat | tożsamość + VAT + KRS jednym zapytaniem | /v1/subjects/{identifier} |
GA |
Pierwsze zapytanie w 5 minut
1. Klucz
Klucz wydajemy ręcznie — złóż wniosek w sekcji
Uzyskaj dostęp (albo napisz na
kontakt@debtstrike.eu).
Dostajesz dwa: ds_test_… (piaskownica, nie rusza
rejestrów, nie zużywa kwoty) i ds_live_… (dane
produkcyjne). Sekret pokazujemy raz — u nas w bazie
jest tylko jego skrót.
2. Zapytanie
curl -s https://api.debtstrike.eu/v1/vat/5252344078 \
-H "Authorization: Bearer ds_live_TWOJ_KLUCZ"
3. Odpowiedź
{
"apiVersion": "v1",
"requestId": "01KYFPKKTXD5ENZ1Q69TYC6PDK",
"status": "found",
"subject": {
"nip": "5252344078", "regon": "140182840", "krs": "0000240611",
"countryCode": "PL", "resolvedVia": "direct"
},
"source": {
"id": "vat", "name": "Wykaz podatników VAT (biała lista)",
"authority": "Ministerstwo Finansów", "official": true,
"endpoint": "https://wl-api.mf.gov.pl/api", "availability": "ga"
},
"data": {
"nip": "5252344078",
"name": "GOOGLE POLAND SPÓŁKA Z OGRANICZONĄ ODPOWIEDZIALNOŚCIĄ",
"statusVat": "Czynny",
"regon": "140182840",
"krs": "0000240611",
"accountNumbers": ["61109000040000000143936780"],
"registrationLegalDate": "2005-10-06"
},
"meta": {
"cache": { "hit": true, "storedAt": "2026-07-26T16:25:17.000Z",
"expiresAt": "2026-07-27T16:25:17.000Z" },
"stale": false,
"billed": 1,
"attribution": "Źródło: Wykaz podatników VAT (biała lista) (Ministerstwo Finansów), stan na 2026-07-26, …"
},
"timestamps": {
"requestedAt": "2026-07-26T17:12:19.552Z",
"sourceFetchedAt": "2026-07-26T16:25:17.000Z",
"validAsOf": "2026-07-26"
},
"warnings": []
}
4. Trzy rzeczy, które warto zrozumieć od razu
Czytaj status, nie kod HTTP
HTTP 200 mówi tylko, że zapytanie zostało obsłużone.
O danych mówi status w kopercie.
Szczegóły.
Zapisz requestId
Jest w każdej odpowiedzi, także w błędzie. Po nim odtworzymy Twoje zapytanie z audytu — to jedyna rzecz, o którą poprosimy przy reklamacji.
Użyj validAsOf, nie czasu pobrania
Rejestry mają własny stan na dzień.
timestamps.validAsOf mówi, na kiedy dane są ważne w
rejestrze — a nie kiedy je pobraliśmy.
Publikujesz dane? Weź attribution
Gotowa nota o źródle. Ustawa o otwartych danych wymaga jej przy ponownym wykorzystaniu — składamy ją za Ciebie.
Uwierzytelnianie
Klucz w nagłówku Authorization, schemat Bearer:
Authorization: Bearer ds_live_eH9vE1TxL6IrfAqU9Xh8Ja0U
| Cecha | Zachowanie |
|---|---|
| Prefiks |
ds_live_ — dane z rejestrów ·
ds_test_ — piaskownica
|
| Przechowywanie |
W bazie tylko sha256(pieprz + sekret). Zgubionego
sekretu nie odzyskamy — wydajemy nowy.
|
| Zakresy |
Klucz ma listę źródeł. Zapytanie o źródło spoza listy →
403 source_not_in_plan.
|
| Allowlista IP |
Opcjonalna, per klucz. Spoza listy →
403 ip_not_allowed.
|
| Odwołanie |
Natychmiastowe, bez restartu usługi. Odwołany klucz →
401 key_revoked.
|
| Rotacja | Wydajemy drugi klucz, przepinasz ruch, dopiero potem odwołujemy pierwszy — bez okna niedostępności. |
Piaskownica. Klucz ds_test_ zwraca
dane przykładowe o tym samym kształcie co produkcja i
nigdy nie dotyka rejestrów. Integrację możesz
napisać i przetestować, nie zużywając ani jednostki kwoty i nie
obciążając limitów rejestru.
Koperta odpowiedzi
Każde źródło odpowiada tą samą strukturą. Zmienia się wyłącznie
zawartość data. Dzięki temu obsługę błędów, cache i
audytu piszesz raz, a nie siedem razy.
| Pole | Znaczenie |
|---|---|
apiVersion |
Zawsze "v1" w tej wersji. |
requestId |
ULID zapytania. Do reklamacji i korelacji z Twoimi logami. |
status |
found · not_found ·
unavailable · forbidden ·
invalid_input
|
subject |
Znormalizowana tożsamość: nip, regon,
krs plus resolvedVia — czym dotarliśmy
do podmiotu (direct, mf_whitelist,
gus_bir).
|
source |
Który rejestr odpowiedział, jaki organ go prowadzi, czy jest oficjalny. |
data |
Dane źródła.
null zawsze, gdy status ≠
found
— nie ma stanów pośrednich.
|
meta.cache |
hit, storedAt, expiresAt.
|
meta.stale |
true = rejestr milczał, oddaliśmy ostatnie znane
dane. Patrz niżej.
|
meta.billed |
Ile jednostek pakietu zdjęła ta odpowiedź: 1 albo
0. Nigdy więcej — patrz
Jak liczymy zapytania.
|
meta.attribution |
Gotowa nota o źródle (wymóg ustawy o otwartych danych). |
timestamps.validAsOf |
Stan danych w rejestrze — to jest data, którą pokazujesz użytkownikowi. |
timestamps.sourceFetchedAt |
Kiedy my pobraliśmy. Przy trafieniu w cache starsze niż
requestedAt.
|
warnings[] |
Lista {code, message}. Nie błędy — zastrzeżenia do
interpretacji. Czytaj je.
|
not_found kontra unavailable
To jest miejsce, w którym różnimy się od proxy na rejestry — i jedyna rzecz z tej dokumentacji, którą naprawdę warto przeczytać uważnie.
not_found |
unavailable |
|
|---|---|---|
| Co się stało | Rejestr odpowiedział: nie mam takiego podmiotu | Rejestr nie odpowiedział (awaria, timeout, limit) |
| Co wiesz | Fakt. Wiedza negatywna, potwierdzona przez organ | Nic. Brak wiedzy |
| Wolno oprzeć decyzję? | Tak | Nie — ponów później |
| HTTP | 200 |
200 |
data |
null |
null albo dane z cache, gdy
meta.stale = true
|
Najczęstszy błąd integracji. Potraktowanie obu
przypadków jako „brak danych” i pokazanie użytkownikowi „firma nie
istnieje”. Przy unavailable to jest
nieprawda — to my nie wiemy. Zwykłe proxy nie robi tego
rozróżnienia, bo zwraca pustkę i zostawia Cię z pytaniem, co ona
znaczy.
Tryb degradacji
Gdy rejestr milczy, a mamy dane w cache, zwracamy je z
meta.stale = true i meta.degraded.reason. To
świadoma decyzja: lepiej oddać wczorajszy odpis z etykietą
„wczorajszy”, niż nie oddać nic.
Nie udajemy jednak, że są świeże —
timestamps.sourceFetchedAt mówi prawdę.
Limit rejestru nigdy nie przecieka na Ciebie. Biała
lista MF blokuje IP po 100 zapytaniach wyszukujących na dobę. Gdy
zbliżamy się do progu, dostajesz stale albo
unavailable — nie 429.
Kod 429 oznacza wyłącznie Twój własny limit.
Jak liczymy zapytania
Jedno zapytanie = jedna jednostka pakietu. Nie ma
mnożników: ani za źródło, ani za świeżość, ani za certyfikat. Nie
musisz nic przeliczać, żeby wiedzieć, ile Ci zostało —
meta.billed w każdej odpowiedzi mówi to wprost.
Zasada, z której wynika cała reszta: płacisz za odpowiedź z rejestru, nie za próbę.
| Sytuacja | Liczy się | Dlaczego |
|---|---|---|
Odpowiedź ok — dane zwrócone |
1 | Oczywiste. |
Odpowiedź not_found |
1 | To odpowiedź, nie błąd. Kosztowała odpytanie rejestru i ma wartość informacyjną: wiedza negatywna potwierdzona przez organ. |
Odpowiedź z cache (bez fresh) |
1 | Dostałeś wynik. Skąd go wzięliśmy, to nasza sprawa — i nasza oszczędność. |
unavailable — rejestr milczy |
0 | Nie obciążamy za cudzą awarię. |
Błąd 5xx po naszej stronie |
0 | Nie obciążamy za własną awarię. |
Błąd 400 — walidacja |
0 | Integracja musi być tania w nauce. |
Błąd 401 / 403 |
0 | — |
Błąd 429 — limit |
0 | Kara już jest, nie podwajamy jej. |
| Zapytanie o N źródeł (agregat) | N | Każde źródło to osobne odpytanie rejestru. |
| Paczka N podmiotów | N | Każdy podmiot to osobna odpowiedź, którą dostajesz i na której możesz oprzeć decyzję. |
| Certyfikat do zapytania | 0 | Nie jest osobnym zapytaniem — patrz Certyfikaty. |
Zapytanie kluczem ds_test_ |
0 | Klucze testowe nie zużywają pakietu i mają osobny limit dzienny. Patrz niżej. |
Klucze testowe (piaskownica)
Klucze testowe nie zużywają pakietu i mają osobny limit
dzienny.
Klucz ds_test_ zwraca stałe dane z próbki, nie rusza
rejestrów i nie zdejmuje ani jednej jednostki — w kopercie zobaczysz
"billed": 0. Obowiązuje go za to własny sufit:
500 zapytań na dobę (do północy UTC) i
2 zapytania na sekundę. Stan sufitu niosą nagłówki
RateLimit-Sandbox-Limit i
RateLimit-Sandbox-Remaining; po jego przekroczeniu
dostajesz 429 quota_exceeded.
Cel jest prosty: integracja ma być darmowa w nauce, ale na kluczu testowym nie da się pracować produkcyjnie — dane w piaskownicy są zamrożone i nieprawdziwe.
Sprawdź to sam. Sto zapytań o CRBR z
fresh=true i certyfikatem zdejmie z pakietu dokładnie
100, nie 3000. Ta liczba jest pilnowana testem
akceptacyjnym, nie deklaracją w cenniku.
Limity i świeżość
Trzy niezależne warstwy. Dwie pierwsze mogą zakończyć się kodem
429. Trzecia — nigdy.
| Warstwa | Nagłówki | Co znaczy wyczerpanie | Co robić |
|---|---|---|---|
| Limit chwilowy token bucket |
RateLimit-LimitRateLimit-RemainingRateLimit-Reset
|
Za dużo zapytań na sekundę |
Poczekaj RateLimit-Reset sekund. Wiadro napełnia
się samo.
|
| Pakiet miesięczny |
RateLimit-Quota-LimitRateLimit-Quota-UsedRateLimit-Quota-RemainingRateLimit-Quota-Reset
|
Pakiet wyczerpany. W planach z nadwyżką API pracuje dalej — do twardego sufitu 3× pakiet, po którym blokuje. | Czekanie nie pomoże: pakiet odnawia się w nowym okresie (miesiąc UTC). |
| Udział świeżych pobrań |
RateLimit-Fresh-LimitRateLimit-Fresh-RemainingX-DS-Freshness
|
Wyczerpany udział fresh=true w pakiecie |
Nic. Odpowiedzi lecą dalej, tylko z cache. |
RateLimit-Limit: 20
RateLimit-Remaining: 18
RateLimit-Reset: 4
RateLimit-Quota-Limit: 4000
RateLimit-Quota-Used: 1182
RateLimit-Quota-Remaining: 2818
RateLimit-Quota-Reset: 388800
RateLimit-Fresh-Limit: 800
RateLimit-Fresh-Remaining: 611
Twardy sufit nadwyżki
Po przekroczeniu pakietu zapytania idą na nadwyżkę, rozliczaną z dołu — ale nie w nieskończoność. Sufit to 3× pakiet, po nim API blokuje i wysyła alert. Blokada jest celowa: chroni Cię przed rachunkiem bez dna po błędzie w pętli. Ostrzegamy wcześniej — przy 80% i 100% pakietu, potem przy 200% i 300%.
Świeżość: ?fresh=true
fresh=true pomija cache i idzie do rejestru.
Nie kosztuje więcej — ma za to własny udział w
pakiecie. Po jego wyczerpaniu zapytanie nie jest odrzucane:
obsługujemy je z cache i mówimy o tym wprost.
HTTP/1.1 200 OK
X-DS-Freshness: degraded
RateLimit-Fresh-Remaining: 0
{
"status": "found",
"meta": { "billed": 1, "cache": { "hit": true } },
"timestamps": { "validAsOf": "2026-07-26" },
"warnings": [
{ "code": "fresh_degraded",
"message": "Wyczerpany udział świeżych pobrań w pakiecie — odpowiedź pochodzi z cache." }
]
}
Dlaczego degradujemy, a nie blokujemy. To
ograniczenie chroni rejestry publiczne przed przeciążeniem, a nie
nasz portfel. Odcięcie Cię od danych byłoby karą wymierzoną nie tam,
gdzie jest problem. Dane dostajesz zawsze —
timestamps.validAsOf mówi, jak świeże są naprawdę.
Błędy
Błędy transportu i autoryzacji zwracamy w formacie
RFC 9457 (application/problem+json).
Brak danych w rejestrze nie jest błędem — to
200 z status: not_found.
HTTP/1.1 401 Unauthorized
Content-Type: application/problem+json
{
"type": "https://api.debtstrike.eu/errors/invalid-key",
"title": "Nieprawidłowy lub odwołany klucz API",
"status": 401,
"code": "invalid_key",
"detail": "Nieprawidłowy format klucza API.",
"requestId": "01KYFPM6DWBAY31GYT46C8FNEF"
}
| HTTP | code |
Znaczenie |
|---|---|---|
| 400 | invalid_identifier |
To nie jest NIP, KRS ani REGON. |
| 401 | invalid_key |
Brak nagłówka, zły format lub nieznany klucz. |
| 401 | key_revoked |
Klucz odwołany. |
| 403 | ip_not_allowed |
Adres spoza allowlisty klucza. |
| 403 | source_not_in_plan |
Źródło spoza zakresów klucza. |
| 404 | unknown_endpoint |
Nie ma takiego zasobu. |
| 413 | payload_too_large |
Przekroczony limit pozycji w zapytaniu zbiorczym. |
| 422 | index_does_not_cover_date |
Indeks pliku płaskiego nie obejmuje wskazanego dnia. |
| 429 | rate_limited |
Limit chwilowy — ponów za RateLimit-Reset s. |
| 429 | quota_exceeded |
Wyczerpana kwota miesięczna. |
| 500 | internal_error |
Nasz błąd. Zgłoś z requestId. |
Awaria rejestru nigdy nie jest kodem 5xx. Gdy
źródło padnie, dostajesz 200 ze
status: unavailable albo dane stale. Kod
500 oznacza wyłącznie błąd po naszej stronie i jest
zawsze błędem do zgłoszenia.
Wersjonowanie
Wersja siedzi w ścieżce: /v1/…. W obrębie
v1 zobowiązujemy się, że:
-
możemy dodać pole w
data, nową wartość wwarnings[].code, nowe źródło i nowy endpoint; - nie usuniemy istniejącego pola ani nie zmienimy jego znaczenia;
-
nie zmienimy wartości
statusani kodów błędówcode.
Zmiana łamiąca kontrakt oznacza /v2 wystawione
obok /v1, nie podmianę. Twój parser ma
ignorować nieznane pola — to jedyne założenie, o które prosimy.
KRS
Odpis z Krajowego Rejestru Sądowego (Ministerstwo Sprawiedliwości).
| Parametr | Gdzie | Opis |
|---|---|---|
identifier |
ścieżka | NIP, KRS albo REGON. Most NIP→KRS robimy po swojej stronie (przez GUS, bez zużywania limitu MF). |
scope |
query |
aktualny (domyślnie) albo pelny —
odpis pełny zawiera historię wpisów.
|
fresh |
query | true pomija cache. Koszt ×3. |
certificate |
query | true wystawia certyfikat zapytania. |
Zawartość data: krs,
nip, regon, name,
legalForm, register, address,
contact, pkd, shareCapital,
representation, flags,
registrationDate, lastEntryDate,
lastFinancialStatement.
Przykład: podmiotu nie ma w rejestrze
{
"status": "not_found",
"subject": { "nip": null, "regon": null, "krs": "1132998955",
"countryCode": "PL", "resolvedVia": "direct" },
"data": null,
"meta": { "cache": { "hit": false }, "stale": false, "billed": 1 },
"timestamps": { "validAsOf": null },
"warnings": []
}
Uwaga na krótkie numery KRS. Numer KRS ma 10 cyfr z
wiodącymi zerami. /v1/krs/123 zostanie uzupełnione do
0000000123 i zwróci istniejącą spółkę — to poprawne
zachowanie rejestru, nie błąd. Jeśli przekazujesz dane od
użytkownika, waliduj po swojej stronie, czym one są.
Limity: MS nie publikuje limitów ani SLA dla API KRS. Przyjęliśmy własny ostrożny sufit 2000 zapytań na dobę i go mierzymy.
Biała lista VAT
Status podatnika
| Parametr | Gdzie | Opis |
|---|---|---|
nip |
ścieżka | NIP podatnika (10 cyfr, separatory dozwolone). |
date |
query | Dzień, na który sprawdzamy status. Domyślnie dziś. |
fresh, certificate |
query | Jak wyżej. |
data:
statusVat
(Czynny/Zwolniony/Niezarejestrowany),
name, regon, krs,
accountNumbers[], registrationLegalDate,
removalDate, restorationDate,
hasVirtualAccounts, adresy.
Weryfikacja rachunku bankowego
Odpowiada na pytanie „czy ten rachunek należał do tego podatnika tego
dnia” — czyli na pytanie, które decyduje o odpowiedzialności
solidarnej i o kosztach uzyskania przychodu.
account to NRB (26 cyfr) albo IBAN.
Skąd bierzemy potwierdzenie. Utrzymujemy własny
indeks dziennego pliku płaskiego MF (3,3 mln par NIP+rachunek,
odświeżany codziennie o 4:00). Gdy indeks obejmuje wskazany dzień,
potwierdzenie idzie z niego (data.provider = "flat_file") — bez zużywania limitów MF. To dlatego
weryfikacja rachunków skaluje się u nas do dziesiątek tysięcy
dziennie.
Semantyka, na której łatwo się przejechać.
Trafienie w pliku jest dowodem pozytywnym. Brak trafienia
nie jest dowodem negatywnym — rachunki wirtualne są
w pliku opisane maskami, których nie rozwijamy. Dlatego przy braku
trafienia zawsze dopytujemy API MF (data.provider = "wl_api"), żeby nigdy nie odpowiedzieć „rachunek nie należy do podatnika”,
gdy w rzeczywistości należy.
Limity MF, których nie musisz znać, bo trzymamy je po swojej stronie: 100 zapytań wyszukujących na dobę na adres IP (do 30 podmiotów w zapytaniu) i 5000 podmiotów metodą sprawdzającą. Przekroczenie blokuje adres IP do północy — razem z wyszukiwarką WWW.
REGON / GUS (fallback CEIDG)
Tożsamość, adres i PKD z rejestru REGON (GUS BIR). Dla jednoosobowych działalności, których BIR nie zwraca w komplecie, przewidziany jest fallback na CEIDG.
data: regon,
nip, krs, name,
entityType, active, address,
pkd, endDate, provider (gus_bir
albo ceidg).
Stan faktyczny: fallback CEIDG jest
zaimplementowany, ale na produkcji nieaktywny —
brakuje klucza do API CEIDG. Do czasu jego uzyskania
provider zawsze wynosi gus_bir. Piszemy o
tym wprost, bo obietnica źródła, którego nie ma, jest gorsza niż
jego brak.
Kody PKD normalizujemy do jednego formatu (19.20.Z)
niezależnie od tego, że GUS oddaje je jako 1920Z, a KRS z
kropkami. To jeden z powodów, dla których warto brać dane przez jedno
API zamiast z siedmiu.
CRBR — beneficjenci rzeczywiści
Beneficjenci rzeczywiści z oficjalnej bramki SOAP Ministerstwa Finansów — nie z nieoficjalnego backendu portalu.
RODO — ograniczenie u źródła. Z numeru PESEL beneficjenta wyprowadzamy wyłącznie rok urodzenia. Samego numeru nie zwracamy, nie logujemy i nie przechowujemy — dotyczy to także surowej odpowiedzi zapisywanej jako dowód w certyfikacie. Maskowanie dzieje się w warstwie pobrania, więc PESEL nie istnieje nigdzie poza pamięcią procesu w chwili parsowania.
not_found tu znaczy coś innego niż gdzie
indziej.
Oznacza brak wpisu w rejestrze, a nie brak podmiotu. Z obowiązku
zgłoszenia zwolnione są m.in. spółki publiczne, a jednoosobowe
działalności w CRBR w ogóle nie figurują. Nie interpretuj tego jako
„spółka ukrywa beneficjentów”.
CRBR jest świadomie poza agregatem
/v1/subjects — dane o osobach fizycznych pobieramy tylko
wtedy, gdy o nie wprost poprosisz. Agregat mówi o tym jawnie w polu
notIncluded.
BZP — zamówienia publiczne
Postępowania, w których podmiot wystąpił jako
wykonawca albo zamawiający. Parametr
limit (domyślnie 50, maksimum 200) ogranicza liczbę
zwróconych ogłoszeń.
{
"status": "found",
"data": {
"summary": { "asContractor": 0, "asOrganization": 2,
"latestPublicationDate": "2026-07-02T07:13:35.000Z" },
"coverage": { "from": "2025-07-26T05:15:20.000Z",
"to": "2026-07-26T08:34:58.000Z",
"noticesIndexed": 545503 },
"notices": [
{
"noticeNumber": "2026/BZP 00318648/01",
"noticeType": "ContractNotice",
"publicationDate": "2026-07-02T07:13:35.000Z",
"role": "organization",
"orderObject": "„Przebudowa wraz z modernizacją Szkoły Podstawowej…”",
"cpvCode": "45000000-7 (Roboty budowlane)",
"organizationName": "Gmina Pyrzyce"
}
]
},
"warnings": [
{ "code": "bzp_coverage",
"message": "Odpowiedź obejmuje ogłoszenia z zakresu 2025-07-26 – 2026-07-26. Poza tym zakresem nie wiemy…" }
]
}
To nasz indeks, nie proxy — i mówimy, co w nim jest.
API Biuletynu nie potrafi wyszukiwać po NIP, nie ma paginacji i
ucina wynik na 500 rekordach, więc zbudowaliśmy własny indeks. Pole
data.coverage podaje zakres, który realnie mamy
zaindeksowany.
Pusta lista nie jest dowodem braku postępowań poza tym
zakresem
— i dlatego ostrzeżenie bzp_coverage jest w każdej
odpowiedzi.
Szukamy po wszystkich znanych identyfikatorach podmiotu (NIP, KRS, REGON), bo zamawiający wpisują je zamiennie w jedno pole — na realnej próbce 19% wykonawców miało tam coś innego niż NIP. Bez tego wyszukiwanie gubiłoby co piątego wykonawcę.
SUDOP — pomoc publiczna i de minimis
Zdarzenia pomocowe zarejestrowane przez UOKiK: kto udzielił, kiedy, w
jakiej formie i za ile (PLN oraz EUR według przelicznika organu).
Parametr limit (domyślnie 200, maksimum 1000) ogranicza
listę events.
{
"status": "found",
"data": {
"totalEvents": 296, "returnedEvents": 2, "truncated": true,
"summary": {
"firstAidOn": "2017-01-17", "lastAidOn": "2026-07-20",
"totalGrossPln": 3106694545.94, "totalGrossEur": 713913825.44,
"deMinimisEvents": 1
},
"deMinimis": {
"pools": [], "windowFrom": "2023-07-26", "windowTo": "2026-07-26",
"totalGrantedEur": 0,
"note": "Wyliczenie ORIENTACYJNE, nie zastępuje zaświadczeń o pomocy de minimis…"
},
"events": [
{
"grantedOn": "2026-07-20",
"grantorName": "Polskie Sieci Elektroenergetyczne S.A.",
"measureName": "Polski mechanizm rynku mocy",
"legalBasis": "ustawa z dnia 8 grudnia 2017 r. o rynku mocy",
"formName": "dotacja lub inne bezzwrotne świadczenie",
"value": { "nominalPln": 108098.97, "grossPln": 108098.97, "grossEur": 24931.15 },
"isDeMinimis": false, "deMinimisPool": null
}
]
}
}
De minimis — co liczymy, a czego nie obiecujemy
Sekcja data.deMinimis sumuje pomoc w ruchomym oknie
trzech lat,
oddzielnie dla każdej puli (e1,
e1t, e1c, e2,
e2c), bo pule mają różne limity i różne podstawy prawne —
od 100 tys. EUR dla transportu drogowego towarów po 750 tys. EUR dla
usług w ogólnym interesie gospodarczym.
Nie sprzedajemy jednej liczby „zostało Ci X limitu”
i chcemy być w tym wprost. Powody: limit dotyczy
jednego przedsiębiorstwa w rozumieniu rozporządzenia, czyli
obejmuje podmioty powiązane — więc suma dla samego NIP-u
bywa zaniżona. Do tego pomoc sprzed 2024 r. podlega
innemu rozporządzeniu i innemu sposobowi liczenia okresu. Pole
headroomEur jest orientacyjne, oznaczone ostrzeżeniem
de_minimis_indicative i
nie zastępuje zaświadczeń o pomocy de minimis.
Wiążącą wykładnię daje organ udzielający pomocy.
Sumy zawsze pochodzą z kompletu zdarzeń, także gdy
lista events została przycięta parametrem
limit — wtedy truncated = true i ostrzeżenie
sudop_truncated. Przycięcie listy nigdy nie zafałszuje
podsumowania.
Agregat podmiotu
Tożsamość + status VAT + dane KRS jednym zapytaniem. Zwraca „kopertę
kopert”: pole
sources zawiera pełne koperty poszczególnych źródeł, więc
każde z nich ma własny status i własne
timestamps.
Pole completeness mówi, ile źródeł odpowiedziało, a
notIncluded — które źródła z oferty
świadomie pominięto i dlaczego. Dziś są to CRBR
(minimalizacja danych osobowych), BZP i SUDOP (osobne, cięższe
zapytania).
Zapytania zbiorcze
Status VAT dla wielu NIP-ów
curl -s https://api.debtstrike.eu/v1/batch/vat \
-H "Authorization: Bearer ds_live_TWOJ_KLUCZ" \
-H "Content-Type: application/json" \
-d '{"nips":["5252344078","7740001454"],"date":"2026-07-26"}'
Maksimum 30 NIP-ów. MF liczy to jako jedno zapytanie
i my też — koszt 1 jednostki niezależnie od liczby podmiotów. Powyżej
30 pozycji: 413.
Weryfikacja wielu rachunków
{
"pairs": [
{ "nip": "5252344078", "account": "61109000040000000143936780" }
],
"date": "2026-07-26"
}
Maksimum 200 par. Odpowiada wyłącznie z naszego indeksu pliku płaskiego, więc nie zużywa limitów MF — koszt 1 jednostki.
Wynik not_confirmed to nie to samo co „rachunek nie
należy”.
Oznacza brak potwierdzenia w pliku (np. rachunek wirtualny opisany
maską). Po odpowiedź definitywną użyj pojedynczego
GET /v1/vat/{nip}/bank-account/{account}, który
dopytuje API MF. Ta różnica jest w odpowiedzi powtórzona jako
ostrzeżenie.
Rozliczenie paczki. Każde sprawdzenie to jedna
jednostka —
paczka 50 NIP-ów = 50 zapytań, niezależnie od wyniku
sprawdzenia. „Niepotwierdzone" liczy się tak samo jak „potwierdzone", bo jedno
i drugie jest odpowiedzią. Zero kosztują wyłącznie pozycje odrzucone
na walidacji (invalid_input) — tak samo jak
400 na pojedynczym zapytaniu.
Gdy indeks nie obejmuje wskazanego dnia, dostajesz
422 index_does_not_cover_date zamiast cichego zgadywania.
Aktualny dzień indeksu sprawdzisz w
katalogu źródeł.
Certyfikaty zapytań
Dodaj ?certificate=true do dowolnego zapytania, a
zamrozimy odpowiedź i policzymy jej odcisk HMAC. Dostajesz
identyfikator, adres i sam odcisk w meta.certificate.
Do czego to służy: dowód, że o tej godzinie rejestr odpowiedział dokładnie tak. Przydaje się w audycie, w sporze z kontrahentem i wszędzie tam, gdzie trzeba wykazać należytą staranność — np. że w dniu przelewu rachunek widniał na białej liście.
Bez dopłaty, w każdym pakiecie. Certyfikat nie jest osobnym zapytaniem i nie zużywa dodatkowej jednostki — to ten sam, już rozliczony fakt z rejestru, tylko zamrożony i podpisany. Liczba certyfikatów jest z natury ograniczona liczbą zapytań w pakiecie, więc nie potrzebuje osobnego limitu.
Certyfikat wystawiamy wyłącznie dla odpowiedzi ze statusem
found; przy not_found dostajesz ostrzeżenie
certificate_skipped. Wymagany jest zakres
certificate przy kluczu.
Jak długo żyją
Certyfikaty są poza retencją, która po 90 dniach czyści cache, a po 400 dniach audyt — w trakcie trwania umowy nie usuwa ich nic. Po jej rozwiązaniu pozostają dostępne do pobrania przez 12 miesięcy, a potem są usuwane. To celowy kompromis: „na zawsze” nie miałoby podstawy prawnej ani końca, a skasowanie ich z dnia na dzień zniszczyłoby dowody, na których klient oparł swój audyt.
Katalog źródeł
Lista źródeł z ich dostępnością, organem prowadzącym, stanem indeksów (m.in. na jaki dzień mamy plik płaski VAT i jaki zakres pokrywa indeks BZP). Pytaj o to zamiast zaszywać stałe w kodzie — dodanie źródła zobaczysz tu pierwszy.
Stan usługi
Pierwszy to liveness. Drugi mówi, na jaki dzień mamy indeks pliku
płaskiego i ile wpisów zawiera — 503 oznacza brak
indeksu, czyli że weryfikacja rachunków idzie przez API MF (wolniej i
za budżetem, ale działa).
Kontrakt OpenAPI
Pełny kontrakt w OpenAPI 3.1, dostępny bez uwierzytelniania — bo klient ma móc wygenerować sobie SDK, nie pytając nas o nic. Przykłady w kontrakcie pochodzą z prawdziwych odpowiedzi produkcji, nie z wyobraźni.
# klient TypeScript
npx openapi-typescript https://api.debtstrike.eu/v1/openapi.json -o registry-api.d.ts
# klient dowolny (Java, C#, Go, Python…)
openapi-generator-cli generate -i https://api.debtstrike.eu/v1/openapi.json \
-g java -o ./registry-api-client
Uzyskaj dostęp
Klucze wydajemy ręcznie, po rozmowie — ten formularz
nie zakłada konta i nie zwraca klucza. Zbieramy z niego dokładnie to,
co i tak musielibyśmy dopytać mailem: kto pyta, do czego i w jakiej
skali. Dostaniesz parę ds_test_… (piaskownica, darmowa) i
ds_live_… (dane produkcyjne).
Wolisz mailem? kontakt@debtstrike.eu — napisz to samo własnymi słowami.