API CEIDGCEIDG APIHurtownia danych CEIDGdane.biznes.gov.plJDGPKDintegracje

API CEIDG v3: token, endpointy, limity i przykłady kodu

Przewodnik po API CEIDG v3 dla programisty: bezpłatny klucz z Hurtowni danych, endpointy /firmy i /firma, filtry, pola JSON, limity i kod w curl, JS i Pythonie.

Zespół OutreachPilot10 listopada 202614 min czytania
API CEIDG v3: token, endpointy, limity i przykłady kodu

Krótka odpowiedź

API CEIDG to bezpłatny interfejs REST Hurtowni danych CEIDG i Biznes.gov.pl pod adresem https://dane.biznes.gov.pl/api/ceidg/v3. Klucz dostajesz po rejestracji w Hurtowni danych i wysyłasz go w nagłówku Authorization: Bearer. Lista /firmy filtruje jednoosobowe działalności po NIP, nazwie, adresie, PKD i dacie rozpoczęcia, ale PKD oraz e-mail i telefon (od 1.11.2026 tylko za zgodą przedsiębiorcy) zwraca dopiero /firma/{id}. Limity: 50 zapytań na 3 minuty i 1 000 na godzinę.

Opis opiera się na dokumentacji dla integratorów API v3 (wersja 1.1 z 14.01.2025), na zapytaniach bez klucza z 1.11.2026 i na naszej integracji z CEIDG w OutreachPilot.pl. Jeśli nie programujesz, a szukasz firm do sprzedaży, zacznij od tekstu CEIDG jako źródło leadów B2B albo poradnika o danych z CEIDG.

Co zwraca API CEIDG, a czego nie?

CEIDG to rejestr przedsiębiorców będących osobami fizycznymi: jednoosobowych działalności i wspólników spółek cywilnych. Spółek z o.o., akcyjnych ani jawnych tu nie ma, bo są w KRS, który ma osobne Open API KRS. Dostawcą API jest Ministerstwo Rozwoju i Technologii (pole schema:provider w każdej odpowiedzi).

DostanieszNie dostaniesz
listę wpisów po NIP, REGON, nazwie, właścicielu, adresie, PKD, statusie i dacie rozpoczęciaPKD i kontaktu na liście /firmy (są tylko w szczegółach)
szczegóły wpisu: kody PKD, daty zawieszenia i wykreślenia, adresy, upadłość i zakazynumeru PESEL i daty urodzenia (art. 43 ust. 1 ustawy o CEIDG)
e-mail, telefon i www, gdy przedsiębiorca je podał (od 1.11.2026 także zgodził się na publikację)kontaktu bez zgody przedsiębiorcy od 1.11.2026
liczbę wpisów pasujących do filtrów (count) w jednym zapytaniuspółek z KRS, statusu VAT i rachunków (to API Białej listy VAT)
identyfikatory wpisów zmienionych w danym okresie (/zmiana)filtra „tylko kod przeważający” i danych finansowych

Starsze poradniki opisują datastore.ceidg.gov.pl, które według Akademii Biznes.gov.pl zastępuje Hurtownia danych pod adresem dane.biznes.gov.pl.

Jak uzyskać token do API CEIDG?

Dostęp jest bezpłatny dla każdego zarejestrowanego użytkownika: według Akademii Biznes.gov.pl informacje o przedsiębiorcach są udostępniane „bezpłatnie, wg idei otwartych danych”. Kroki według stron odczytanych 1.11.2026:

  1. Konto na Biznes.gov.pl. Do Hurtowni danych logujesz się Profilem Zaufanym (strona Hurtowni danych).
  2. Wniosek o dostęp. Uprawnienia nadaje e-usługa „Wniosek o dostęp do raportów CEIDG”. Dostęp dostajesz po zaakceptowaniu oświadczenia o ochronie i przetwarzaniu danych osobowych.
  3. Klucz w mailu. Według strony dla integratorów (aktualizacja 11.09.2026) po rejestracji przychodzi mail z dostępem do aplikacji raportowej i kluczem do API Hurtowni. Dokumentacja nazywa go tokenem JWT i każe wysyłać w nagłówku Authorization: Bearer {JWT-TOKEN}.

Okresu ważności klucza ani sposobu jego odnowienia w dokumentacji nie znaleźliśmy, więc ustaw alarm na serię odpowiedzi 401. Dokumentacja podaje za to, że dane użytkowników API są przechowywane 36 miesięcy.

Klucz trzymaj w zmiennej środowiskowej albo w menedżerze sekretów, nigdy w repozytorium. Pułapka z naszej integracji: klucz wklejony ze spacją na końcu albo z przedrostkiem Bearer dawał 401, choć sam był poprawny. Przykłady niżej czyszczą go przed użyciem.

Zapytanie bez klucza (nasz test z 1.11.2026, godz. 19:25):

curl -s -i "https://dane.biznes.gov.pl/api/ceidg/v3/firmy?miasto=Krak%C3%B3w&limit=1"

zwraca HTTP/2 401, content-type: application/json i treść {"message":"Unauthorized","http_status_code":401}. Nieprawidłowy klucz dał o 19:33 to samo. Środowisko testowe z dokumentacji (test-dane.biznes.gov.pl) nie odpowiedziało nam 1.11.2026 w ciągu 15 sekund.

Jakie endpointy ma API CEIDG v3?

Wszystkie metody to GET z tym samym nagłówkiem autoryzacji. Dokumentacja ostrzega, że wielkość liter ma znaczenie i w nazwach parametrów, i w ich wartościach, a poprawna notacja nazw to małe litery.

ŚcieżkaZwracaKlucz w JSONNasze doświadczenie
/firmy?...listę wpisów po filtrach, do 25 na stronęfirmy[], count, linksbez PKD i kontaktu (surowa odpowiedź z 8.10.2026)
/firma?nip=... albo ?regon=...pełne dane wpisów o tym NIP-ie lub REGON-iefirma[]jeden NIP bywa w kilku wpisach
/firma/{id}pełne dane jednego wpisufirma[0]źródło PKD, kontaktu i dat
/firma?ids=...&ids=...szczegóły kilku wpisów narazfirma[]jest w dokumentacji, nie testowaliśmy
/zmiana?dataod=...&datado=...identyfikatory wpisów zmienionych w okresieidentyfikatoryWpisow[]dokumentacja zaleca okno do 5 dni; nie testowaliśmy
/raporty?dataod=...&datado=...listę gotowych raportów Hurtowniraporty[]nie testowaliśmy
/raport/{id}plik raportuarchiwum ZIPnie testowaliśmy

/zmiana przydaje się do synchronizacji przyrostowej: przyjmuje też datę z godziną (RRRR-MM-DD GG:MM:SS), a po każdy identyfikator idziesz do /firma/{id}.

Filtry listy /firmy

Filtry poza page, limit, dataod i datado można powtarzać (pkd=6201Z&pkd=6210B). W naszej integracji wartości jednego pola łączą się przez LUB, a różne pola przez ORAZ.

ParametrFormatUwagi
nip, regoncyfryNIP i REGON przedsiębiorcy; nip_sc i regon_sc to identyfikatory spółki cywilnej
imie, nazwisko, nazwatekstimię i nazwisko właściciela to osobne filtry
miasto, wojewodztwo, powiat, gminanazwy jak w TERYTpowiat grodzki to nazwa miasta (powiat=Kraków), ziemski jego nazwa (powiat=tarnowski); oba działały 13.10.2026
kod, ulica, budynek, lokalkod=31-000kod pocztowy z kreską
pkd6201Zkod bez kropek; wynik obejmuje firmy z tym kodem jako przeważającym albo pozostałym
status5 wartościAKTYWNY, ZAWIESZONY, WYKRESLONY, OCZEKUJE_NA_ROZPOCZECIE_DZIALANOSCI (taka pisownia, bez „L”), WYLACZNIE_W_FORMIE_SPOLKI
dataod, datadoRRRR-MM-DDdata rozpoczęcia działalności, oba końce włącznie (sprawdzone 10.10.2026)
pageliczbastrony liczone od zera
limitod 1 do 25więcej dało HTTP 400 NIEPOPRAWNY_ROZMIAR_STRONY (13.10.2026), choć przykład w dokumentacji ma limit=50

Trzech rzeczy dokumentacja nie mówi wprost:

  • Bez status dostajesz wszystkie statusy, także wpisy wykreślone: adresy w links odpowiedzi bez tego filtra zawierają wszystkie pięć wartości. Do listy kontaktów ustaw status=AKTYWNY.
  • Kolejności wyników dokumentacja nie opisuje. Najnowsze firmy wybierzesz przez dataod i datado, a sortuj po swojej stronie.
  • Do końca 2026 r. w CEIDG są wpisy w obu klasyfikacjach PKD. W losowej próbie 465 aktywnych wpisów z 3.10.2026 kody PKD 2025 miało 49,5% (przedział 95%: od 44,9 do 54,0%, raport PKD 2025). Szukaj po obu: programiści to 6201Z w PKD 2007 oraz 6210A i 6210B w PKD 2025. Odpowiedniki sprawdzisz w wyszukiwarce PKD, terminy zmiany w tekście o PKD 2025.

Jak pobrać listę firm z CEIDG przez API?

Przykłady szukają aktywnych firm z kodem PKD programistów w Krakowie, które rozpoczęły działalność w październiku 2026 r., i pobierają szczegóły wpisów. Klucz jest w zmiennej CEIDG_API_TOKEN. W curl trzy zapytania: lista (--data-urlencode koduje polskie znaki), szczegóły wpisu o id z listy i wyszukiwanie po NIP, które od razu daje pełne dane, czasem kilku wpisów:

read -rs CEIDG_API_TOKEN && export CEIDG_API_TOKEN   # wklej klucz, nie trafi do historii
curl -s --get "https://dane.biznes.gov.pl/api/ceidg/v3/firmy" \
  -H "Authorization: Bearer $CEIDG_API_TOKEN" -H "Accept: application/json" \
  --data-urlencode "pkd=6201Z" --data-urlencode "pkd=6210A" --data-urlencode "pkd=6210B" \
  --data-urlencode "miasto=Kraków" --data-urlencode "status=AKTYWNY" \
  --data-urlencode "dataod=2026-10-01" --data-urlencode "datado=2026-10-31" \
  --data-urlencode "page=0" --data-urlencode "limit=25" \
  | jq '{count, id: .firmy[0].id, start: .firmy[0].dataRozpoczecia}'

curl -s "https://dane.biznes.gov.pl/api/ceidg/v3/firma/$ID" \
  -H "Authorization: Bearer $CEIDG_API_TOKEN" -H "Accept: application/json" \
  | jq '.firma[0] | {pkdGlowny, rokPkd, status, maEmail: (.email != null)}'

curl -s "https://dane.biznes.gov.pl/api/ceidg/v3/firma?nip=$NIP" \
  -H "Authorization: Bearer $CEIDG_API_TOKEN" -H "Accept: application/json" \
  | jq '[.firma[] | {id, status, dataRozpoczecia}]'

Puste wyjście i błąd jq zwykle oznaczają HTTP 204, czyli brak wpisów dla podanych filtrów.

Wersja dla Node 18 i nowszych. Funkcja ceidg() pilnuje odstępu 3,6 s między zapytaniami (zalecenie dokumentacji), po HTTP 429 wstrzymuje wszystkie zapytania procesu na 190 s, a błędy sieci i odpowiedzi HTML ponawia z rosnącym odstępem:

// ceidg.mjs, Node 18+. Klucz tylko ze zmiennej środowiskowej CEIDG_API_TOKEN.
const BASE = "https://dane.biznes.gov.pl/api/ceidg/v3";
const TOKEN = (process.env.CEIDG_API_TOKEN ?? "").trim().replace(/^Bearer\s+/i, "");
if (!TOKEN) throw new Error("Ustaw zmienną CEIDG_API_TOKEN");

const ODSTEP_MS = 3_600; // dokumentacja: 50 zapytań na 3 min, czyli 1 co 3,6 s
const BLOKADA_MS = 190_000; // 180 s od ostatniego zapytania i zapas
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
export class BladTrwaly extends Error {}

let nastepne = 0; // najwcześniejsza chwila kolejnego zapytania, wspólna dla procesu
async function slot() {
  const teraz = Date.now();
  const start = Math.max(teraz, nastepne);
  nastepne = start + ODSTEP_MS;
  await sleep(start - teraz);
}

export async function ceidg(sciezka, proby = 3) {
  for (let proba = 1; proba <= proby; proba++) {
    await slot();
    let res, tekst;
    try {
      res = await fetch(BASE + sciezka, {
        headers: { Authorization: `Bearer ${TOKEN}`, Accept: "application/json" },
        signal: AbortSignal.timeout(15_000),
      });
      tekst = await res.text();
    } catch (err) {
      // timeout albo zerwane połączenie („fetch failed”)
      if (proba === proby) throw err;
      await sleep(2_000 * 2 ** proba + Math.random() * 1_000);
      continue;
    }
    if (res.status === 204 || res.status === 404) return null; // brak danych
    if ([400, 401, 403].includes(res.status)) {
      throw new BladTrwaly(`HTTP ${res.status}: ${tekst.slice(0, 200)}`);
    }
    if (res.status === 429) {
      nastepne = Date.now() + BLOKADA_MS; // wszystkie kolejne zapytania czekają
      continue;
    }
    if (res.ok) {
      try {
        return JSON.parse(tekst);
      } catch {
        // strona HTML (przerwa serwisowa) albo urwany JSON: błąd przejściowy
      }
    }
    if (proba === proby) throw new Error(`CEIDG: HTTP ${res.status}, brak poprawnego JSON`);
    await sleep(2_000 * 2 ** proba + Math.random() * 1_000);
  }
  throw new Error("CEIDG: wyczerpane próby");
}

// Lista: aktywne firmy z PKD programistów w Krakowie, start w październiku 2026
const q = new URLSearchParams();
for (const kod of ["6201Z", "6210A", "6210B"]) q.append("pkd", kod); // PKD 2007 i 2025
q.set("miasto", "Kraków");
q.set("status", "AKTYWNY");
q.set("dataod", "2026-10-01");
q.set("datado", "2026-10-31");
q.set("limit", "25");

const firmy = [];
for (let page = 0; ; page++) {
  q.set("page", String(page));
  const lista = await ceidg(`/firmy?${q}`);
  if (!lista?.firmy?.length) break;
  firmy.push(...lista.firmy);
  if (firmy.length >= lista.count || lista.firmy.length < 25) break;
}

// Szczegóły: osobne zapytanie na każdą firmę
const pkdZKropkami = (kod) => kod?.replace(/^(\d{2})(\d{2})([A-Z])$/, "$1.$2.$3");
for (const { id } of firmy.slice(0, 5)) {
  const wpis = (await ceidg(`/firma/${id}`))?.firma?.[0];
  console.log(id, pkdZKropkami(wpis?.pkdGlowny?.kod), wpis?.rokPkd, wpis?.email ? "e-mail jest" : "brak e-maila");
}

To samo w Pythonie z biblioteką requests:

"""ceidg.py, Python 3.9+ z biblioteką requests. Klucz tylko ze zmiennej CEIDG_API_TOKEN."""
import os
import random
import time

import requests

BASE = "https://dane.biznes.gov.pl/api/ceidg/v3"
TOKEN = os.environ.get("CEIDG_API_TOKEN", "").strip().removeprefix("Bearer ").strip()
if not TOKEN:
    raise SystemExit("Ustaw zmienną CEIDG_API_TOKEN")

ODSTEP = 3.6  # s: 50 zapytań na 3 min
BLOKADA = 190  # s po HTTP 429
_nastepne = 0.0


class BladTrwaly(Exception):
    pass


def _slot():
    global _nastepne
    teraz = time.monotonic()
    start = max(teraz, _nastepne)
    _nastepne = start + ODSTEP
    time.sleep(start - teraz)


def ceidg(sciezka, params=None, proby=3):
    global _nastepne
    for proba in range(1, proby + 1):
        _slot()
        try:
            r = requests.get(
                BASE + sciezka,
                params=params,
                headers={"Authorization": f"Bearer {TOKEN}", "Accept": "application/json"},
                timeout=15,
            )
        except requests.RequestException:
            if proba == proby:
                raise
            time.sleep(2 * 2 ** proba + random.random())
            continue
        if r.status_code in (204, 404):
            return None
        if r.status_code in (400, 401, 403):
            raise BladTrwaly(f"HTTP {r.status_code}: {r.text[:200]}")
        if r.status_code == 429:
            _nastepne = time.monotonic() + BLOKADA  # kolejne zapytania czekają
            continue
        if r.ok:
            try:
                return r.json()
            except ValueError:
                pass  # HTML zamiast JSON: błąd przejściowy
        if proba == proby:
            raise RuntimeError(f"CEIDG: HTTP {r.status_code}, brak poprawnego JSON")
        time.sleep(2 * 2 ** proba + random.random())
    raise RuntimeError("CEIDG: wyczerpane próby")


params = [("pkd", "6201Z"), ("pkd", "6210A"), ("pkd", "6210B"),
          ("miasto", "Kraków"), ("status", "AKTYWNY"),
          ("dataod", "2026-10-01"), ("datado", "2026-10-31"), ("limit", 25)]
firmy, strona = [], 0
while True:
    lista = ceidg("/firmy", params + [("page", strona)])
    if not lista or not lista.get("firmy"):
        break
    firmy += lista["firmy"]
    if len(firmy) >= lista["count"] or len(lista["firmy"]) < 25:
        break
    strona += 1

print(len(firmy), "wpisów")
if firmy:
    wpis = (ceidg(f"/firma/{firmy[0]['id']}") or {}).get("firma", [{}])[0]
    print(wpis.get("pkdGlowny"), wpis.get("rokPkd"), "e-mail jest" if wpis.get("email") else "brak e-maila")

Przykładów nie uruchamialiśmy na produkcyjnym API, bo nasz klucz obsługuje działającą aplikację. Ścieżki błędów (błąd sieci, strona HTML, 429, 204) sprawdziliśmy na podstawionych odpowiedziach; zasady są te same co w naszym kliencie produkcyjnym.

Mapa pól odpowiedzi

Kształt odpowiedzi listy (wartości zastąpione wielokropkiem):

{
  "firmy": [
    {
      "id": "…",
      "nazwa": "…",
      "adresDzialalnosci": { "ulica": "ul. …", "budynek": "…", "lokal": "…", "miasto": "…", "wojewodztwo": "…", "powiat": "…", "gmina": "…", "kraj": "PL", "kod": "…", "terc": "…", "simc": "…" },
      "wlasciciel": { "imie": "…", "nazwisko": "…", "nip": "…", "regon": "…" },
      "dataRozpoczecia": "RRRR-MM-DD",
      "status": "AKTYWNY",
      "link": "https://dane.biznes.gov.pl/api/ceidg/v3/firma/…"
    }
  ],
  "count": "…",
  "links": { "first": "…", "prev": "…", "self": "…", "next": "…", "last": "…" },
  "properties": { "dc:title": "firmy", "schema:provider": "Ministerstwo Rozwoju i Technologii" }
}

Najważniejsze pola:

PoleLista /firmySzczegóły /firmaUwagi
idtaktakidentyfikator wpisu, klucz do /firma/{id}
nazwataktakfirma przedsiębiorcy, zawiera jego imię i nazwisko
wlasciciel.imie, wlasciciel.nazwiskotaktakdane osoby fizycznej
wlasciciel.nip, wlasciciel.regontaktakREGON bywa 9- albo 14-cyfrowy
status, dataRozpoczeciataktakdata jako RRRR-MM-DD
dataZawieszenia, dataWznowienia, dataWykreslenianietakbywają nieobecne, gdy zdarzenia nie było
adresDzialalnosci.*taktakterc, simc i ulic to kody TERYT
pkdGlowny.kod, pkd[], rokPkdnietakkod bez kropek, rokPkd to 2007 albo 2025
email, telefon, wwwnietakod 1.11.2026 tylko za zgodą przedsiębiorcy
adresDoreczenElektronicznychnietakadres do e-Doręczeń, nie zwykła skrzynka
spolki[]nietakNIP i REGON spółek cywilnych przedsiębiorcy
upadlosc, zakazy, dataZgonunietakdo odsiewania firm w upadłości i po śmierci właściciela

Jak pobrać kod PKD firmy?

Lista go nie zawiera: surowa odpowiedź /firmy z 8.10.2026 miała tylko id, nazwa, adresDzialalnosci, wlasciciel, dataRozpoczecia, status i link. Kod przeważający jest w pkdGlowny.kod szczegółów, wszystkie kody w pkd[] (w naszych odpowiedziach razem z przeważającym), a klasyfikacja w rokPkd. Kody przychodzą bez kropek: 6210B to 62.10.B, co zamienia funkcja pkdZKropkami z przykładu w Node.

Filtr pkd łapie też kody pozostałe, więc firmy, dla których branża jest poboczna, odsiejesz dopiero po szczegółach: jedno zapytanie na firmę.

Pułapki w danych

  • Tabela pól w dokumentacji ma literówki. Pisze np. firma.właściciel.nip i firma.obywatelswto.kraj, a w JSON są klucze wlasciciel i obywatelstwa. Mapuj według prawdziwej odpowiedzi, nie według tabeli.
  • Ulica z przedrostkiem. ulica przychodzi jako ul. Zwierzyniecka (przykład z dokumentacji). Przed porównaniem adresów zdejmij ul. .
  • Różna wielkość liter. W danych są obok siebie KRAKÓW i Kraków (nasz odczyt z 13.10.2026), a województwa przychodzą wielkimi literami. Porównuj bez wielkości liter i polskich znaków.
  • Puste wartości. Pusty obiekt, pusta lista i pusty napis traktuj jak brak informacji; tak robi nasz klient przy polu upadlosc.
  • Jeden NIP, kilka wpisów. Po zamknięciu i ponownym otwarciu działalności ten sam NIP ma osobne wpisy (w przykładzie z dokumentacji dwa wpisy z jednym NIP-em mają różne id i REGON-y). Kluczem w bazie niech będzie id.

Jaki jest limit zapytań API CEIDG?

Dokumentacja opisuje dwa limity działające naraz: 50 żądań w ciągu 3 minut i 1 000 w ciągu 60 minut. Po przekroczeniu pierwszego API robi 180 sekund przerwy liczonej od ostatniego żądania. Żądania wysłane przed końcem przerwy mogą według dokumentacji skończyć się ciągłym brakiem dostępu. Optymalny odstęp to 3,6 s, czyli dokładnie 1 000 zapytań na godzinę: oba limity spotykają się w jednym tempie.

Czas pracy przy jednym zapytaniu co 3,6 s:

ZadanieZapytaniaCzas
liczba wpisów dla filtrów (limit=1, pole count)13,6 s
500 firm z listy, bez szczegółów20 stron po 25ok. 1 min 12 s
500 firm ze szczegółami (PKD, kontakt)20 + 500ok. 31 min
5 000 firm ze szczegółami200 + 5 000ok. 5 h 12 min
lista wszystkich aktywnych wpisów (2 649 377 wpisów, odczyt z 10.10.2026)ok. 106 tys. stronok. 106 h, ponad 4 doby

Szczegóły wszystkich aktywnych wpisów to kolejne 2,65 mln zapytań, czyli ponad 100 dni pracy klucza. Do analiz całego rejestru sprawdź najpierw gotowe raporty Hurtowni (/raporty).

Jeden klucz, jeden limit dla całej aplikacji

Dokumentacja nie precyzuje, czy limit liczy się na klucz, czy na adres IP, ale nasz przypadek pasuje do limitu na klucz. 29.10.2026 o 13:10 skrypt pomiarowy na innym komputerze niż produkcja, z tym samym kluczem, dostał od CEIDG HTTP 429 przy 10 zapytaniach na minutę. Mieścił się w limicie 3-minutowym (30 z 50), ale 10 na minutę to 600 na godzinę, czyli 60% limitu godzinowego, a resztę zużywała produkcja.

Dlatego w OutreachPilot.pl wszystkie procesy z tym kluczem (wyszukiwarka, zadania w tle, skrypty) biorą zapytania z jednego wspólnego licznika: najwyżej 45 na 3 minuty i 900 na godzinę. Zadania w tle mogą zająć najwyżej 60% puli, żeby nie blokować użytkownika czekającego na wynik, a po 429 milkną wszyscy na 190 s. Osobny limiter w każdym procesie nie działa, bo procesy nie wiedzą o sobie nawzajem.

Jak obsłużyć 429, awarie i przerwy serwisowe?

Dokumentacja wymienia kody 200, 204, 400, 401, 403, 404, 429 i 500. Tak je obsługujemy:

OdpowiedźCo znaczyCo zrobić
200 z JSONdaneparsuj
200, ale treść to nie JSONstrona błędu bramki albo przerwy serwisowej (widzieliśmy 10.10 i 29.10.2026)błąd przejściowy: ponów później, nie zapisuj „brak firmy”
204brak danych dla kryteriówpusta lista; przy /firma?nip= NIP-u nie ma w CEIDG, np. to spółka z KRS
400błędne zapytanie, np. limit powyżej 25popraw zapytanie, nie ponawiaj
401, 403brak klucza, zły klucz albo brak uprawnieńzatrzymaj proces i sprawdź klucz
404zasób nie istnieje, np. zły idtraktuj jak brak wpisu
429przekroczony limitwstrzymaj wszystkie zapytania na co najmniej 180 s od ostatniego
500, błąd sieci, timeoutawaria po stronie rejestruponów z rosnącym odstępem

Pomiar własny: 29 i 30.10.2026 pobieraliśmy losowe wpisy do raportu o danych kontaktowych w tempie najwyżej 10 zapytań na minutę: do 3 prób na zapytanie i jeszcze jedna runda po 90 s. Wieczorem 29.10.2026 (od 18:30 do 22:24) nie udało się pobrać 74 ze 182 wpisów, czyli ok. 40%, najczęściej przez zerwane połączenie (fetch failed w Node). Od ok. 21:10 zamiast JSON przychodziła strona przerwy serwisowej, więc pracę wstrzymaliśmy do nocy. Między 2:35 a 7:24 30.10.2026 przepadło 35 z 474 wpisów (7%).

Druga pułapka to głębokie stronicowanie. 10.10.2026 strony z wysokim page na liście wszystkich aktywnych wpisów odpowiadały od 25 do 70 s, a potem kończyły się przekroczeniem czasu. Dziel więc listę na przedziały dataod i datado, sprawdzaj count przedziału zapytaniem z limit=1 i stronicuj w małych przedziałach.

Zasady, które u nas działają:

  1. Jeden licznik zapytań na klucz dla wszystkich procesów, a każde ponowienie liczy się jak nowe zapytanie.
  2. Po 429 cisza przez co najmniej 180 s od ostatniego zapytania, bez ponowień w trakcie.
  3. Twardy limit czasu: u nas 15 s dla listy i 6 s dla szczegółów jednego wpisu.
  4. Parsowanie w try: strona HTML i urwany JSON to błąd przejściowy, nie brak danych.
  5. Kolejka zamiast pętli: wpis z błędem wraca do kolejki, a kursor stoi w miejscu.
  6. Cache: listy i szczegóły trzymamy 24 h, ale status firmy tuż przed wysyłką sprawdzamy bez cache.
  7. Logi bez NIP-u i REGON-u: maskujemy je, bo to identyfikatory osób fizycznych.

Co zmienia 1 listopada 2026 i co z RODO?

Wpis w CEIDG opisuje osobę fizyczną, więc imię, nazwisko, adres, NIP i dane kontaktowe są danymi osobowymi w rozumieniu RODO. Jawność rejestru tego nie zmienia: kto zapisuje te dane u siebie, ma obowiązki z RODO, m.in. informacyjny z art. 14. Pobranie danych przez API i ich ponowne wykorzystanie, na przykład w marketingu, to osobne kwestie prawne: pierwszą reguluje m.in. ustawa o otwartych danych, a wysyłkę ofert mailem art. 398 Prawa komunikacji elektronicznej (poradnik o art. 398 PKE). Praktykę opisuje RODO w cold mailingu B2B.

Według ustawy z 13.03.2026 (Dz.U. 2026 poz. 507) nowy art. 43 ust. 1 i 1a ustawy o CEIDG pozwala udostępniać e-mail, telefon i stronę www tylko wtedy, gdy wnioskodawca zgodził się na ich udostępnienie. Do 31.10.2026 było odwrotnie: dane były jawne, chyba że przedsiębiorca się sprzeciwił. Wniosek CEIDG-1 w wersji 3.3 (od 14.10.2026) ma pole zgody na publikację telefonu i e-maila (pytania i odpowiedzi ministerstwa z 16.10.2026).

Skala zmiany: w losowej próbie 453 aktywnych wpisów z 10.10.2026 e-mail podawało 19,9% wpisów, telefon 15,9%, a stronę www 3,1% (raport o danych kontaktowych w CEIDG). Ustawa nie ma przepisu przejściowego o danych kontaktowych opublikowanych przed 1.11.2026. Jak API pokazuje wpisy bez zgody po tej dacie, jeszcze nie sprawdziliśmy; na stronach Hurtowni i Akademii Biznes.gov.pl nie znaleźliśmy 1.11.2026 komunikatu o zmianie pól email, telefon i www.

Dla kodu wynika z tego jedno: brak pola email albo www nie oznacza, że firma nie ma maila ani strony. Zapisuj taki stan jako „nie wiadomo”, a nie „brak”, żeby filtr „bez strony” nie łapał firm, które nie zgodziły się na publikację adresu. To informacja o przepisach, nie porada prawna.

Czego API CEIDG nie da?

  • Spółek z KRS (API KRS) ani statusu VAT i rachunków (API Białej listy).
  • Filtra „tylko kod przeważający”: kod przeważający sprawdzisz w szczegółach, jednym zapytaniem na firmę.
  • Pewnego kontaktu: przed zmianą e-mail miał co piąty aktywny wpis, od 1.11.2026 tylko wpisy ze zgodą, a czy adres przyjmuje pocztę, API nie sprawdza (składnię i rekordy MX sprawdzisz w weryfikatorze e-mail).
  • Szybkiej pełnej kopii: sama lista aktywnych wpisów to ponad 4 doby pracy klucza, szczegóły ponad 100 dni.

Jeśli budujesz własny produkt na danych z rejestru, API wystarczy, a zasady z tego tekstu oszczędzą Ci blokad. Jeśli potrzebujesz listy firm z branży i miasta, w OutreachPilot.pl to jedna funkcja: wyszukiwarka pobiera firmy z CEIDG po PKD i mieście (limity, cache i szczegóły są po naszej stronie), a aplikacja znajduje i weryfikuje adresy e-mail przed kampanią wysyłaną z Twojej skrzynki. Opis jest na stronie baza firm CEIDG. Plan Free kosztuje 0 zł i daje 50 leadów miesięcznie (cennik), a nowe konto zaczyna od 14 dni planu Pro bez karty.

Najczęstsze pytania

Jak uzyskać token API CEIDG?

Załóż konto na Biznes.gov.pl, złóż e-usługę „Wniosek o dostęp do raportów CEIDG” i zaakceptuj oświadczenie o ochronie danych osobowych. Po rejestracji w Hurtowni danych przychodzi mail z kluczem do API, który wysyłasz w nagłówku Authorization: Bearer. Okresu ważności klucza dokumentacja nie podaje.

Czy API CEIDG jest darmowe?

Tak. Według Akademii Biznes.gov.pl dane z Hurtowni danych CEIDG są udostępniane bezpłatnie, potrzebna jest tylko rejestracja. Ograniczeniem jest limit: 50 zapytań na 3 minuty i 1 000 na godzinę.

Jaki jest limit zapytań API CEIDG?

Dokumentacja (wersja 1.1 z 14.01.2025) podaje dwa limity naraz: 50 żądań na 3 minuty i 1 000 na 60 minut. Po przekroczeniu pierwszego API blokuje dostęp na 180 s od ostatniego żądania, więc ponawianie w trakcie blokady ją przedłuża. Bezpieczne tempo to jedno zapytanie co 3,6 s na cały klucz.

Jak pobrać PKD firmy z CEIDG przez API?

Nie z listy /firmy, bo PKD jest tylko w szczegółach wpisu. Zapytaj /firma/{id} albo /firma?nip=: kod przeważający jest w pkdGlowny.kod, wszystkie kody w pkd[], a klasyfikacja w rokPkd. Kody są bez kropek, np. 6210B to 62.10.B.

Czy API CEIDG zwraca e-mail i telefon po 1 listopada 2026?

Pola email, telefon i www są w szczegółach wpisu, ale od 1.11.2026 rejestr udostępnia te dane tylko za zgodą przedsiębiorcy (art. 43 ust. 1 i 1a ustawy o CEIDG, Dz.U. 2026 poz. 507). Przed zmianą e-mail miało 19,9% aktywnych wpisów (próba 453 wpisów z 10.10.2026). Brak pola znaczy „nie wiadomo”, nie „firma nie ma maila”.

Czym różni się Hurtownia danych CEIDG od API?

Hurtownia danych to serwis dane.biznes.gov.pl z aplikacją raportową i gotowymi raportami, do którego logujesz się Profilem Zaufanym. API v3 to programowy dostęp do tej samej Hurtowni na kluczu z maila rejestracyjnego: wpisy (/firmy, /firma), zmiany (/zmiana) i raporty (/raporty). Konto i wniosek są wspólne.

Stan na 1 listopada 2026. Endpointy, parametry, limity i kody opisaliśmy według dokumentacji dla integratorów API v3 w wersji 1.1 z 14.01.2025 (odczyt 1.11.2026); nowszego archiwum dokumentacji z 22.04.2026 ze strony dla integratorów nie porównywaliśmy. Odpowiedzi bez klucza i z błędnym kluczem sprawdziliśmy 1.11.2026 o 19:25 i 19:33, tego dnia odczytaliśmy też strony dane.biznes.gov.pl, akademia.biznes.gov.pl, biznes.gov.pl i akty w ISAP. Brak PKD na liście (8.10.2026), daty i stronicowanie (10.10.2026), limit 25 na stronę i filtry obszaru (13.10.2026) oraz 429 i awarie (29-30.10.2026) to pomiary własne OutreachPilot.pl; udziały danych kontaktowych i PKD 2025 pochodzą z naszych raportów z 10.10 i 3.10.2026.

Wyszukaj firmy z Twojej branży i miasta.

OutreachPilot szuka firm w Google Maps, PKT.pl i CEIDG, pokazuje dostępne dane kontaktowe i od razu przygotowuje kampanię po polsku.

14 dni Pro bez karty · potem Free 0 zł

Podobne artykuły

API KRSOpen API KRSodpis aktualny KRS

API KRS: odpis aktualny w JSON, endpointy, pola i limity

Przewodnik po Open API KRS dla programisty: adresy OdpisAktualny, OdpisPelny i Biuletyn, ścieżki pól JSON, kody błędów, limity i kod w curl, Node i Pythonie.

8 listopada 202614 min