For developers · REST and MCP

VAT White List (Wykaz VAT) API by NIP
keyless REST/JSON

VAT status and entity data from Poland's VAT White List (Wykaz VAT, Ministry of Finance) by tax ID (NIP), with a single GET that returns clean JSON. No API key, no sign-up. We have no daily limit of our own, but the Ministry of Finance API has one — once it is exhausted, the endpoint answers 503. It is a layer over the Ministry's Wykaz VAT API that handles sessions, dates in the URL and response parsing for you.

One GET, one JSON

The endpoint is public, the method is GET, and the ?format=json parameter returns machine-readable data (without it you get an HTML page for humans). The White List data is in the bl field (Biała Lista):

curl "https://skanfirmy.pl/nip/5260250995?format=json"

You get the VAT status, official name, REGON, address (for natural persons, since 26 Sep 2026, only the town instead of the address, and since 27 Sep 2026 no REGON), KRS number and a Ministry of Finance timestamp, straight from the VAT White List:

{
  "nip": "5260250995",
  "source": "krs",
  "bl": {
    "nip": "5260250995",
    "name": "ORANGE POLSKA SPÓŁKA AKCYJNA",
    "regon": "012100784",
    "statusVat": "Czynny",
    "vatActive": true,
    "statusVatCode": "active",
    "address": "ALEJE JEROZOLIMSKIE 160, 02-326 WARSZAWA",
    "krs": "0000010681",
    "registrationLegalDate": "1996-01-01",
    "accountNumbers": ["<26-digit account number>", "…"],
    "mfRequestDateTime": "25-08-2026 14:59:57"
  },
  "krs": { ... },
  "krsError": null,
  "ceidg": null,
  "privacy": null,
  "checkedAt": "2026-08-25"
}

No API key, no sign-up

The endpoint works right away: no account to create, no API key to generate. That is a deliberate difference from commercial Wykaz VAT wrappers: the goal is a VAT-status check you can wire up in a minute, including from an AI agent. A limit protects the service from abuse: at most 20 requests per 10 seconds from one IP address (above that, HTTP 429 for 10 seconds; since 25 Sep 2026). We have no daily limit of our own, but the Ministry of Finance API has one for this service — once it is exhausted, the endpoint answers 503 with a Retry-After header. The service is free of charge and meant for single lookups triggered by users or their agents, not for bulk data harvesting.

What the bl field returns

The VAT White List (Biała Lista) is kept by the Ministry of Finance. The bl field in the /nip/{nip} response holds the VAT taxpayer data for that NIP:

FieldMeaning
statusVatRaw Ministry of Finance literal, verbatim: Czynny — active VAT payer, Zwolniony — exempt taxpayer, Niezarejestrowany — not in the VAT register
vatActiveDerived field (boolean): true only for an active VAT payer (statusVat === "Czynny"). Unambiguous, language-neutral branching without knowing the Polish literals
statusVatCodeDerived field (enum): active / exempt / not_registered — a machine-readable, language-independent counterpart to statusVat
nameOfficial taxpayer name from the VAT White List
regonThe entity's REGON number; for a natural person the field is omitted since 27 Sep 2026 (privacy.hidden lists regon)
addressAddress from the VAT White List (registered seat or fixed place of business). For a natural person, since 26 Sep 2026, always null — the town is in city instead
cityNatural persons only, since 26 Sep 2026: just the town from the White List address, sent instead of the address (omitted when the address has no postal code)
krsKRS number (if the entity is in the National Court Register)
registrationLegalDateDate of registration as a VAT taxpayer; for a natural person the field is omitted since 27 Sep 2026
accountNumbersBank accounts from the White List (26-digit NRB) — since 26 Sep 2026 only for entities with a KRS number
accountCount, accountCheckNatural persons only, since 26 Sep 2026: how many accounts the White List has and — when there are any — a ready POST /rachunek request for checking one specific account
mfRequestDateTimeTimestamp of the Ministry of Finance response — proof of the moment the status was valid (useful for evidence purposes)

The raw Ministry of Finance literal (statusVat) is always kept in the response, verbatim — it is the evidentiary value of "what the register said". For branching in code, use the derived statusVatCode (active/exempt/not_registered) or vatActive, or compare against the raw literal ("Czynny", "Zwolniony") — never against a translated display label, because translating would change the value your logic depends on.

Bank accounts from the White List

For entities with a KRS number (companies, foundations, associations) the /nip/{nip} endpoint does return the bank accounts registered on the MF White List — in the bl.accountNumbers field (an array of 26-digit NRB numbers). For natural persons running a business, since 26 Sep 2026 you get only the number of accounts instead of the list (details below). To confirm whether a specific account is on the White List for a given NIP (a due-diligence check), use POST /rachunek, the MCP tool sprawdz_rachunek or the Bank account check tool — they return a definitive yes/no for a single account against the VAT White List.

Natural persons running a business — change since 26 Sep 2026

We treat an entity without a KRS number — a sole proprietorship or a civil-law partnership — as a natural person, because its name, address and account numbers are usually personal data of the owner or the partners. So since 26 Sep 2026 we return for such an entity only what is needed to verify a counterparty (GDPR data minimisation):

  • instead of the account list, the number of accounts in bl.accountCount and, when there are any, bl.accountCheck: a ready POST /rachunek request for checking the account on an invoice;
  • only the town from the address, in bl.city (bl.address is null) — the White List does not say whether a natural person's address is a place of business or a home (VAT Act art. 96b(3)(7): the fixed place of business or, when there is none, the home address);
  • since 27 Sep 2026 no REGON number, registration date or PKD codes: bl.regon and bl.registrationLegalDate are omitted and ceidg is always null (we no longer fetch the PKD codes of natural persons);
  • a privacy field: what was left out (hidden), why (reason) and where the full entry is (officialSource — the VAT taxpayer register page on gov.pl);
  • the X-Robots-Tag: noindex and Cache-Control: private, max-age=3600 headers; the HTML page shows the name only in the page body, not in the title, description or structured data.

Nothing changes for entities with a KRS number, and their privacy is null. /nips/{list} and the MCP tools work the same way, and since 27 Sep 2026 so do the tools on this site (VAT White List, KRS, Bulk NIP and the homepage search). Part of a response for a natural person:

{
  "nip": "<NIP>",
  "bl": {
    "name": "<first name, last name and business name>",
    "statusVat": "Czynny",
    "address": null,
    "city": "WARSZAWA",
    "accountCount": 2,
    "accountCheck": {
      "method": "POST",
      "url": "https://skanfirmy.pl/rachunek",
      "body": { "nip": "<NIP>", "nrb": "<26 cyfr rachunku>" }
    },
    ...
  },
  "privacy": {
    "naturalPerson": true,
    "hidden": ["accountNumbers", "residenceAddress", "regon", "registrationLegalDate"],
    "reason": "Dane osoby fizycznej prowadzącej działalność ograniczone do niezbędnych do weryfikacji kontrahenta (RODO). Pełny wpis publikuje rejestr źródłowy (officialSource).",
    "officialSource": "https://www.gov.pl/web/kas/wykaz-podatnikow-vat"
  },
  ...
}

The reason text and the nrb placeholder come from the API in Polish, verbatim.

Checking one specific account — POST /rachunek

Since 26 Sep 2026 you can check the account number from an invoice with a single POST request, again with no API key. The NIP and the account number go in the request body (JSON), not in the URL — URL paths end up in traffic statistics, the request body does not:

curl -X POST "https://skanfirmy.pl/rachunek" \
  -H "Content-Type: application/json" \
  -d '{"nip": "<NIP>", "nrb": "<26-digit account number>"}'

We ask the Ministry of Finance (the "check" method of the White List API) and return its answer: the raw literal accountAssigned ("TAK" — yes, or "NIE" — no), the derived assigned field (true/false) and requestId — the Ministry's request id, worth keeping as proof of due diligence:

{
  "nip": "<NIP>",
  "nrb": "<26-digit account number>",
  "accountAssigned": "TAK",
  "assigned": true,
  "requestId": "<Ministry request id>",
  "mfRequestDateTime": "26-09-2026 10:15:02",
  "checkedAt": "2026-09-26"
}

The account number may contain spaces and a PL prefix. A wrong NIP or account number (length, checksum) returns 400 without a query to the Ministry, and an exhausted Ministry daily limit for the service (or a refusal by the Ministry) returns 503 with a Retry-After header; since 27 Sep 2026 that response also has a contact field with the address for integrations that need steady access at a larger scale. Responses carry Cache-Control: no-store, so they are not kept in any cache. The same limit applies as for the other endpoints: 20 requests per 10 seconds from one IP address. An AI agent can do the same with the MCP tool sprawdz_rachunek.

In Python

With the requests library it is a few lines. Note the comparison against the raw literal "Czynny":

import requests

def vat_status(nip: str) -> str:
    r = requests.get(f"https://skanfirmy.pl/nip/{nip}?format=json", timeout=10)
    r.raise_for_status()
    return r.json()["bl"]["statusVat"]

s = vat_status("5260250995")
# compare against the raw MF literal, not a translated label
if s == "Czynny":
    print("Active VAT payer")
else:
    print("Heads up — status:", s)
# ORANGE POLSKA SPÓŁKA AKCYJNA → Czynny

An entity that is not in the register returns 404, and a NIP with an invalid checksum returns 400. It is worth handling both instead of assuming every NIP has an entry.

In JavaScript

const r = await fetch("https://skanfirmy.pl/nip/5260250995?format=json");
if (r.ok) {
  const { bl } = await r.json();
  console.log(bl.name, "→", bl.statusVat);
  // bl.statusVat === "Czynny" — compare against the raw literal
}

In PHP

$ch = curl_init("https://skanfirmy.pl/nip/5260250995?format=json");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$bl = json_decode(curl_exec($ch), true)["bl"];
echo $bl["name"] . " · VAT: " . $bl["statusVat"];
// ORANGE POLSKA SPÓŁKA AKCYJNA · VAT: Czynny

Several NIPs at once

To check the VAT status for a list of entities in a single request, use /nips/{list}?format=json — separate the NIPs with commas (at most 30 per request) and the response contains a results array with a flat entry for each of them (a repeated NIP counts once, and NIPs with an invalid checksum are listed in invalidInput, not in results) — no bl field and no KRS extract (krs is just the number from the White List): nip, found, name, statusVat (with the derived vatActive and statusVatCode), regon, krs, address, accountNumbers and registrationLegalDate. For natural persons (since 27 Sep 2026 without regon and registrationLegalDate) address is null, the town (when it can be read) is in city, and accountCount, privacy and — when there are accounts — accountCheck replace accountNumbers. A NIP outside the White List has only nip and found: false:

curl "https://skanfirmy.pl/nips/5260250995,7740001454?format=json"

More than VAT status, from one NIP

The /nip/{nip}?format=json response combines the VAT White List (the bl field) with KRS data (the krs field) in one response. If you also need REGON data or EU VAT-number validation, use the separate endpoints (all GET → JSON, no key):

  • /nip/{nip} — VAT status (VAT White List) + KRS + REGON (except for natural persons)
  • /nips/{list} — several NIPs at once (comma-separated)
  • /regon/{nip} — data from the REGON register (GUS) — see GUS (REGON) API
  • /vies/{country}/{number} — EU VAT number validation (VIES, European Commission) — see VIES API

KRS data pulled from the same NIP is covered by the KRS API, and REGON data by the GUS (REGON) API.

For AI agents, at https://skanfirmy.pl/mcp there is a Model Context Protocol (MCP) server with a sprawdz_nip tool (the same data, including the bl field) and sprawdz_rachunek (verify a specific bank account against the VAT White List), also with no key. A model-readable map of the endpoints is in llms.txt, and the full machine spec is in OpenAPI 3.1. For a lookup in the UI, use the VAT White List tool.

The public endpoint vs a typical paid Wykaz VAT API

Most commercial REST wrappers over the VAT White List run on a freemium model — an account, an API key and a daily request limit on the free tier. This endpoint is public and free. The differences in short:

Typical paid Wykaz VAT APIskanfirmy.pl /nip
API keyrequirednot needed
Registration / accountyesno
Request limitusually yes20 requests per 10 s per IP; no daily quota of our own (Ministry of Finance API daily limit — 503 once exhausted)
Response formatREST/JSONREST/JSON
MCP server for AI agentsusually noneyes
Scopeusually VAT status onlyVAT + KRS + REGON + VIES (separate endpoints)
Modelfreemium / subscriptionfree

This is not the official Wykaz VAT API — the data comes from the same source (the VAT White List kept by the Ministry of Finance), just exposed with a single GET request.

Where the data comes from, and what this is not

The data comes straight from the VAT White List (Biała Lista) kept by the Ministry of Finance. This is an independent tool; it is not the official Ministry API and is not affiliated with it — it only exposes that data in a more convenient form. The scope and freshness of the data match the VAT White List; the mfRequestDateTime timestamp says the moment the status was fetched from the Ministry.

Frequently asked questions

How do I check a company's VAT status by NIP in JSON?

Send a GET to https://skanfirmy.pl/nip/{NIP}?format=json and read the bl.statusVat field. It returns a Ministry of Finance literal: Czynny, Zwolniony or Niezarejestrowany. The same bl field also gives you the name, REGON, address (for natural persons, since 26 Sep 2026, only the town instead of the address, and since 27 Sep 2026 no REGON) and KRS number.

Do I need an API key or to sign up?

No. The /nip/{nip} endpoint is public — it returns JSON once you add ?format=json, with no API key and no account. We have no daily limit of our own, but the Ministry of Finance API has one — once it is exhausted, the endpoint answers 503.

Does the endpoint return the bank-account list from the White List?

For entities with a KRS number, yes: the bl.accountNumbers field contains the bank accounts (NRB) registered on the White List. For natural persons running a business, since 26 Sep 2026 we return only the number of accounts (bl.accountCount). To definitively confirm whether a specific account is on the list, use POST /rachunek, the MCP tool sprawdz_rachunek or the Bank account check tool in the UI.

What values does statusVat take and how should I compare them?

The statusVat field takes raw Ministry of Finance literals: Czynny, Zwolniony or Niezarejestrowany, and it is always kept verbatim. For branching you can use the derived statusVatCode enum (active/exempt/not_registered) or the vatActive boolean, both language-independent; if you compare against the raw literal, do not translate it before branching — translating would change the value your logic relies on.

Is it free to use?

Yes. The endpoint is free and requires no registration. The data comes straight from the VAT White List (Biała Lista) kept by the Ministry of Finance.