NIS2Europe API Developer Portal

NIS2 Compliance API

Programmatic access to EU NIS2 compliance — scoping, gap analysis, incident templates, document packages — live for 23 EU countries in 21 languages. ES, FR, IE and NL are added the moment their national transposition acts enter into force: we never issue documents based on law that is not yet in force.

Base URL: https://api.nis2europe.eu

Quick Start

Get compliance information for your organisation in under 2 minutes:

# 1. Check NIS2 applicability for a 120-employee energy company in Germany
curl -X POST https://api.nis2europe.eu/v1/scoping \
  -H "X-API-Key: nis2_test_client_XXXX" \
  -H "Content-Type: application/json" \
  -d '{"tootajad":120,"sektor":"energia","kaive_meur":25,"riik":"de","keel":"en"}'

# 2. Get NIS2 → ISO 27001 framework mapping (lawyer_* and grc_platform roles)
curl https://api.nis2europe.eu/v1/framework-mapping \
  -H "X-API-Key: nis2_test_client_XXXX"

# 3. Browse the service catalogue with prices (all roles)
curl https://api.nis2europe.eu/v1/services \
  -H "X-API-Key: nis2_test_client_XXXX"
Endpoint roles differ. Every example above works for a grc_platform key. Some endpoints are role-restricted — e.g. POST /v1/incident-templates is available to all roles except grc_platform, and framework-mapping excludes client. Check the Auth column in the Endpoints table before calling; a disallowed role returns 403 FORBIDDEN_ROLE.
import requests

API_KEY = "nis2_test_client_XXXX"  # replace with your key
BASE = "https://api.nis2europe.eu"
headers = {"X-API-Key": API_KEY}

# 1. Scoping check
resp = requests.post(f"{BASE}/v1/scoping", headers=headers, json={
    "tootajad": 120, "sektor": "energia", "kaive_meur": 25, "riik": "de", "keel": "en"
})
result = resp.json()["data"]
print(result["klassifikatsioon"])  # "essential" | "important" | "valjas" (out of scope)

# 2. Framework mapping
mapping = requests.get(f"{BASE}/v1/framework-mapping", headers=headers).json()

# 3. Order a compliance document package
order = requests.post(f"{BASE}/v1/orders", headers=headers, json={
    "teenus": "pakett", "riik": "de"
}).json()["data"]
print(order["makselink"])  # Revolut payment link
const API_KEY = 'nis2_test_client_XXXX';
const BASE = 'https://api.nis2europe.eu';

// 1. Scoping check
const scope = await fetch(`${BASE}/v1/scoping`, {
  method: 'POST',
  headers: { 'X-API-Key': API_KEY, 'Content-Type': 'application/json' },
  body: JSON.stringify({ tootajad: 120, sektor: 'energia', kaive_meur: 25, riik: 'de', keel: 'en' })
}).then(r => r.json());
console.log(scope.data.klassifikatsioon);

// 2. All framework mappings
const maps = await fetch(`${BASE}/v1/framework-mapping`, {
  headers: { 'X-API-Key': API_KEY }
}).then(r => r.json());

Authentication

All API requests require an API key passed in the X-API-Key header.

Key prefixModeDescription
nis2_live_*LiveProduction key — real document generation, real payments via Revolut.
nis2_test_*SandboxTest key — realistic mock responses, no billing, no documents generated.

Get a key — self-service (immediate)

Sign up programmatically with POST /v1/signup (no key required). You get a working sandbox key immediately in the response, plus a Revolut payment link for your chosen licence; the live key is emailed the moment payment is confirmed. The same signup is also available as a form on each country's API page.

curl -X POST https://api.nis2europe.eu/v1/signup \
  -H "Content-Type: application/json" \
  -d '{"nimi":"Example GmbH","email":"dev@example.com","roll":"grc_platform","riik":"de","litsents":"enterprise"}'

# Response
{
  "data": {
    "staatus": "ootel_makset",           // live key pending payment
    "litsents": "enterprise",
    "sandbox_voti": "nis2_test_grc_platform_…",  // works immediately (mock mode) — shown ONCE
    "order_id": "a8396e58-…",
    "makselink": "https://checkout.revolut.com/…", // pay to activate the live key
    "hind_eur": 9900,
    "dokumentatsioon": "https://api.nis2europe.eu/developers/",
    "note": "Sandbox key works immediately (mock mode). The live API key is generated and emailed after payment is received."
  },
  "meta": { "powered_by": "AIPOS OÜ (reg. 16966532)", "request_id": "…" }
}
FieldRequiredNotes
nimiyesCompany or individual name (2–200 chars)
emailyesWhere the live key is sent after payment
rollyeslawyer_firm / lawyer_individual / client / grc_platform
litsentsnoprofessional (default) or enterprise
riiknoBilling country, ISO alpha-2 (drives VAT)
kmkrnoVAT number (reverse charge — see Pricing)
Signup is rate-limited to 5 requests/hour per IP. The sandbox key is enough to integrate and test every endpoint end-to-end before you pay.

Prefer email?

Alternatively, contact info@nis2europe.eu with your company name, role, and intended use case — we will issue a key manually. Self-service above is faster; the email route is there if you prefer a human.

Response envelope

Every response is wrapped in a consistent envelope:

{
  "data": { ... },        // endpoint-specific payload
  "meta": {
    "powered_by": "AIPOS OÜ (reg. 16966532)",
    "request_id": "550e8400-e29b-41d4-a716-446655440000",
    "environment": "sandbox"  // only for nis2_test_ keys
  }
}

Sandbox Mode

Sandbox keys (prefix nis2_test_) give you full API access without any billing or real document generation. Responses carry meta.environment: "sandbox".
  • All read endpoints (scoping, mapping, incident-templates, wizard) return real data from the knowledge base.
  • POST /v1/orders returns a single canonical mock order (order_id: "sandbox-demo-order-001") — no real Revolut payment link is created, no documents are generated. The mock is stateless, so both POST and GET return byte-identical data for the demo order.
  • GET /v1/orders/{order_id} returns the mock only for the demo id sandbox-demo-order-001; any other id returns 404 NOT_FOUND — so you can exercise your not-found handling in sandbox exactly as in production.
  • The mock's tulemus.dokumendid[].allalaadimislink returns a real watermarked SANDBOX SAMPLE PDF (HTTP 200, application/pdf) so you can test download handling end to end.
  • Sandbox keys auto-receive Professional-tier rate limits (300 req/min, 50 000 req/month).
  • Sandbox environments are clearly marked in all API responses and must not be used in production.

Sandbox order response

{
  "data": {
    "order_id": "sandbox-demo-order-001",
    "teenus": "pakett", "riik": "EU",   // fixed demo order (not country-specific)
    "makse_staatus": "tasutud",
    "dokumendi_staatus": "vabastatud",
    "tulemus": {
      "dokumendid": [{ "nimi": "NIS2_Riskijuhtimispoliitika_SANDBOX.md",
                       "allalaadimislink": "https://api.nis2europe.eu/v1/download/sandbox-token-demo", ... }],
      "sandbox_note": "SANDBOX: fixed demo order … live key runs a real, country-specific order."
    }
  },
  "meta": { "environment": "sandbox", "powered_by": "AIPOS OÜ (reg. 16966532)", ... }
}

API Endpoints

Base URL: https://api.nis2europe.eu  |  Current version: v1

MethodPathDescriptionAuth
POST/v1/signupSelf-service signup (sandbox key + payment link)None
POST/v1/scopingNIS2 applicability checkAll roles
GET/v1/scoping/questionsScoping question keysAll roles
GET/v1/framework-mappingAll NIS2 art 21(2) ↔ ISO/DORA/GDPRlawyer_*, grc_platform
GET/v1/framework-mapping/{key}Single measure mappinglawyer_*, grc_platform
POST/v1/incident-templates24h/72h/30d notification templatesAll except grc_platform
GET/v1/wizard/questionsGap-analysis questions (localised)All roles
POST/v1/wizard/evaluateGap-analysis evaluationAll roles
GET/v1/servicesService catalogue with pricesAll roles
POST/v1/ordersCreate order + payment linkAll roles
GET/v1/orders/{order_id}Order status + download linksAll roles
GET/v1/download/{token}Download document (token-based, no auth)Token
POST/v1/account/vatSet billing/VAT detailsAll roles
GET/v1/billing/summaryInvoices, subscription, usageAll roles
GET/v1/healthAPI health check (no auth)None
GET/v1/statusFull service health (no auth)None
GET/v1/changelogKB version changelog (no auth)None
Localisation. Endpoints with human-readable output accept a language selector — keel (query or body) on GET/most endpoints, language in the incident-templates body. This includes GET /v1/scoping/questions inputs, GET /v1/wizard/questions, POST /v1/wizard/evaluate, POST /v1/scoping, POST /v1/incident-templates, and GET /v1/services (e.g. GET /v1/services?keel=en returns English service names). Default language is Estonian unless a translation for the requested language is missing, in which case the field falls back to the default.

POST /v1/scoping — NIS2 Applicability

Deterministic classification: essential / important / valjas (out of scope). No LLM, <200ms.

Request

{
  "tootajad": 120,        // employees (required); size class is derived from this + turnover
  "sektor": "energia",    // NIS2 Annex I/II sector key — full list: GET /v1/scoping/questions
  "kaive_meur": 25,       // annual turnover, MEUR (optional)
  "riik": "de",           // ISO 3166-1 alpha-2 (23 live EU countries)
  "keel": "en"            // response language ("en" | "et" | "de" | "fi" | ...)
}

Response

{
  "data": {
    "klassifikatsioon": "important",  // "essential" | "important" | "valjas" (out of scope)
    "suurus": "keskmine",
    "sektor": "energia",
    "annex": "I",
    "pohjendused": ["A medium-sized entity in the sector 'Energy' (Annex I) -> IMPORTANT (Art 3(2))."],
    "sources": [
      { "seadus": "Directive (EU) 2022/2555", "paragrahv": "art 3(1)", "kb_versioon": "kb_2026-07-22_c109ef7f" }
    ]
  }
}

GET /v1/framework-mapping — Framework Cross-Mapping

NIS2 art 21(2) sub-measures (a–j) mapped to ISO 27001, DORA, and GDPR controls. Each control reference is an object {"viide", "nimetus"} (reference code + short name), not a bare string. The nimetus field of a measure is a per-language dictionary (22 languages).

Single measure — GET /v1/framework-mapping/{key}

{key} may be a letter (aj), an article form (art 21(2)(a), (a), 21(2)(a)), or a name fragment. Unknown key → 404 NOT_FOUND.

curl https://api.nis2europe.eu/v1/framework-mapping/a \
  -H "X-API-Key: nis2_test_client_XXXX"

# Response (nimetus + control lists truncated for readability)
{
  "data": {
    "voti": "a",
    "artikkel": "art 21(2)(a)",
    "nimetus": {                    // per-language dict (et, en, de, fi, … 22 langs)
      "et": "riskianalüüsi ja infosüsteemide turvalisuse poliitikad",
      "en": "policies on risk analysis and information system security"
    },
    "kaardistus": {                  // arrays of {viide, nimetus} objects — NOT bare strings
      "iso27001": [
        { "viide": "klausel 5.2",  "nimetus": "infoturbepoliitika" },
        { "viide": "klausel 6.1.2", "nimetus": "infoturberiski hindamine" }
      ],
      "dora": [ { "viide": "art 5",  "nimetus": "juhtimine ja korraldus" } ],
      "gdpr": [ { "viide": "art 24", "nimetus": "vastutava töötleja kohustused (riskipõhine)" } ]
    },
    "iso_sertifitseeritud": {         // what an ISO 27001-certified org already has vs the NIS2 gap
      "kate": "tugev",           // "tugev" (strong) | "osaline" (partial)
      "olemas": "ISMS-i riskihindamise metoodika, riskiregister, …",
      "vahe": "Kontrolli, et poliitika kohaldamisala kataks KÕIK NIS2 …"
    },
    "sources": [
      {
        "seadus": "direktiiv (EL) 2022/2555",
        "paragrahv": "art 21(2)(a)",
        "url": "https://eur-lex.europa.eu/legal-content/EN/TXT/?uri=CELEX:32022L2555",
        "seisukuupaev": "2023-01-16",
        "kb_versioon": "kb_2026-07-24_c109ef7f"
      }
    ]
  },
  "meta": { "powered_by": "AIPOS OÜ (reg. 16966532)", "request_id": "…" }
}
There is no kirjeldus field, and iso27001/dora/gdpr live under kaardistus as arrays of objects. To read a control code use data.kaardistus.iso27001[0].viide; the human-readable measure name is data.nimetus["en"].

All measures — GET /v1/framework-mapping

Returns every NIS2 art 21(2) sub-measure plus a certification baseline summary. Each item in meetmed[] has the exact same shape as the single-measure response above.

curl https://api.nis2europe.eu/v1/framework-mapping \
  -H "X-API-Key: nis2_test_client_XXXX"

# Response (truncated)
{
  "data": {
    "baseline": {
      "kokku": 10,                         // total measures a–j
      "tugev_kate": ["art 21(2)(a)", "art 21(2)(c)", "…"],   // strongly covered by ISO 27001
      "osaline_kate": ["art 21(2)(b)", "art 21(2)(g)", "…"], // partially covered
      "vahed": { "art 21(2)(a)": "Kontrolli, et poliitika …" }  // per-measure NIS2 gap
    },
    "meetmed": [
      { "voti": "a", "artikkel": "art 21(2)(a)", "nimetus": { "…" },
        "kaardistus": { "…" }, "iso_sertifitseeritud": { "…" }, "sources": [ "…" ] },
      { "voti": "b", "…" }
      // … 10 measures total (a–j)
    ]
  },
  "meta": { "powered_by": "AIPOS OÜ (reg. 16966532)", "request_id": "…" }
}

POST /v1/incident-templates — Notification Templates

Generate NIS2 art 23 incident notification text for all three stages (24h early warning, 72h incident notification, 30-day final report).

Request

{
  "detected_at": "2026-07-22T10:00:00Z",  // ISO 8601
  "country": "de",
  "language": "en",
  "incident_summary": "Ransomware encrypting ERP system"
}

Response (truncated)

{
  "data": {
    "phases": [
      {
        "phase": "early_warning",
        "deadline": "2026-07-23T10:00:00Z",
        "legal_basis": "Directive (EU) 2022/2555 art 23(1)",
        "template": "Early warning — 24 hours\n\n...",
        "sources": [ ... ]
      },
      { "phase": "incident_notification", ... },
      { "phase": "final_report", ... }
    ]
  }
}

POST /v1/wizard/evaluate — Gap Analysis

Free gap analysis against all 10 NIS2 art 21(2) security measures. Returns current compliance score and prioritised recommendations. All 20 measure keys (a1j2, retrievable from GET /v1/wizard/questions) are required; each answer is one of jah | osaliselt | ei (yes | partial | no). The response carries koondskoor out of max_skoor (40) plus a prioritised pingerida.

curl -X POST https://api.nis2europe.eu/v1/wizard/evaluate \
  -H "X-API-Key: nis2_test_client_XXXX" \
  -H "Content-Type: application/json" \
  -d '{"vastused":{"a1":"jah","a2":"osaliselt","b1":"jah","b2":"ei","c1":"jah","c2":"osaliselt","d1":"jah","d2":"ei","e1":"jah","e2":"osaliselt","f1":"jah","f2":"ei","g1":"jah","g2":"osaliselt","h1":"jah","h2":"ei","i1":"jah","i2":"osaliselt","j1":"jah","j2":"ei"},"keel":"en","riik":"de"}'
# All 20 keys a1..j2 required; each value is "jah" | "osaliselt" | "ei"
vastused = {k: "osaliselt" for k in [
    "a1","a2","b1","b2","c1","c2","d1","d2","e1","e2",
    "f1","f2","g1","g2","h1","h2","i1","i2","j1","j2"]}
resp = requests.post(f"{BASE}/v1/wizard/evaluate", headers=headers, json={
    "vastused": vastused, "keel": "en", "riik": "de"
})
data = resp.json()["data"]
print(data["koondskoor"], "/", data["max_skoor"], data["jargmine_samm"])

Orders — Document Packages

Order a legally-grounded NIS2 compliance document package. Payment via Revolut; documents released within 15 minutes of payment confirmation.

POST /v1/orders — create order

{
  "teenus": "pakett",    // "pakett" | "b_skann" | "yksikdokument" | "hooldus" | ...
  "riik": "de",          // target jurisdiction
  "idempotency_key": "your-unique-key-001"  // optional; prevents duplicates
}

Response includes makselink (Revolut hosted payment page). After payment, documents are generated and available via the tulemus.dokumendid[].allalaadimislink fields. Download links are time-limited (72 hours).

GET /v1/orders/{order_id} — poll status

makse_staatusdokumendi_staatusMeaning
ootelootelWaiting for payment
tasutudgenereerimiselPaid; generating documents
tasutudvabastatudDocuments ready; download links active

POST /v1/account/vat — VAT / Billing Details

Set your VAT number and billing address. VIES validation runs at invoice creation time.

{
  "kmkr": "DE123456789",      // VAT registration number
  "arveldus_riik": "de",     // billing country (ISO alpha-2)
  "arveldus_aadress": "Musterstraße 1, 10115 Berlin",
  "reg_kood": "HRB 12345"    // company register code
}

GET /v1/billing/summary — Usage & Invoices

Returns your orders, invoices (with download links), active subscription, and monthly API usage vs limit.

GET /v1/status — Service Health

Public endpoint (no auth). Returns overall health and per-service status.

{
  "data": {
    "overall": "operational",
    "timestamp": "2026-07-22T18:00:00Z",
    "teenused": {
      "api":      { "status": "ok" },
      "database": { "status": "ok" },
      "redis":    { "status": "ok" },
      "worker":   { "status": "ok" }
    }
  }
}

GET /v1/changelog — KB Version History

Returns the structured knowledge base changelog — which legal source changed and when.

curl https://api.nis2europe.eu/v1/changelog

Error Codes

All errors follow the envelope format: {"error": {"code": "...", "message": "...", "request_id": "...", "details": [ ... ]}}

Language. error.message is human-readable and defaults to English; it is returned in Estonian when the request sends ?keel=et or an Accept-Language: et header. error.code is a stable machine identifier and never changes with language — always branch on code, not message.
Structured details. Validation errors (VALIDATION_FAILED) include a structured error.details[] array — e.g. [{"field":"tootajad","type":"missing","msg":"Field required"}] — instead of a stringified Python object, so you can map errors to form fields programmatically. Other errors may also include details[] (e.g. the allowed roles for FORBIDDEN_ROLE).
HTTPcodeWhen
401UNAUTHORIZEDMissing, unknown, or blocked API key
403FORBIDDEN_ROLEYour role is not permitted for this endpoint
404NOT_FOUNDResource does not exist (order, file, measure key)
409IDEMPOTENCY_CONFLICTDuplicate idempotency_key with different payload
422VALIDATION_FAILEDInvalid request body or parameters
429RATE_LIMITEDToo many requests; check Retry-After header
500INTERNAL_ERRORUnexpected server error; contact support
503UPSTREAM_UNAVAILABLEDependent service temporarily unavailable

On rate limiting, the response includes headers: X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, Retry-After.

Rate Limits

TierReq/minReq/month
Baas (no subscription)602 000
Professional (3 900 EUR/mo)30050 000
Enterprise (9 900 EUR/mo)1 200500 000
Sandbox (nis2_test_*)30050 000

Every response includes X-RateLimit-* headers so you can track consumption programmatically. Each authenticated API key is tracked independently.

Pricing

All prices are net EUR. Full packages include legally-grounded documents in the target country's official language, signed by a local partner lawyer.
ServicePrice (EUR net)
Compliance Document Package8 900 – 17 900 (R1–R4)
Annual Maintenance2 900/month or 19 000/year prepaid
B-Scan (gap assessment)4 900 – 9 900 (R1–R4)
Framework mapping / single document / incident1 490
Supply-chain attestation2 900 – 5 900 (R1–R4)
Re-sign (document update)1 900 – 3 900 (R1–R4)
Combo discountmax 10 %
API licence Professional3 900 / month
API licence Enterprise9 900 / month

What the licence includes — and what it does not

Without licenceProfessionalEnterprise
API access: scoping, gap analysis, incident templates, framework-mapping queries (rate limit + monthly request quota)IncludedIncluded — higher quota, SLA, priority queue
Documents at 1 490 (framework-mapping document, incident-notification template pack, single document)1 490 each745 each for up to 10 documents/month; thereafter 1 490 each745 each for up to 30 documents/month; thereafter 1 490 each
Larger products (document package, B-scan, supply-chain attestation, re-sign, maintenance)Always full price — regardless of licence
The licence includes no documents free of charge or without limit. Every document is always a separate order with separate prepayment — the licence discount changes only the unit price (745 instead of 1 490) up to the monthly quota; beyond it, orders simply revert to full price. Example: 10 framework-mapping documents per month on a Professional licence cost 10 × 745 = 7 450 EUR instead of 10 × 1 490 = 14 900 EUR — a saving of 7 450 EUR per month at full quota. The discount quota (10 on Professional, 30 on Enterprise) resets at the start of each calendar month (UTC) — not on a rolling 30-day window.
VAT. All prices are net (excluding VAT); invoices are issued by AIPOS OÜ (Estonia). Estonian clients: +24% Estonian VAT. Business clients in another EU country with a valid VAT number (VIES-verified): reverse charge — 0% on the invoice, you account for VAT in your own country (Art. 196 of the VAT Directive); without a valid VAT number, Estonian VAT of 24% is added. Clients outside the EU: no Estonian VAT (export of services, 0%).

Payment rule: All orders are prepaid via Revolut to AIPOS OÜ. Documents are released only after payment confirmation. No credit terms.

→ SLA & versioning policy