<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Bartosz Kuć</title>
    <description>The latest articles on DEV Community by Bartosz Kuć (@bartoszkuc).</description>
    <link>https://dev.to/bartoszkuc</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4090240%2Fff85ca6a-70e6-4f61-97cb-9bccfa369238.jpg</url>
      <title>DEV Community: Bartosz Kuć</title>
      <link>https://dev.to/bartoszkuc</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/bartoszkuc"/>
    <language>en</language>
    <item>
      <title>Jak programistycznie sprawdzić polską firmę (VAT, Biała Lista, KRS) — REST, Python, MCP</title>
      <dc:creator>Bartosz Kuć</dc:creator>
      <pubDate>Sat, 22 Aug 2026 23:01:15 +0000</pubDate>
      <link>https://dev.to/bartoszkuc/jak-programistycznie-sprawdzic-polska-firme-vat-biala-lista-krs-rest-python-mcp-4nnd</link>
      <guid>https://dev.to/bartoszkuc/jak-programistycznie-sprawdzic-polska-firme-vat-biala-lista-krs-rest-python-mcp-4nnd</guid>
      <description>&lt;p&gt;Weryfikacja kontrahenta to w Polsce nie fanaberia, tylko element należytej staranności — status VAT i zgodność rachunku z Białą Listą wprost wpływają na to, czy zaliczysz koszt i odliczysz VAT. Problem w tym, że oficjalne źródła (Ministerstwo Finansów, Ministerstwo Sprawiedliwości, GUS, Komisja Europejska) mają rozproszone, różniące się między sobą API. Poniżej pokazuję, jak sprowadzić to do kilku wywołań HTTP, które zwracają czysty JSON — z poziomu &lt;code&gt;curl&lt;/code&gt;, Pythona i agenta AI mówiącego protokołem MCP.&lt;/p&gt;

&lt;p&gt;Wszystkie przykłady korzystają z &lt;a href="https://skanfirmy.pl" rel="noopener noreferrer"&gt;skanfirmy.pl&lt;/a&gt; — zestawu narzędzi do weryfikacji firm po NIP/KRS/REGON oraz unijnego VAT (VIES). Dane pochodzą wprost z oficjalnych rejestrów, endpointy są bez opłat i bez rejestracji (bez klucza API), a warstwa webowa działa client-side, bez trackingu.&lt;/p&gt;

&lt;h2&gt;
  
  
  Warstwa REST: jeden GET, jeden JSON
&lt;/h2&gt;

&lt;p&gt;Najprostszy przypadek — sprawdzenie NIP-u. Endpoint jest publiczny, metoda &lt;code&gt;GET&lt;/code&gt;, odpowiedź to JSON:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl https://skanfirmy.pl/nip/5260250995
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;W odpowiedzi dostaniesz m.in. status VAT (czynny/zwolniony/niezarejestrowany), dane podmiotu z Wykazu VAT oraz rachunki figurujące na Białej Liście. Dostępne ścieżki:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;GET /nip/{nip}&lt;/code&gt; — status VAT i dane z Białej Listy dla jednego NIP-u&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;GET /nips/{lista}&lt;/code&gt; — kilka NIP-ów naraz (lista rozdzielona przecinkami)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;GET /regon/{nip}&lt;/code&gt; — dane z rejestru REGON (GUS)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;GET /vies/{country}/{number}&lt;/code&gt; — walidacja unijnego numeru VAT (np. &lt;code&gt;/vies/DE/811128135&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Ponieważ to zwykły GET zwracający JSON, wpina się bez ceremonii w dowolny pipeline — cron, funkcję serverless, hook w CI, cokolwiek co potrafi zrobić request HTTP.&lt;/p&gt;

&lt;h2&gt;
  
  
  Python: weryfikacja w kodzie
&lt;/h2&gt;

&lt;p&gt;Z biblioteką &lt;code&gt;requests&lt;/code&gt; całość mieści się w kilku linijkach. Poniżej minimalna funkcja, która sprawdza status VAT i sygnalizuje wyjątkiem, gdy podmiot nie jest czynnym płatnikiem:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;sprawdz_vat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nip&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://skanfirmy.pl/nip/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;nip&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;raise_for_status&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;dane&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dane&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;vatStatus&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;dane&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Czynny&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;ValueError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;NIP &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;nip&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;: status VAT = &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="si"&gt;!r}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;dane&lt;/span&gt;

&lt;span class="n"&gt;wynik&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;sprawdz_vat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;5260250995&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Rachunki na Białej Liście:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;wynik&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;accountNumbers&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[]))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Jedna uwaga na dobre praktyki: literały zwracane przez rejestry MF (&lt;code&gt;"Czynny"&lt;/code&gt;, &lt;code&gt;"Zwolniony"&lt;/code&gt;) traktuj jako wartości kanoniczne — porównuj się do oryginału, a ewentualne tłumaczenie zostaw wyłącznie na warstwę prezentacji. Dzięki temu logika nie rozjedzie się przy zmianie języka interfejsu.&lt;/p&gt;

&lt;p&gt;Masę NIP-ów do przetworzenia hurtowo? Do jednorazowego batcha z eksportem CSV/JSON jest webowe &lt;a href="https://skanfirmy.pl/bulk" rel="noopener noreferrer"&gt;/bulk&lt;/a&gt;, a programistycznie ten sam efekt osiągniesz przez &lt;code&gt;GET /nips/{lista}&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  MCP: to samo dla agenta AI
&lt;/h2&gt;

&lt;p&gt;Tu robi się ciekawie. Kluczowy wyróżnik skanfirmy.pl to pełna dostępność dla agentów: pod &lt;code&gt;https://skanfirmy.pl/mcp&lt;/code&gt; stoi serwer &lt;a href="https://skanfirmy.pl/mcp" rel="noopener noreferrer"&gt;Model Context Protocol&lt;/a&gt; z 9 narzędziami — również bez klucza API. Agent (np. asystent księgowy) może wywołać weryfikację NIP-u tak samo, jak człowiek klika w formularz.&lt;/p&gt;

&lt;p&gt;MCP mówi po JSON-RPC 2.0 przez &lt;code&gt;POST&lt;/code&gt;. Wywołanie konkretnego narzędzia to metoda &lt;code&gt;tools/call&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST https://skanfirmy.pl/mcp &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/call",
    "params": {
      "name": "sprawdz_nip",
      "arguments": { "nip": "5260250995" }
    }
  }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Listę narzędzi z ich schematami wejścia zwróci &lt;code&gt;tools/list&lt;/code&gt; (ta sama koperta, inna &lt;code&gt;method&lt;/code&gt;). Dla agentów, które wolą samo REST, jest jeszcze &lt;code&gt;https://skanfirmy.pl/llms.txt&lt;/code&gt; — mapa endpointów i sposobu użycia w formacie czytelnym dla modeli. Chcąc iść szerzej niż polskie rejestry, zajrzyj do serwisu siostrzanego &lt;a href="https://otwarteapi.pl" rel="noopener noreferrer"&gt;otwarteapi.pl&lt;/a&gt; — katalogu publicznych API (polskich i światowych) pod kątem agentów AI.&lt;/p&gt;

&lt;h2&gt;
  
  
  Poza jednorazowym sprawdzeniem: monitoring i webhook
&lt;/h2&gt;

&lt;p&gt;Status VAT kontrahenta czy jego rachunek na Białej Liście potrafią zmienić się z dnia na dzień — a jednorazowy check tego nie wychwyci. Dlatego jest &lt;a href="https://skanfirmy.pl/monitoring" rel="noopener noreferrer"&gt;/monitoring&lt;/a&gt;: codzienne alerty o zmianie statusu VAT lub rachunku, z powiadomieniem push przez webhook podpisany HMAC. W praktyce dopinasz endpoint u siebie, weryfikujesz podpis nagłówka i reagujesz — bez odpytywania rejestrów w pętli.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wersja EN
&lt;/h2&gt;

&lt;p&gt;Cały serwis jest dwujęzyczny. Angielskie odpowiedniki stron żyją pod prefiksem &lt;code&gt;/en/&lt;/code&gt; (np. &lt;code&gt;https://skanfirmy.pl/en/&lt;/code&gt;), a endpointy REST i MCP są językowo neutralne — działają identycznie niezależnie od tego, po której stronie interfejsu jesteś.&lt;/p&gt;

&lt;p&gt;Podsumowując: trzy warstwy, jedno źródło danych. &lt;code&gt;curl&lt;/code&gt;/GET do szybkiego sprawdzenia, &lt;code&gt;requests&lt;/code&gt; do wpięcia w kod, MCP do agenta — wszystko zwraca JSON, bez rejestracji i bez klucza API. Reszta to już Twój pipeline.&lt;/p&gt;

</description>
      <category>poland</category>
      <category>api</category>
      <category>mcp</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
