Norwegian VAT validation: how brreg and MVA work
Norway's VAT (MVA) system, org-number format, MOD-11 checksum, and the public brreg REST API. No auth required, but watch the deprecation notices.
TL;DR
- Norwegian VAT is called MVA (merverdiavgift), standard rate 25%.
- VAT numbers are the 9-digit org-number with an optional
MVAsuffix appended for display. - The Brønnøysundregistrene (brreg) REST API is fully public, no authentication required.
- vatverify wraps brreg, normalizes the
MVAsuffix, and caches responses.
@vatverify/node (used in the Node example below) ships on npm at the API launch. Reading this before launch? The REST endpoint is live today. Swap any vat.validate({ vat_number }) call for fetch('https://api.vatverify.dev/v1/validate?vat_number=...') with an Authorization: Bearer header. The Python and cURL snippets on this page work today without any package install.
What is MVA?
MVA (merverdiavgift) is Norway's value-added tax. Standard rate is 25%. Reduced rates apply to specific categories:
| Category | Rate |
|---|---|
| Food | 15% |
| Passenger transport, cinema, hotels | 12% |
| Certain cultural / voluntary sector | 0% |
Norway is not an EU member, but it is in the European Economic Area (EEA). Norwegian VAT is entirely separate from VIES. There is no cross-registry lookup. You must query brreg directly (or via vatverify) for any NO-prefixed number.
Org-number format
Norwegian businesses are identified by a 9-digit organisasjonsnummer (org-number). The last digit is a MOD-11 check digit. For display purposes, a VAT-registered business appends MVA (or mva) to the org-number, but this suffix carries no additional information. It just signals VAT registration status. Examples:
NO123456789: international formatNO123456789MVA: international format with VAT suffix123456789: domestic format (no prefix)
vatverify normalizes all three to the 9-digit core before the lookup.
MOD-11 checksum (Norway variant)
The Norwegian MOD-11 uses a specific weight sequence applied to the first 8 digits, with the 9th digit acting as the check digit:
function noMod11(digits: string): boolean {
if (digits.length !== 9) return false;
const weights = [3, 2, 7, 6, 5, 4, 3, 2];
const sum = digits.slice(0, 8).split('').reduce((a, d, i) => a + Number(d) * weights[i], 0);
const check = (11 - (sum % 11)) % 11;
return check === Number(digits[8]);
}The weights cycle through 3, 2, 7, 6, 5, 4, 3, 2 for positions 1–8. You multiply each digit by its weight, sum the products, subtract the result from 11 modulo 11, and compare against the 9th digit. If the check remainder is 10, no valid check digit exists and the number is structurally invalid. @vatverify/vat-rates implements this algorithm for offline validation before any upstream request is made.
brreg REST API
The Brønnøysundregistrene provides a public REST API for the Entity Register (Enhetsregisteret):
https://data.brreg.no/enhetsregisteret/api/enheter/{org_nummer}No authentication, no API key. It is an open government dataset. Responses are JSON. The key fields for VAT validation are:
registrertIMvaregisteret: boolean,trueif the business is VAT-registeredorganisasjonsform.kode: the entity type code (e.g.,ASfor limited company,ENKfor sole proprietor)navn: the registered business name
Note: the brreg API response uses registrertIMvaregisteret (Norwegian spelling; mvaregisteret is one word meaning "the VAT register"). vatverify exposes this as data.valid in its unified response so you don't need to remember the Norwegian field name.
Sample request/response
import { Vatverify } from '@vatverify/node';
const vat = new Vatverify(process.env.VATVERIFY_API_KEY!);
const result = await vat.validate({ vat_number: 'NO123456789MVA' });
// result.data.valid → true
// result.data.company?.name → "Example AS"
// result.data.country.code → "NO"import os
import httpx
response = httpx.get(
"https://api.vatverify.dev/v1/validate",
params={"vat_number": "NO123456789MVA"},
headers={"Authorization": f"Bearer {os.environ['VATVERIFY_API_KEY']}"},
)
result = response.json()
# result["data"]["valid"] → True
# result["data"]["company"]["name"] → "Example AS"
# result["data"]["country"]["code"] → "NO"curl "https://api.vatverify.dev/v1/validate?vat_number=NO123456789MVA" \
-H "Authorization: Bearer $VATVERIFY_API_KEY"Example response:
{
"data": {
"valid": true,
"vat_number": "NO123456789",
"country": { "code": "NO", "name": "Norway" },
"company": {
"name": "Example AS",
"address": "Exempelgata 1, 0001 Oslo"
},
"verified_at": "2026-04-14T15:42:03.451Z"
},
"meta": {
"request_id": "0190f8ea-a5b2-7000-a123-000000000000",
"cached": false,
"source": "brreg",
"source_status": "live",
"latency_ms": 312
}
}VAT-registered vs registered business
A business can be registered in the Entity Register (Enhetsregisteret) without being in the VAT Register (Merverdiavgiftsregisteret). Registration in the Entity Register is mandatory for all businesses above a minimal size; VAT registration only applies if annual turnover exceeds NOK 50,000. vatverify checks registrertIMvaregisteret specifically. A business that exists in brreg but has registrertIMvaregisteret: false returns data.valid: false.
EORI adjacency
Norwegian EORI numbers (Economic Operators Registration and Identification) are sometimes confused with MVA numbers because they share the same 9-digit org-number core. An EORI number for a Norwegian business looks like NO123456789, identical to the VAT number. The difference is context and registry: EORI is administered by Norwegian Customs (Tolldirektoratet), not brreg. If you need EORI verification, that is a separate lookup; vatverify's validate endpoint covers VAT (MVA) registration only.
Rate limits
brreg's official documentation states a limit of 60 requests per second. Anecdotally the API tolerates bursts above 100 req/sec without throttling, but 60 req/sec is the safe planning assumption if you are hitting it directly. With vatverify, cached lookups never reach brreg. Only cache misses generate upstream requests.
FAQ
Do I need a Brønnøysundregistrene account?
No. The brreg Enhetsregisteret API is a fully public open-government dataset. No registration, no API key, no rate-limit agreement required. You can hit it directly from any IP. vatverify still wraps it because caching, normalization, and the unified response schema are worth having even for a public endpoint.
What's the difference between the 9-digit org-number and the MVA org-number?
They are the same 9 digits. MVA is a display-only suffix that signals "this org-number is VAT-registered." It carries no additional digits or encoded information. Stripping MVA from NO123456789MVA gives you NO123456789, the same number, valid for both Entity Register and VAT Register lookups.
Is brreg ever down?
Rarely. Brønnøysundregistrene maintains ≥99.95% uptime on the public API and publishes a status page at status.brreg.no. Planned maintenance windows are announced in advance and are infrequent. vatverify returns cached results with meta.source_status: "degraded" in the rare event of an outage.
Does the API cover EORI?
Partially. brreg can confirm that an org-number is registered, which is a prerequisite for EORI. But EORI validation itself (confirming the EORI number is active in the customs system) requires a separate lookup at Tolldirektoratet. EORI is not a VAT concern. It is a customs identification. vatverify's validate endpoint covers MVA (VAT) registration only.
What about sole proprietors (ENK)?
Sole proprietors (Enkeltpersonforetak, entity type code ENK) receive the same 9-digit org-number format as any other entity. Many sole proprietors are below the NOK 50,000 VAT registration threshold and are therefore not VAT-registered. If you validate an ENK org-number and the business has not crossed the threshold, you will get data.valid: false. The company exists, but is not a VAT taxpayer.