VulCode
Blog

API GUS BIR krok po kroku: dane firmy po numerze NIP

Usługa BIR udostępnia dane z rejestru REGON bezpłatnie. Pokazujemy cały przebieg: klucz testowy, sesja, wyszukiwanie po NIP, pełny raport i pułapki odpowiedzi w formacie MTOM. Kod w PHP uruchomiony na środowisku testowym GUS.

VulCodeVulCode14 min czytania
API GUS BIR krok po kroku: dane firmy po numerze NIP - VulCode
Co zbudujeszPHP, 7 kroków
  1. ZalogujKrok 1Klucz użytkownika daje 20-znakowy sid.
  2. DaneSzukajPodmiotyKrok 2NIP daje REGON, typ i podstawowy adres.
  3. DanePobierzPelnyRaportKrok 3Pełne dane według typu podmiotu.
  4. Formularz uzupełniony samWynikNazwa i adres firmy po wpisaniu NIP.

Poradnik w skrócie

Klient wpisuje NIP, a formularz sam uzupełnia nazwę firmy, adres i REGON. Dane pochodzą prosto z rejestru REGON przez usługę BIR1 Głównego Urzędu Statystycznego, która jest bezpłatna dla firm i instytucji.

API GUS BIR: dane firmy po NIP

Grupa
Faktury i płatności
Poziom
średni
Czas czytania
około 14 min
Kod
PHP

Pliki: bir.php, company.php.

Kroki
7 kroków

W 7 z nich kod albo polecenia do skopiowania.

Sprawdzenie
Środowisko testowe GUS, klucz publiczny

Logowanie, wyszukiwanie, pełny raport i wylogowanie przeszły

Dokumentacja
2 oficjalne dokumenty

API REGON (BIR1), informacje, rejestracja i limity; Instrukcja techniczna usługi BIR1.2 i przykłady kopert SOAP

Z naszego kodu
walidator NIP

vulcode-panel/src/tools/walidator-nip/checks.ts

Zanim zaczniesz

Usługa działa na SOAP 1.2 i zwraca XML w formacie MTOM. Inny protokół, na przykład REST, nie jest obsługiwany. Dlatego większość problemów z integracją to nie logika, tylko koperta SOAP i parsowanie odpowiedzi, i od tego zaczynamy.

Co musisz mieć
API GUS BIR: dane firmy po NIP
Klucz testowy albo produkcyjny
Środowisko testowe działa bez rejestracji, z publicznym kluczem abcde12345abcde12345. Klucz produkcyjny dostaniesz po wysłaniu na regon_bir@stat.gov.pl pełnej nazwy firmy, jej REGON, danych osoby do kontaktu i, jeśli łączysz się przez własny serwer, adresów IP.
PHP 8 z curl i SimpleXML
Wystarczą wbudowane rozszerzenia. Klient SoapClient z PHP nie poradzi sobie z odpowiedzią MTOM bez dodatkowej pracy, dlatego wysyłamy kopertę przez curl.
Wiedza o danych testowych
Baza testowa jest pełna, ale nieaktualizowana: większość danych pochodzi z 8 listopada 2014 r., a nazwiska i adresy są zanonimizowane. Na teście zobaczysz na przykład ulicę "Test-Krucza".
Miejsce na cache
Ten sam NIP pytany przy każdym wejściu na formularz szybko zbliża Cię do limitów. Tabela z wynikiem z ostatniej doby wystarczy.

Limity zapytań według pory dnia

GodzinyNa godzinęNa minutę i na sekundę
8:00 - 16:596000120 na minutę, 3 na sekundę
6:00 - 7:59 i 17:00 - 21:598000150 na minutę, 3 na sekundę
22:00 - 5:5910000200 na minutę, 4 na sekundę

Krok po kroku

7 kroków. Okno obok pokazuje plik kroku, który właśnie czytasz.

Zbuduj kopertę SOAP i odczytaj odpowiedź MTOM

Każde wywołanie to koperta SOAP 1.2 z nagłówkami WS-Addressing: wsa:To z adresem usługi i wsa:Action z nazwą metody. Content-Type żądania to application/soap+xml.

Odpowiedź przychodzi jako application/xop+xml, czyli MTOM: koperta jest zawinięta w granice multipart. Wycinamy samą kopertę wyrażeniem regularnym i dopiero ją parsujemy.

Zaloguj się kluczem

Metoda Zaloguj zwraca dwudziestoznakowy identyfikator sesji. Każde kolejne wywołanie musi mieć go w nagłówku HTTP o nazwie sid. Sesja kończy się po Wyloguj albo po 60 minutach.

Znajdź podmiot po NIP

DaneSzukajPodmioty przyjmuje jeden identyfikator: Regon, Krs albo Nip, albo listę do 20 identyfikatorów w polach Regony9zn, Regony14zn, Krsy, Nipy. Pole Nip leży w przestrzeni nazw DataContract, stąd prefiks dat.

Wynik to XML zapisany jako tekst wewnątrz odpowiedzi, więc parsujesz go drugi raz. Gdy podmiotu nie ma, dostajesz element dane z ErrorCode 4 i komunikatem "Nie znaleziono podmiotu dla podanych kryteriów wyszukiwania".

Wybierz raport według typu podmiotu

Pola Typ i SilosID z wyszukiwania mówią, który pełny raport zwróci dane. Typ P to jednostka prawna (także spółka cywilna), SilosID 6. Typ F to osoba fizyczna, a SilosID rozróżnia działalność z CEIDG (1), rolniczą (2), pozostałą (3) i skreśloną przed 8 listopada 2014 r. (4).

Raport dla P wywołany dla F i na odwrót zwróci kod 4, jakby podmiotu nie było. To najczęstsza przyczyna "pustych" wyników według samej instrukcji GUS.

Pobierz pełny raport

DanePobierzPelnyRaport przyjmuje REGON (9 cyfr dla podmiotu, 14 dla jednostki lokalnej) i nazwę raportu. Raport BIR12OsPrawna dla GUS na teście ma 55 pól: od praw_regon9 i praw_nip przez daty wpisu po adres siedziby.

Uwaga
Kod pocztowy w wyszukiwaniu ma postać 00-925, a w pełnym raporcie 00925, bez kreski. Jeśli porównujesz oba źródła albo pokazujesz kod klientowi, sformatuj go sam.

Wyloguj się

Wyloguj zamyka sesję i zwraca true. Wywołania Zaloguj, GetValue i Wyloguj też wliczają się do limitów, więc nie loguj się osobno dla każdego zapytania w jednej operacji.

Zapamiętuj wyniki

W formularzu wywołuj usługę dopiero po wpisaniu pełnego, poprawnego NIP, nie przy każdym znaku. Wynik trzymaj przez dobę: dane w rejestrze nie zmieniają się co minutę, a limity liczy się na użytkownika.

Okno kodukrok 1 z 7
bir.phpNowy plik, 39 linii
<?phpdeclare(strict_types=1); const BIR_URL = 'https://wyszukiwarkaregontest.stat.gov.pl/wsBIR/UslugaBIRzewnPubl.svc';const BIR_NS = 'http://CIS/BIR/PUBL/2014/07'; function bir(string $action, string $body, ?string $sid = null): string{    $envelope = '<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope"'        . ' xmlns:ns="' . BIR_NS . '" xmlns:dat="' . BIR_NS . '/DataContract">'        . '<soap:Header xmlns:wsa="http://www.w3.org/2005/08/addressing">'        . '<wsa:To>' . BIR_URL . '</wsa:To>'        . '<wsa:Action>' . BIR_NS . '/IUslugaBIRzewnPubl/' . $action . '</wsa:Action>'        . '</soap:Header><soap:Body>' . $body . '</soap:Body></soap:Envelope>';     $headers = ['Content-Type: application/soap+xml; charset=utf-8'];    if ($sid !== null) {        $headers[] = 'sid: ' . $sid;    }    $curl = curl_init(BIR_URL);    curl_setopt_array($curl, [        CURLOPT_POST => true,        CURLOPT_POSTFIELDS => $envelope,        CURLOPT_HTTPHEADER => $headers,        CURLOPT_RETURNTRANSFER => true,        CURLOPT_TIMEOUT => 30,    ]);    $raw = (string) curl_exec($curl);    curl_close($curl);     // Odpowiedź to MTOM: koperta SOAP leży między granicami multipart.    if (!preg_match('/<s:Envelope.*<\/s:Envelope>/s', $raw, $match)) {        throw new RuntimeException('BIR nie zwrócił koperty SOAP');    }    $xml = new SimpleXMLElement($match[0]);    $xml->registerXPathNamespace('b', BIR_NS);    $result = $xml->xpath('//b:' . $action . 'Result');    return $result ? (string) $result[0] : '';}
bir.phpZmiana w pliku: 4 linie
function bir_login(string $key): string{    return bir('Zaloguj',        '<ns:Zaloguj><ns:pKluczUzytkownika>' . $key . '</ns:pKluczUzytkownika></ns:Zaloguj>');} $sid = bir_login(getenv('GUS_BIR_KEY') ?: 'abcde12345abcde12345');  // klucz testowy GUS
bir.phpZmiana w pliku: 11 linii
function bir_find_by_nip(string $sid, string $nip): ?SimpleXMLElement{    $result = bir('DaneSzukajPodmioty',        '<ns:DaneSzukajPodmioty><ns:pParametryWyszukiwania>'        . '<dat:Nip>' . $nip . '</dat:Nip>'        . '</ns:pParametryWyszukiwania></ns:DaneSzukajPodmioty>', $sid);    if ($result === '') {        return null;  // pusta odpowiedź: sesja wygasła albo zły sid    }    $root = new SimpleXMLElement($result);  // wynik to XML zapisany w tekście    return isset($root->dane->Regon) ? $root->dane : null;  // ErrorCode 4: brak podmiotu} $company = bir_find_by_nip($sid, '5261040828');echo $company->Nazwa, ' | REGON ', $company->Regon, ' | typ ', $company->Typ, "\n";
bir.phpZmiana w pliku: 9 linii
function bir_report_name(SimpleXMLElement $company): ?string{    return match ((string) $company->Typ . '/' . (string) $company->SilosID) {        'P/6' => 'BIR12OsPrawna',        'F/1' => 'BIR12OsFizycznaDzialalnoscCeidg',        'F/2' => 'BIR12OsFizycznaDzialalnoscRolnicza',        'F/3' => 'BIR12OsFizycznaDzialalnoscPozostala',        'F/4' => 'BIR12OsFizycznaDzialalnoscSkreslonaDo20141108',        default => null,    };}
bir.phpZmiana w pliku: 7 linii
function bir_report(string $sid, string $regon, string $report): SimpleXMLElement{    $result = bir('DanePobierzPelnyRaport',        '<ns:DanePobierzPelnyRaport><ns:pRegon>' . $regon . '</ns:pRegon>'        . '<ns:pNazwaRaportu>' . $report . '</ns:pNazwaRaportu></ns:DanePobierzPelnyRaport>', $sid);    return new SimpleXMLElement($result);} $full = bir_report($sid, (string) $company->Regon, bir_report_name($company))->dane;echo $full->praw_nazwa, ', ', $full->praw_adSiedzKodPocztowy, ' ', $full->praw_adSiedzMiejscowosc_Nazwa;
bir.phpZmiana w pliku: 2 linie
bir('Wyloguj',    '<ns:Wyloguj><ns:pIdentyfikatorSesji>' . $sid . '</ns:pIdentyfikatorSesji></ns:Wyloguj>', $sid);
company.phpNowy plik, 22 linie
function company_by_nip(PDO $db, string $nip): ?array{    $row = $db->prepare('SELECT data, fetched_at FROM gus_cache WHERE nip = ?');    $row->execute([$nip]);    $hit = $row->fetch(PDO::FETCH_ASSOC);    if ($hit && strtotime($hit['fetched_at']) > time() - 86400) {        return json_decode($hit['data'], true);  // dane z ostatniej doby    }    $sid = bir_login(getenv('GUS_BIR_KEY'));    try {        $company = bir_find_by_nip($sid, $nip);    } finally {        bir('Wyloguj', '<ns:Wyloguj><ns:pIdentyfikatorSesji>' . $sid . '</ns:pIdentyfikatorSesji></ns:Wyloguj>', $sid);    }    if ($company === null) {        return null;    }    $data = json_decode(json_encode($company), true);    $db->prepare('REPLACE INTO gus_cache (nip, data, fetched_at) VALUES (?, ?, NOW())')       ->execute([$nip, json_encode($data, JSON_UNESCAPED_UNICODE)]);    return $data;}

Sprawdź, czy działa

Cały kod z poradnika uruchomiliśmy 6 października 2026 r. na środowisku testowym GUS z kluczem publicznym. Logowanie zwróciło identyfikator sesji, wyszukiwanie po NIP 5261040828 zwróciło Główny Urząd Statystyczny z zanonimizowanym adresem bazy testowej, pełny raport BIR12OsPrawna miał 55 pól, a Wyloguj zwróciło true. Sprawdziliśmy też dwa przypadki brzegowe: NIP bez podmiotu daje ErrorCode 4, a nieprawidłowy sid daje pustą odpowiedź zamiast błędu.

terminal6 października 2026Środowisko testowe GUS, klucz publiczny
$ php bir.php
GŁÓWNY URZĄD STATYSTYCZNY | REGON 000331501 | typ P
ul. Test-Krucza 208, 00-925 Warszawa
BIR12OsPrawna: 55 pól, kod pocztowy 00925
Wyloguj: true
$ php bir.php 1111111111
ErrorCode 4: Nie znaleziono podmiotu dla podanych kryteriów wyszukiwania.
Logowanie, wyszukiwanie, pełny raport i wylogowanie przeszły
Co znaczą linie wyniku
  1. linie 2-3Wyszukiwanie po NIP 5261040828

    Baza testowa zwraca Główny Urząd Statystyczny z numerem REGON, typem P i zanonimizowanym adresem.

  2. linie 4-5Pełny raport i wylogowanie

    Raport BIR12OsPrawna ma 55 pól, a Wyloguj zwraca true.

  3. linie 6-7NIP bez podmiotu

    Numer, którego nie ma w rejestrze, daje ErrorCode 4 z opisem zamiast danych firmy.

Po czym poznasz, że działa u Ciebie

  • Zaloguj zwraca 20-znakowy sid.
  • Wyszukiwanie zwraca REGON, Typ i SilosID.
  • Pełny raport dla właściwego typu ma dane, a nie ErrorCode 4.
  • Wyloguj zwraca true.

Najczęstsze błędy

Ta sama droga co w "Co zbudujesz" na górze strony, z miejscami, w których praca staje najczęściej. Przy każdym błędzie: objaw, przyczyna i co zrobić.

Gdzie staje i dlaczego

Zaloguj

Krok 1

Klucz użytkownika daje 20-znakowy sid.

  • Zatrzymuje się: Parser XML zgłasza błąd na pierwszym znaku odpowiedzi

    Odpowiedź to MTOM z granicami multipart, a nie sam XML. Co zrobić: Wytnij kopertę od <s:Envelope do </s:Envelope> i dopiero ją parsuj.

  • Zatrzymuje się: Sesja nie działa za load balancerem

    Sid uzyskany przez jedną instancję aplikacji jest używany przez inną. Co zrobić: Każda instancja loguje się sama i używa własnego sid. Tak zaleca instrukcja GUS.

DaneSzukajPodmioty

Krok 2

NIP daje REGON, typ i podstawowy adres.

  • Zatrzymuje się: Pusta odpowiedź z DaneSzukajPodmioty

    Brak nagłówka sid, sesja starsza niż 60 minut albo zamknięta nocnym czyszczeniem puli o 3:20. Co zrobić: Zaloguj się ponownie. Przyczynę podaje GetValue z parametrem KomunikatKod: kod 7 to brak sesji.

  • Zatrzymuje się: Brak pola Nip w wyszukiwaniu mimo poprawnej koperty

    Element Nip bez przestrzeni nazw DataContract. Co zrobić: Użyj prefiksu dat z przestrzenią http://CIS/BIR/PUBL/2014/07/DataContract.

DanePobierzPelnyRaport

Krok 3

Pełne dane według typu podmiotu.

  • Zatrzymuje się: ErrorCode 4 dla firmy, która istnieje

    Raport dla osoby prawnej wywołany dla osoby fizycznej albo odwrotnie. Co zrobić: Wybierz raport według pól Typ i SilosID z wyszukiwania.

Formularz uzupełniony sam

Wynik

Nazwa i adres firmy po wpisaniu NIP.

    Jak to robimy u siebie

    Ten sam temat w kodzie, który utrzymujemy: walidator NIP.

    Panel firmy z zapytaniami do rejestru REGON, limitami i nocną aktualizacją

    Najpierw suma kontrolna, potem GUS

    Zanim formularz zapyta GUS, sprawdzamy sumę kontrolną NIP: wagi 6, 5, 7, 2, 3, 4, 5, 6, 7, reszta z dzielenia przez 11 musi równać się ostatniej cyfrze. Numer z literówką nie zużywa wtedy limitu zapytań i nie czeka na odpowiedź z rejestru.

    Ta funkcja to kod naszego darmowego narzędzia walidator NIP, które działa na naszej stronie, w przeglądarce i bez konta. W integracji z GUS powtórz ten test na serwerze, bo to serwer decyduje, co trafia do usługi.

    vulcode-panel/src/tools/walidator-nip/checks.tsts, 16 linii
    const digits = (s: string) => s.split('').map(Number);const weighted = (value: string, weights: readonly number[]) =>  digits(value).reduce((sum, d, i) => sum + d * (weights[i] ?? 0), 0); const checkNip = (raw: string): Check => {  const nip = raw.replace(/^PL/, '');  const control = weighted(nip, [6, 5, 7, 2, 3, 4, 5, 6, 7]) % 11;  const valid = control !== 10 && control === Number(nip[9]);  return {    input: raw,    kind: 'NIP',    valid,    message: valid ? 'Suma kontrolna się zgadza.' : 'Suma kontrolna się nie zgadza. W numerze jest literówka.',    formatted: valid ? [['NIP', nip], ['NIP UE (VAT-UE)', `PL${nip}`]] : [],  };};

    Fragment narzędzia walidator NIP, skrócony do samego NIP.

    Powiązane strony i źródła

    Usługa, narzędzia i hasła, do których odsyła ten poradnik, i kolejne poradniki.

    Oficjalna dokumentacja

    Wolisz, żeby zrobił to ktoś, kto już to robił?

    Opisz swój system. Odpiszemy w godzinę z planem integracji i wyceną.

    Pytania