REGON Validation in Node.js
The Polish statistical business registry number — two lengths, two different weight sets, and an optional GUS BIR lookup. Here's how to validate it correctly.
In this guide
1. What is REGON?
REGON (Rejestr Gospodarki Narodowej) is the Polish National Business Registry Number assigned by GUS (Glowny Urzad Statystyczny — Central Statistical Office). Every legal entity and natural person conducting business activity in Poland receives a REGON number. It is used in:
- Tax filings and invoices
- Public procurement
- National Court Register (KRS) entries
- Social security (ZUS) registration
Two types exist:
- 9-digit REGON identifies the entire entity (company, NGO, sole proprietor)
- 14-digit REGON identifies a local unit (branch, office, factory) of an entity
| Entity | REGON | Type |
|---|---|---|
| Zaklad Ubezpieczen Spolecznych | 017344390 | 9-digit (entity) |
| ZUS Oddzial w Warszawie | 01734439000317 | 14-digit (local unit) |
2. Structure — 9-digit vs 14-digit
Both forms are purely numeric strings. Their internal structure determines how the check digit is computed:
| Form | Positions | Description |
|---|---|---|
| 9-digit | 1–8 | Base number |
| 9 | Check digit (computed over digits 1–8) | |
| 14-digit | 1–9 | Full 9-digit REGON of the parent entity |
| 10–13 | Local unit number | |
| 14 | Check digit (computed over digits 1–13) |
3. The MOD-11 checksum algorithm
REGON uses a weighted MOD-11 checksum. Each digit is multiplied by a fixed weight, the products are summed, and the remainder after dividing by 11 produces the check digit. There are two separate weight sets — one for the 9-digit form and another for the 14-digit form.
9-digit REGON
- Multiply each of the first 8 digits by its weight:
[8, 9, 2, 3, 4, 5, 6, 7] - Sum all products
- Calculate
sum mod 11 - If the remainder is 10, the check digit is 0; otherwise the check digit equals the remainder
- Compare the result with the 9th digit
14-digit REGON
- First, validate the embedded 9-digit prefix using the 9-digit algorithm above
- Then multiply each of the first 13 digits by weights:
[2, 4, 8, 5, 0, 9, 7, 3, 6, 1, 2, 4, 8] - Sum all products, compute
sum mod 11, same rule: if remainder is 10, check digit is 0 - Compare the result with the 14th digit
Here is a straightforward JavaScript implementation of both variants:
function validateRegon9(regon) { const d = regon.split('').map(Number); const weights = [8, 9, 2, 3, 4, 5, 6, 7]; const sum = weights.reduce((acc, w, i) => acc + d[i] * w, 0); const check = sum % 11 === 10 ? 0 : sum % 11; return check === d[8]; } function validateRegon14(regon) { if (!validateRegon9(regon.slice(0, 9))) return false; const d = regon.split('').map(Number); const weights = [2, 4, 8, 5, 0, 9, 7, 3, 6, 1, 2, 4, 8]; const sum = weights.reduce((acc, w, i) => acc + d[i] * w, 0); const check = sum % 11 === 10 ? 0 : sum % 11; return check === d[13]; }
?lookup=true parameter to check the GUS BIR registry.4. Worked example — step by step
Let's walk through the 9-digit REGON 123456785 to see the MOD-11 algorithm in action:
| Position | Digit | Weight | Product |
|---|---|---|---|
| 1 | 1 | 8 | 8 |
| 2 | 2 | 9 | 18 |
| 3 | 3 | 2 | 6 |
| 4 | 4 | 3 | 12 |
| 5 | 5 | 4 | 20 |
| 6 | 6 | 5 | 30 |
| 7 | 7 | 6 | 42 |
| 8 | 8 | 7 | 56 |
Sum = 8 + 18 + 6 + 12 + 20 + 30 + 42 + 56 = 192
192 mod 11 = 192 − 17 × 11 = 192 − 187 = 5
The remainder is 5, which is not 10, so the check digit is 5.
The 9th digit of 123456785 is 5 — it matches. The REGON is structurally valid.
5. Why format checks alone are not enough
A valid checksum does not mean the entity exists
Companies close, merge, or are deleted from the registry. The number remains mathematically valid — the MOD-11 checksum will still pass — but the entity behind it is gone. Relying solely on checksum validation gives you a false sense of confidence.
GUS BIR has the authoritative data
The Central Statistical Office maintains the official REGON registry through its BIR (Baza Internetowa REGON) system. Only a BIR lookup can confirm the entity's name, NIP, address, and whether business activity is still ongoing. The checksum tells you nothing about any of these.
14-digit REGONs add a second layer of complexity
A 14-digit REGON can have a valid checksum at both levels — the 9-digit prefix passes its own MOD-11 check, and the full 14-digit number passes its separate MOD-11 check — and still refer to a closed local unit. Double validation (structure + registry) is needed to be sure.
6. The right solution: one API call
The IsValid REGON API handles the full validation stack in a single GET request:
Format check
9 or 14 digits, all numeric
MOD-11 checksum
Correct weight set for 9-digit and 14-digit forms
GUS BIR lookup (optional)
Name, NIP, address, and activity status from the official registry
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: REGON Validation API docs →
7. Node.js code example
Using the built-in fetch API available in Node.js 18+:
// regon-validator.mjs const API_KEY = process.env.ISVALID_API_KEY; const BASE_URL = 'https://api.isvalid.dev'; async function validateRegon(regon, { lookup = false } = {}) { const url = new URL('/v0/pl/regon', BASE_URL); url.searchParams.set('value', regon); if (lookup) url.searchParams.set('lookup', 'true'); const response = await fetch(url, { headers: { Authorization: `Bearer ${API_KEY}` }, }); if (!response.ok) { throw new Error(`API error: ${response.status}`); } return response.json(); } // ── Basic validation ──────────────────────────────────────────────────────── const result = await validateRegon('123456785'); if (!result.valid) { console.log('Invalid REGON'); } else { console.log('Type:', result.type); // "entity" or "local-unit" } // ── With GUS BIR lookup ───────────────────────────────────────────────────── const detailed = await validateRegon('123456785', { lookup: true }); if (detailed.valid && detailed.regon?.found) { console.log('Name :', detailed.regon.name); console.log('NIP :', detailed.regon.nip); console.log('City :', detailed.regon.city); console.log('Street :', detailed.regon.street); }
lookup parameter is optional. Use it when you need the entity's name, NIP, or address. Omit it for fast checksum-only validation.8. cURL examples
Basic 9-digit validation:
curl -H "Authorization: Bearer YOUR_API_KEY" \ "https://api.isvalid.dev/v0/pl/regon?value=123456785"
With GUS BIR lookup:
curl -H "Authorization: Bearer YOUR_API_KEY" \ "https://api.isvalid.dev/v0/pl/regon?value=123456785&lookup=true"
14-digit local unit:
curl -H "Authorization: Bearer YOUR_API_KEY" \ "https://api.isvalid.dev/v0/pl/regon?value=12345678512347"
9. Understanding the response
Valid REGON (basic)
{ "valid": true, "type": "entity" }
Valid REGON (with lookup)
{ "valid": true, "type": "entity", "regon": { "checked": true, "found": true, "name": "EXAMPLE COMPANY SP. Z O.O.", "nip": "1234567890", "voivodeship": "MAZOWIECKIE", "district": "m. st. Warszawa", "community": "Srodmiescie", "city": "Warszawa", "postalCode": "00-001", "street": "ul. Przykladowa", "houseNumber": "10", "flatNumber": "5", "activityEndDate": null } }
Invalid REGON
{ "valid": false }
| Field | Type | Description |
|---|---|---|
| valid | boolean | Whether the REGON passed format and checksum validation |
| type | string | "entity" (9-digit) or "local-unit" (14-digit). Only present when valid: true |
| regon | object | GUS BIR lookup result. Only present when lookup=true and valid: true |
| regon.checked | boolean | Whether the GUS BIR API was successfully queried |
| regon.found | boolean | Whether the entity was found in the registry. Only present when checked: true |
| regon.name | string | Full legal name of the entity |
| regon.nip | string | NIP (tax identification number) |
| regon.voivodeship | string | Voivodeship (province) |
| regon.city | string | City |
| regon.postalCode | string | Postal code |
| regon.street | string | Street name |
| regon.houseNumber | string | Building number |
| regon.flatNumber | string | null | Apartment/unit number |
| regon.activityEndDate | string | null | Date when business activity ended (null if still active) |
10. Edge cases to handle
GUS BIR unavailable
The GUS BIR SOAP API has occasional downtimes. When lookup=true but the API is unreachable, the response includes regon.checked: false with reason: "unavailable". Your code should handle this gracefully.
const result = await validateRegon('123456785', { lookup: true }); if (result.valid && result.regon) { if (!result.regon.checked) { // GUS BIR was unreachable — checksum passed but no registry data console.warn('GUS BIR unavailable, retry later'); } else if (result.regon.found) { console.log('Entity:', result.regon.name); } else { console.log('Not found in the registry'); } }
Dissolved entities
A REGON that passes checksum validation may belong to a dissolved company. The activityEndDate field in the lookup response tells you when the entity stopped operating. Always check this field for KYC/onboarding flows.
const result = await validateRegon('123456785', { lookup: true }); if (result.valid && result.regon?.found) { if (result.regon.activityEndDate) { console.log('Entity dissolved on:', result.regon.activityEndDate); // Reject for onboarding — this company is no longer active } else { console.log('Entity is active'); } }
14-digit with valid prefix but invalid suffix
A 14-digit REGON where the first 9 digits pass the 9-digit checksum but the full 14-digit checksum fails. The API returns { valid: false } in this case — both checksums must pass for the number to be considered valid.
// 9-digit prefix is valid, but full 14-digit checksum fails const result = await validateRegon('12345678500000'); console.log(result.valid); // false
Leading zeros
REGONs can start with 0 (e.g. 017344390). Do not store them as integers — use strings to preserve leading zeros. Casting to a number would turn 017344390 into 17344390, which is only 8 digits and will fail validation.
// WRONG — loses the leading zero const regon = Number('017344390'); // 17344390 — 8 digits, invalid // CORRECT — preserve as string const regon = '017344390'; // 9 digits, ready for validation
Summary
See also
Try REGON validation instantly
Free tier includes 100 API calls per day. No credit card required. Checksum validation and optional GUS BIR lookup included.