Guide · Node.js · SDK · Poland

CEIDG Validation in Node.js

The Polish registry for sole proprietors and civil partnerships — validated by NIP checksum, confirmed by a live CEIDG lookup returning owner name, address, status, and PKD activity codes.

1. What is CEIDG?

CEIDG (Centralna Ewidencja i Informacja o Działalności Gospodarczej — Central Registration and Information on Business Activity) is the official Polish government registry for:

  • Sole proprietors (JDG — jednoosobowa działalność gospodarcza)
  • Civil partnerships (spółki cywilne) — each partner registers separately

CEIDG does not cover companies incorporated under commercial law (sp. z o.o., S.A., sp. k., etc.) — those are registered in the KRS (National Court Register). If you receive a NIP and do not know the entity type, start with CEIDG; if not found, fall back to KRS.

CEIDG is maintained by the Ministry of Economic Development and is publicly accessible. It stores:

  • Owner's first and last name
  • Business name (if different from the owner's name)
  • Registration status (active / suspended / deleted)
  • Registered business address
  • PKD activity codes (Polish Classification of Activities, based on NACE Rev. 2)
  • REGON statistical number and NIP tax number
  • Business commencement date

2. NIP — the key identifier

CEIDG entries are identified by NIP (Numer Identyfikacji Podatkowej — Tax Identification Number), the Polish equivalent of a VAT/tax ID. A NIP is:

  • Exactly 10 digits, no letters
  • Written with hyphens as NNN-NNN-NN-NN (e.g. 526-104-08-28)
  • Protected by a MOD-11 weighted checksum on the last digit
NIPEntity
526-104-08-28ORACLE POLSKA SP. Z O.O. (KRS company)
987-654-32-10Sole proprietor (CEIDG entry)
ℹ️The same NIP is used for both KRS companies and CEIDG sole proprietors. The NIP format and checksum are identical — only the registry lookup reveals which category the entity belongs to.

3. The NIP MOD-11 checksum algorithm

The NIP checksum uses a weighted MOD-11 algorithm. The first 9 digits are each multiplied by a fixed weight, the products are summed, and the result modulo 11 must equal the 10th (check) digit. If the remainder is 10, the number is always invalid.

Position12345678910
Weight657234567check
function nipValid(nip) {
  const digits = nip.replace(/[\-\s]/g, '').split('').map(Number);
  if (digits.length !== 10) return false;
  const weights = [6, 5, 7, 2, 3, 4, 5, 6, 7];
  const sum = weights.reduce((acc, w, i) => acc + digits[i] * w, 0);
  const check = sum % 11;
  return check !== 10 && check === digits[9];
}
⚠️If sum % 11 === 10, the NIP is always structurally invalid — no valid check digit exists for that combination. This is by design in the Polish NIP specification.

4. Worked example — step by step

Let's verify NIP 526-104-08-28 (digits: 5261040828):

PositionDigitWeightProduct
15630
22510
36742
4122
5030
64416
7050
88648
92714

Sum = 30 + 10 + 42 + 2 + 0 + 16 + 0 + 48 + 14 = 162

162 mod 11 = 162 − 14 × 11 = 162 − 154 = 8

The remainder is 8 (not 10), so the check digit must be 8.

The 10th digit of 5261040828 is 8 — it matches. The NIP is valid.


5. Why format checks alone are not enough

A valid NIP checksum does not mean the business is active

Sole proprietors can suspend or close their business. The NIP remains valid and the MOD-11 checksum still passes — but the CEIDG status will be suspended or deleted. For any onboarding or compliance use case, you need the live registry status.

You need the owner's name and address for KYC

The NIP alone identifies a tax entity but tells you nothing about the person behind it. A CEIDG lookup returns the owner's first and last name, business address, and registration date — all required for customer due diligence (CDD) and Know Your Customer (KYC) flows.

PKD codes define what the business is allowed to do

Polish sole proprietors register the specific PKD (Polska Klasyfikacja Działalności) activity codes under which they operate. For regulated industries — financial services, healthcare, transport — you may need to verify that the supplier or partner holds the correct PKD codes before contracting with them.


6. The right solution: one API call

The IsValid CEIDG API handles the full validation and lookup stack in a single GET request:

1

Format check

Exactly 10 digits, strips hyphens and spaces

2

MOD-11 checksum

Weights [6,5,7,2,3,4,5,6,7], mod 11 must equal the 10th digit

3

CEIDG registry lookup (optional)

Owner name, business name, address, status, REGON, start date, and PKD codes from the official government API

Get your free API key at isvalid.dev. The free tier includes 100 calls per day with no credit card required.

Full parameter reference and response schema: CEIDG API docs →


7. Node.js code example

Using the @isvalid-dev/sdk package or the built-in fetch API (Node.js 18+):

// ceidg-validator.mjs
import { createClient } from '@isvalid-dev/sdk';

const iv = createClient({ apiKey: process.env.ISVALID_API_KEY });

// ── NIP checksum validation only ────────────────────────────────────────────
const result = await iv.pl.ceidg('9876543210');

if (!result.valid) {
  console.log('Invalid NIP');
} else {
  console.log('NIP:', result.nip); // "987-654-32-10"
}

// ── With CEIDG registry lookup ───────────────────────────────────────────────
const detailed = await iv.pl.ceidg('9876543210', { lookup: true });

if (detailed.valid && detailed.ceidg?.found) {
  const c = detailed.ceidg;
  console.log('Owner    :', c.firstName, c.lastName);
  console.log('Business :', c.businessName);
  console.log('Status   :', c.status);      // "active" | "suspended" | "deleted"
  console.log('City     :', c.city);
  console.log('PKD      :', c.primaryPkd);  // e.g. "70.22.Z"
  console.log('All PKD  :', c.pkd);         // ["70.22.Z", "74.90.Z", ...]
}
The lookup parameter is optional. Omit it for fast checksum-only validation (e.g. validating a form field). Add it when you need to confirm the entity is active and retrieve owner details.

8. cURL examples

NIP checksum validation only:

curl -H "Authorization: Bearer YOUR_API_KEY" \
  "https://api.isvalid.dev/v0/pl/ceidg?value=9876543210"

With CEIDG registry lookup:

curl -H "Authorization: Bearer YOUR_API_KEY" \
  "https://api.isvalid.dev/v0/pl/ceidg?value=9876543210&lookup=true"

Hyphens are accepted and stripped automatically:

curl -H "Authorization: Bearer YOUR_API_KEY" \
  "https://api.isvalid.dev/v0/pl/ceidg?value=987-654-32-10"

9. Understanding the response

Valid NIP (checksum only)

{
  "valid": true,
  "nip": "987-654-32-10"
}

Valid NIP with CEIDG lookup

{
  "valid": true,
  "nip": "987-654-32-10",
  "ceidg": {
    "checked": true,
    "found": true,
    "status": "active",
    "firstName": "JAN",
    "lastName": "KOWALSKI",
    "businessName": "JK CONSULTING JAN KOWALSKI",
    "regon": "146123456",
    "city": "Warszawa",
    "postalCode": "00-001",
    "street": "Marszałkowska",
    "houseNumber": "1",
    "flatNumber": null,
    "startDate": "2015-03-01",
    "pkd": ["70.22.Z", "74.90.Z", "63.11.Z"],
    "primaryPkd": "70.22.Z"
  }
}

NIP not found in CEIDG

{
  "valid": true,
  "nip": "526-104-08-28",
  "ceidg": {
    "checked": true,
    "found": false
  }
}

Invalid NIP

{ "valid": false }
FieldTypeDescription
validbooleanNIP passes format and MOD-11 checksum
nipstringNIP in canonical NNN-NNN-NN-NN format. Present only when valid: true
ceidg.checkedbooleanWhether the CEIDG API was successfully queried
ceidg.foundbooleanWhether the entity was found in CEIDG
ceidg.statusstringactive, suspended, or deleted
ceidg.firstName / lastNamestring | nullOwner's full name
ceidg.businessNamestring | nullRegistered business name (may differ from owner name)
ceidg.pkdstring[]All registered PKD activity codes
ceidg.primaryPkdstring | nullPrimary (predominant) PKD activity code

10. Edge cases to handle

Entity found but status is suspended or deleted

The NIP is valid and the entity exists in CEIDG, but the business is no longer active. Always check ceidg.status before onboarding.

const result = await validateCeidg(nip, { lookup: true });

if (result.valid && result.ceidg?.found) {
  if (result.ceidg.status !== 'active') {
    // Business is suspended or deleted — do not onboard
    return {
      status: 'rejected',
      reason: `CEIDG status: ${result.ceidg.status}`,
    };
  }
  // Proceed with onboarding
}

NIP not found in CEIDG — try KRS

A valid NIP that is not in CEIDG likely belongs to a KRS company (sp. z o.o., S.A., etc.) or a non-business entity (e.g. a state institution). Fall back to the VAT or KRS endpoints.

const ceidg = await validateCeidg(nip, { lookup: true });

if (ceidg.valid && ceidg.ceidg?.checked && !ceidg.ceidg.found) {
  // Not a sole proprietor — try VAT/VIES for companies
  const vat = await iv.vat(`PL${nip}`, { checkVies: true });
  if (vat.valid && vat.vies?.valid) {
    console.log('KRS company confirmed via VIES:', vat.vies.name);
  }
}

CEIDG API unavailable

When ceidg.checked is false, the CEIDG government API could not be reached. The NIP passed checksum validation, but no registry data is available. Handle this gracefully — accept provisionally and re-check.

const result = await validateCeidg(nip, { lookup: true });

if (result.valid && result.ceidg && !result.ceidg.checked) {
  // CEIDG API temporarily unavailable
  await scheduleRecheck(nip, '1h');
  return { status: 'provisional', reason: 'ceidg_unavailable' };
}

NIP with hyphens or spaces

Users and systems often enter NIPs in various formats. The API strips hyphens and spaces before validation, so all these forms are equivalent:

// All of these are accepted and produce the same result:
await validateCeidg('9876543210');
await validateCeidg('987-654-32-10');
await validateCeidg('987 654 32 10');

Summary

Validate the NIP MOD-11 checksum before any registry call — it catches typos instantly
Use lookup=true to confirm the entity is active — a valid NIP checksum does not prove the business exists
Check ceidg.status — suspended and deleted entries still return found: true
Check ceidg.primaryPkd for regulated industries — verify the entity holds the required activity codes
Do not assume "not found in CEIDG" means invalid — KRS companies use a different registry
Do not block on ceidg.checked: false — accept provisionally and re-check when the API recovers

See also

Try CEIDG validation instantly

Free tier includes 100 API calls per day. No credit card required. NIP checksum validation and optional CEIDG registry lookup included.