Guide · Python · SDK · REST API

WKN Validation in Python

The WKN has no check digit — any 6-character alphanumeric string passes format validation. Here's how to validate WKNs properly in Python with a single API call that confirms the identifier and enriches it with live instrument data.

1. What is a WKN?

A WKN (Wertpapierkennnummer — also written WPKN or WPK) is the 6-character alphanumeric identifier used to identify securities in Germany. Issued since 1968 by WM Datenservice (formerly Wertpapier-Mitteilungen) in Frankfurt, WKNs appear in German brokerage confirmations, bank statements, portfolio reports, and financial data feeds.

While ISIN has become the standard for cross-border and regulatory reporting (MiFID II, EMIR), WKNs remain pervasive in the German retail and private banking segment. Many German brokers, neo-banks, and wealth management platforms expose WKNs alongside ISINs — and some legacy systems expose WKNs only.

Unlike ISIN, CUSIP, or SEDOL, the WKN has no check digit. It is an opaque sequence — there is no algorithm to verify structural correctness. Confirming a WKN requires a database lookup.


2. WKN anatomy

A WKN is exactly 6 characters long, using digits and uppercase letters:

PropertyDetail
Length6 characters
Allowed characters[A-Z0-9]
Check digitNone
Issuing authorityWM Datenservice, Frankfurt
ScopeGerman securities (equities, bonds, funds, warrants, ETFs)

Some well-known examples:

WKNISINSecurity
514000DE0005140008Deutsche Bank AG
716460DE0007164600SAP SE
723610DE0007236101Siemens AG
840400DE0008404005Allianz SE
ℹ️Historically WKNs were purely numeric. Alphanumeric WKNs (containing letters A–Z) were introduced for structured products, warrants, and certificates. Both formats are equally valid and follow the same 6-character rule.

3. WKN inside a German ISIN

German ISINs embed the WKN in a predictable way. The NSIN (National Securities Identifying Number) portion of a DE ISIN is the WKN zero-padded on the left to 9 characters:

# Extracting WKN from a German ISIN
isin = "DE0005140008"

country = isin[:2]   # "DE"
nsin    = isin[2:11] # "000514000"
check   = isin[11]   # "8"

# The WKN is the NSIN with leading zeros stripped
wkn = nsin.lstrip("0")  # "514000"

print(wkn)  # → 514000
If you already have a DE ISIN, use /v0/isin directly — it validates the full ISIN and returns the WKN embedded in the NSIN alongside all instrument enrichment. Use /v0/wkn only when you have a standalone WKN.

4. Why format checks are not enough

No check digit — typos are invisible

Because there is no check digit, a one-character typo (514001 instead of 514000) passes format validation silently. Without a database lookup there is no way to detect it. In a trading or portfolio management context, this kind of error can result in the wrong security being referenced in a position, order, or report.

Retired and reassigned WKNs

When a company is acquired, merged, or delisted, its WKN may be retired or superseded. A structurally valid WKN might map 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 registry

WM Datenservice does not publish a free downloadable list of all valid WKNs. The WKN database is a commercial product. An API backed by OpenFIGI is the practical alternative for confirming WKNs against real securities.


5. The right solution: one API call

The IsValid WKN API combines format validation and live OpenFIGI enrichment in a single GET /v0/wkn request.

Format
Validation
6-char [A-Z0-9] check
Live
Enrichment
Name, ISIN, FIGI, exchange & more
100/day
Free tier
no credit card

Get your free API key at isvalid.dev.

Full parameter reference and response schema: WKN Validation API docs →


6. Python code example

Using the isvalid-sdk package or the requests library directly.

# wkn_validator.py
import os
from isvalid_sdk import IsValid

iv = IsValid(api_key=os.environ["ISVALID_API_KEY"])

# ── Example usage ─────────────────────────────────────────────────────────────

result = iv.wkn("514000")  # Deutsche Bank AG

if not result["valid"]:
    print("Invalid WKN: must be 6 alphanumeric characters")
else:
    print(f"WKN         : {result['wkn']}")
    if result.get("found"):
        print(f"Name        : {result.get('name')}")
        print(f"ISIN        : {result.get('isin')}")
        print(f"Ticker      : {result.get('ticker')}")
        print(f"Exchange    : {result.get('exchCode')}")
        print(f"FIGI        : {result.get('figi')}")
        print(f"Sector      : {result.get('marketSector')}")

Expected output for 514000:

WKN         : 514000
Name        : Deutsche Bank AG
ISIN        : DE0005140008
Ticker      : DBK
Exchange    : GY
FIGI        : BBG000BC5588
Sector      : Equity

In a FastAPI endpoint:

# router.py (FastAPI)
from fastapi import APIRouter, HTTPException
import requests, os

router = APIRouter()
API_KEY = os.environ["ISVALID_API_KEY"]


@router.get("/securities/validate-wkn")
def validate_wkn_endpoint(wkn: str):
    if not wkn:
        raise HTTPException(status_code=400, detail="Missing wkn parameter")

    try:
        resp = requests.get(
            "https://api.isvalid.dev/v0/wkn",
            params={"value": wkn},
            headers={"Authorization": f"Bearer {API_KEY}"},
            timeout=10,
        )
        resp.raise_for_status()
        result = resp.json()
    except requests.RequestException:
        raise HTTPException(status_code=502, detail="WKN validation service unavailable")

    if not result["valid"]:
        raise HTTPException(status_code=400, detail=f"Invalid WKN: {wkn}")

    return {
        "wkn": result["wkn"],
        "found": result.get("found"),
        "isin": result.get("isin"),
        "name": result.get("name"),
        "ticker": result.get("ticker"),
        "exchCode": result.get("exchCode"),
        "figi": result.get("figi"),
    }
The API strips whitespace and uppercases input automatically — no pre-processing needed.

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:

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 an invalid WKN:

{
  "valid": false
}
FieldTypeDescription
validbooleanFormat validation result (6-char [A-Z0-9])
wknstringNormalised (uppercased, whitespace-stripped) WKN
foundboolean | nulltrue — found in OpenFIGI; false — not found; null — OpenFIGI unavailable
isinstring | nullAssociated ISIN (e.g. DE0005140008)
namestring | nullFull instrument name
tickerstring | nullExchange ticker symbol
exchCodestring | nullBloomberg exchange code (e.g. GY for XETRA)
securityTypestring | nullSecurity type (e.g. Common Stock)
marketSectorstring | nullMarket sector (e.g. Equity)
figistring | nullFinancial Instrument Global Identifier
compositeFIGIstring | nullComposite FIGI across all exchanges

9. Edge cases to handle

found: null vs. found: false

found: false means OpenFIGI responded but did not recognise the WKN. found: null means the lookup failed (timeout, rate limit). In the null case, treat the result as inconclusive and retry — do not reject the WKN as invalid.

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.


10. Summary

WhatDetail
Format6 alphanumeric characters [A-Z0-9], no check digit
Check algorithmNone — purely opaque sequence
Issuing authorityWM Datenservice, Frankfurt
CoverageGerman securities (equities, bonds, ETFs, warrants, structured products)
Enrichment sourceOpenFIGI (Bloomberg) via ID_WERTPAPIER
API endpointGET /v0/wkn?value=514000

Python integration notes

Use Pydantic V2's @field_validator or the Annotated + AfterValidator pattern to embed WKN validation in your data models. The validator calls the IsValid API and raises a ValueError if valid is False. Because there is no check digit to compute locally, the API call is the only meaningful validation step — format-only regex checks provide little protection against real-world errors.

Async batch validation

For bulk imports — portfolio data from a custodian file, for example — use asyncio.gather() with a shared httpx.AsyncClient and an asyncio.Semaphore to cap concurrency at your API rate limit. This reduces total validation time from O(n) sequential to O(1) bounded by the pool size — important when processing hundreds of WKNs at once.

  • Store ISVALID_API_KEY in a .env file and load with python-dotenv
  • Cache results in Redis with a 24h TTL — WKN-to-instrument mappings rarely change
  • Apply .strip().upper() before validation to normalise input from different source systems
  • Use NewType('Wkn', str) to distinguish validated WKNs from raw strings in type annotations
  • Store the isin field from the response — it enables downstream cross-referencing without additional API calls