WKN Validation in Node.js
The Wertpapierkennnummer is Germany's native securities identifier — 6 alphanumeric characters, no check digit, embedded in every German ISIN. Here's how to validate WKNs correctly in Node.js and enrich them with live instrument data.
In this guide
1. What is a WKN?
A WKN (Wertpapierkennnummer — also written WPKN or WPK) is a 6-character alphanumeric identifier assigned to securities traded in Germany. It has been used since 1968 and is issued by WM Datenservice (formerly Wertpapier-Mitteilungen), the Frankfurt-based financial data provider that acts as the National Numbering Agency (NNA) for German securities under ISO 6166.
WKNs appear in German brokerage confirmations, bank statements, and legacy trading systems. While ISINs have become the standard in cross-border and regulatory contexts (MiFID II, EMIR), WKNs remain widely used in the German retail and private banking segment. Many German financial APIs and data feeds still expose WKNs alongside or instead of ISINs.
Unlike ISIN, CUSIP, or SEDOL, the WKN has no check digit. It is a purely opaque sequence — there is no algorithm to verify whether a WKN is valid. The only way to confirm a WKN maps to a real security is to look it up in a reference database.
2. WKN anatomy
A WKN is exactly 6 characters long and consists of digits and uppercase letters:
| Property | Detail |
|---|---|
| Length | 6 characters |
| Allowed characters | [A-Z0-9] |
| Check digit | None |
| Issuing authority | WM Datenservice, Frankfurt |
| Scope | German securities (equities, bonds, funds, warrants, ETFs) |
Some well-known examples:
| WKN | ISIN | Security |
|---|---|---|
| 514000 | DE0005140008 | Deutsche Bank AG |
| 716460 | DE0007164600 | SAP SE |
| 723610 | DE0007236101 | Siemens AG |
| 840400 | DE0008404005 | Allianz SE |
3. WKN inside a German ISIN
German ISINs (country code DE) embed the WKN in a predictable way. The NSIN (National Securities Identifying Number) portion of a German ISIN is the WKN zero-padded on the left to 9 characters:
| Part | Chars | Example |
|---|---|---|
| Country code | 1–2 | DE |
| NSIN (WKN padded to 9) | 3–11 | 000514000 |
| ISIN check digit | 12 | 8 |
For Deutsche Bank (WKN 514000):
DE + 000514000 + 8 = DE0005140008 ┌──┐ ┌───┐┌──┐ ┌┐ │DE│ │000││514000│ │8│ ← ISIN check digit (Luhn over expanded string) └──┘ └───┘└──────┘ └┘ ↑ ↑ country WKN zero-padded to 9 chars
You can extract a WKN from a German ISIN by taking characters 5–10 (1-indexed): strip the leading zeros from the NSIN to recover the original WKN. If you already have a DE ISIN, pass it to /v0/isin instead of /v0/wkn.
4. Why format checks aren't enough
No check digit — any 6-char string passes format validation
Because WKN has no check digit, every 6-character alphanumeric string is structurally valid. A typo like 514001 instead of 514000 passes a regex check but refers to a completely different security — or no security at all. Without a database lookup, there is no way to detect the error.
Retired and reassigned WKNs
When a company is delisted, merges, or undergoes a corporate restructuring, its WKN may be retired or replaced. A structurally valid WKN might point to a company that no longer exists, a fund that was wound up, or a warrant that expired. Format validation cannot distinguish an active security from a retired one.
No public WKN registry
WM Datenservice does not publish a free, downloadable list of all valid WKNs. The database is a commercial product. Maintaining your own mapping table is impractical — an API backed by OpenFIGI is the standard alternative for applications that need to verify WKNs against real securities.
Alphanumeric vs. numeric — know your source system
Some legacy German banking systems pad WKNs with leading zeros to reach 6 digits, treating them as numeric codes. Others pass them without padding. If your data arrives with inconsistent formatting, normalise before validating: strip whitespace, uppercase, and ensure exactly 6 characters.
5. The right solution: one API call
The IsValid WKN API handles format validation and instrument lookup via OpenFIGI in a single GET /v0/wkn request. Because there is no check digit to compute, the value is entirely in the enrichment: confirming the WKN maps to a real security and surfacing its name, ISIN, exchange, and FIGI.
Get your free API key at isvalid.dev. The free tier includes 100 calls per day.
Full parameter reference and response schema: WKN Validation API docs →
6. Node.js code example
Using the @isvalid-dev/sdk package or the native fetch API (Node 18+).
// wknValidator.js import { createClient } from '@isvalid-dev/sdk'; const iv = createClient({ apiKey: process.env.ISVALID_API_KEY }); // ── Example usage ───────────────────────────────────────────────────────────── const result = await iv.wkn('514000'); // Deutsche Bank AG if (!result.valid) { console.log('Invalid WKN: wrong format (must be 6 alphanumeric characters)'); } else { console.log(`WKN : ${result.wkn}`); if (result.found) { console.log(`Name : ${result.name}`); console.log(`ISIN : ${result.isin}`); console.log(`Ticker : ${result.ticker}`); console.log(`Exchange : ${result.exchCode}`); console.log(`FIGI : ${result.figi}`); console.log(`Sector : ${result.marketSector}`); } }
Expected output for 514000:
WKN : 514000 Name : Deutsche Bank AG ISIN : DE0005140008 Ticker : DBK Exchange : GY FIGI : BBG000BC5588 Sector : Equity
In an Express.js route handler:
// routes/securities.js (Express) app.get('/securities/validate-wkn', async (req, res) => { const { wkn } = req.query; if (!wkn) { return res.status(400).json({ error: 'Missing wkn parameter' }); } let result; try { result = await validateWkn(wkn); } catch { return res.status(502).json({ error: 'WKN validation service unavailable' }); } if (!result.valid) { return res.status(400).json({ error: 'Invalid WKN', wkn }); } res.json({ wkn: result.wkn, found: result.found ?? null, isin: result.isin ?? null, name: result.name ?? null, ticker: result.ticker ?? null, exchCode: result.exchCode ?? null, figi: result.figi ?? null, }); });
514 000 and 514000 are handled correctly.7. cURL example
Deutsche Bank AG:
curl -H "Authorization: Bearer YOUR_API_KEY" \ "https://api.isvalid.dev/v0/wkn?value=514000"
SAP SE:
curl -H "Authorization: Bearer YOUR_API_KEY" \ "https://api.isvalid.dev/v0/wkn?value=716460"
Invalid format — fewer than 6 characters:
curl -H "Authorization: Bearer YOUR_API_KEY" \ "https://api.isvalid.dev/v0/wkn?value=5140"
8. Understanding the response
Response for a valid WKN found in OpenFIGI:
{ "valid": true, "wkn": "514000", "found": true, "dataSource": "openfigi", "isin": "DE0005140008", "name": "Deutsche Bank AG", "ticker": "DBK", "exchCode": "GY", "securityType": "Common Stock", "marketSector": "Equity", "figi": "BBG000BC5588", "compositeFIGI": "BBG000BC5588" }
Response for a valid WKN not found in OpenFIGI:
{ "valid": true, "wkn": "514000", "found": false }
Response for an invalid WKN:
{ "valid": false }
| Field | Type | Description |
|---|---|---|
| valid | boolean | Format validation result (6-char [A-Z0-9]) |
| wkn | string | Normalised (uppercased, whitespace-stripped) WKN |
| found | boolean | null | true — found in OpenFIGI; false — not found; null — OpenFIGI unavailable |
| dataSource | string | Always openfigi when present |
| isin | string | null | Associated ISIN (e.g. DE0005140008); null if not returned |
| name | string | null | Full instrument name |
| ticker | string | null | Exchange ticker symbol |
| exchCode | string | null | Bloomberg exchange code (e.g. GY for XETRA) |
| securityType | string | null | Security type (e.g. Common Stock) |
| marketSector | string | null | Market sector (e.g. Equity) |
| figi | string | null | Financial Instrument Global Identifier |
| compositeFIGI | string | null | Composite FIGI across all exchanges |
9. Edge cases to handle
found: null vs. found: false
found: false means OpenFIGI was reachable but did not recognise the WKN. found: null means the lookup failed (timeout, rate limit, or network error). In the null case the WKN is still structurally valid — treat it as inconclusive and retry rather than rejecting it.
WKN vs. ISIN — know which you have
If you receive a 12-character code starting with DE, it is a German ISIN — use /v0/isin instead. A 6-character code is a WKN; use /v0/wkn. You can extract the WKN from a German ISIN by taking characters 5–10 and stripping leading zeros.
Multiple listings per WKN
OpenFIGI may return a result for the primary listing on XETRA (exchange code GY). The same security may also trade on Frankfurt Stock Exchange, regional German exchanges, or as a depositary receipt elsewhere. The API returns the first matching result; use the ISIN from the response to look up all listings via /v0/isin if you need the complete picture.
10. Summary
| What | Detail |
|---|---|
| Format | 6 alphanumeric characters [A-Z0-9], no check digit |
| Check algorithm | None — purely opaque sequence |
| Issuing authority | WM Datenservice, Frankfurt |
| Coverage | German securities (equities, bonds, ETFs, warrants, structured products) |
| Enrichment source | OpenFIGI (Bloomberg) via ID_WERTPAPIER |
| API endpoint | GET /v0/wkn?value=514000 |
For cross-referencing, see also:
- ISIN validation — ISINs embed WKNs for DE-prefixed codes
- SEDOL validation — the UK equivalent
- CUSIP validation — the North American equivalent
- WKN Validation in Python
Node.js integration notes
In a TypeScript codebase, define a branded type to prevent WKNs from being confused with other 6-character strings at compile time: type Wkn = string & { readonly __brand: 'Wkn' }. Validate at the system boundary — when the WKN enters your application from user input, an upstream API, or a file import — and carry the branded type through the rest of your pipeline.
WKN validation is particularly relevant in German banking integrations: online brokerage order entry, portfolio management dashboards, and wealth management reporting tools. If your application accepts WKNs from users, the absence of a check digit means any plausible-looking 6-character string passes format validation. Pair the API call with client-side pattern matching (/^[A-Z0-9]{6}$/i) for instant UI feedback, and confirm against OpenFIGI on submission.
Caching strategy
WKN-to-instrument mappings change infrequently — corporate actions aside, a given WKN refers to the same security for years. Cache validated results in Redis with a 24-hour TTL using the normalised WKN as the key. The IsValid API already caches OpenFIGI responses for 24 hours on its end; your own cache layer eliminates the network round trip for repeated lookups of the same identifier within a session.
- Assert
process.env.ISVALID_API_KEYat server startup, not lazily at first request - Use
Promise.allSettled()for batch WKN validation — it collects all results without aborting on the first failure - Log the full parsed API response alongside the raw WKN — the ISIN and FIGI returned are useful for downstream reconciliation
- If you need the full ISIN from a WKN, use the
isinfield from the response rather than constructing it manually