LEI Validation, Search & LOU Lookup in Python
Three API endpoints for Legal Entity Identifiers — validate with MOD-97 checksum, search 2.3 million entities by name, and list all Local Operating Units. Here's how it all fits together using Python and the requests library.
In this guide
1. What is a LEI?
A Legal Entity Identifier (LEI) is a 20-character alphanumeric code that uniquely identifies a legal entity participating in financial transactions. The standard is ISO 17442, managed by the Global Legal Entity Identifier Foundation (GLEIF).
LEIs are mandatory for:
- Derivatives reporting under EMIR and Dodd-Frank
- MiFID II transaction reporting in the EU
- Securities financing transactions (SFTR)
- Any entity trading on regulated markets
The GLEIF database contains over 2.3 million active LEIs, covering entities in 250+ jurisdictions.
| Entity | LEI |
|---|---|
| Goldman Sachs | 784F5XWPLTWKTBV3E584 |
| Deutsche Bank | 7LTWFZYICNSX8D621K86 |
| Apple Inc | HWUPKR0MPOU8FGXBT394 |
2. LEI structure — the 20-character code
A LEI is exactly 20 characters: a 4-character LOU prefix + a 14-character entity identifier + 2 check digits.
| Part | Length | Description | Example (Goldman Sachs) |
|---|---|---|---|
| LOU prefix | 4 | Identifies the Local Operating Unit that issued the LEI | 784F |
| Entity | 14 | Unique entity identifier (alphanumeric, no vowels in prefix) | 5XWPLTWKTBV3E5 |
| Check digits | 2 | MOD-97 checksum (identical to IBAN) | 84 |
All characters are uppercase alphanumeric (A-Z, 0-9). The LOU prefix is assigned by GLEIF to each Local Operating Unit — there are about 40 active LOUs worldwide.
3. The MOD-97 checksum
The LEI checksum uses the same MOD-97 algorithm as IBAN (ISO 7064):
Take the full 20-character LEI
Convert each letter to a number: A=10, B=11, ..., Z=35
Concatenate all digits into a single large number string
Calculate the remainder when dividing by 97
If the remainder equals 1, the LEI is valid
Worked example: 784F5XWPLTWKTBV3E584
Each character is converted to its numeric equivalent. Digits stay as-is; letters become two-digit numbers (A=10, B=11, ..., Z=35):
784F→ 7, 8, 4, 15 (F=15)5XWPLTWKTBV3E5→ 5, 33, 32, 25, 21, 29, 32, 20, 29, 11, 31, 3, 14, 584→ 8, 4
The full digit string is: 7841553325212932202911313141584. Computing this number modulo 97 yields 1 — so the LEI is valid.
Python implementation
def validate_lei_checksum(lei: str) -> bool: """Validate a LEI using the ISO 17442 MOD-97 checksum. Args: lei: A 20-character alphanumeric LEI string. Returns: True if the checksum is valid, False otherwise. """ digits = "" for ch in lei: if ch.isalpha(): digits += str(ord(ch) - 55) # A=10, B=11, ..., Z=35 else: digits += ch # Python handles arbitrarily large integers — no chunking needed return int(digits) % 97 == 1 print(validate_lei_checksum("784F5XWPLTWKTBV3E584")) # True print(validate_lei_checksum("784F5XWPLTWKTBV3E500")) # False
registrationStatus from the API response.4. Three endpoints — validate, search, LOUs
The IsValid LEI API provides three endpoints that cover the full spectrum of LEI operations — from validating a single code to searching across 2.3 million entities:
Validate a single LEI
GET /v0/lei?value=... — MOD-97 checksum + entity lookup from the GLEIF database (2.3M records) with fallback to the GLEIF REST API. Returns full entity details including legal name, country, status, and the issuing LOU.
Search entities by name
GET /v0/lei/search?q=... — Fuzzy search across 2.3M legal entity names using PostgreSQL trigram similarity. Filter by country and entity status. Paginated results sorted by relevance.
List all LOUs
GET /v0/lei/lous — Complete list of ~40 Local Operating Units with their LEI, name, country, and status. Useful for building dropdowns, validating LOU prefixes, or understanding who issued a given LEI.
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: LEI Validation API docs →
5. Endpoint 1: Validate a single LEI
The validate endpoint performs a multi-step verification chain:
Format check
20 alphanumeric characters (A-Z, 0-9)
MOD-97 checksum
ISO 7064 checksum — remainder must equal 1
Local database lookup
GLEIF Golden Copy (~2.3M records) — dataSource: "gleif-db"
GLEIF API fallback
If not found locally, query GLEIF REST API — dataSource: "gleif-api"
LOU resolution
LOU info resolved in parallel from the lei_lous table
Response — entity found (Goldman Sachs)
{ "valid": true, "lei": "784F5XWPLTWKTBV3E584", "louCode": "784F", "checkDigits": "84", "found": true, "dataSource": "gleif-db", "entity": { "legalName": "GOLDMAN SACHS GROUP INC", "country": "US", "entityStatus": "ACTIVE", "registrationStatus": "ISSUED", "category": null, "initialRegistrationDate": "2012-06-06", "lastUpdate": "2024-08-14", "nextRenewal": "2025-08-14", "managingLou": "EVK05KS7XY1DEII3R011" }, "lou": { "lei": "EVK05KS7XY1DEII3R011", "name": "Bloomberg Finance L.P.", "country": "US", "status": "ACTIVE" } }
Response — invalid checksum
{ "valid": false }
Response — valid checksum but entity not found
{ "valid": true, "lei": "784F5XWPLTWKTBV3E584", "louCode": "784F", "checkDigits": "84", "found": false, "dataSource": "gleif-api", "entity": null, "lou": null }
| Field | Type | Description |
|---|---|---|
| valid | boolean | Whether the LEI passed format and MOD-97 checksum validation |
| lei | string | The normalised 20-character LEI (only present when valid: true) |
| louCode | string | First 4 characters — the LOU prefix (only present when valid: true) |
| checkDigits | string | Last 2 characters — the MOD-97 check digits (only present when valid: true) |
| found | boolean | Whether the entity was found in the GLEIF database or API (only present when valid: true) |
| dataSource | string | null | "gleif-db" (local database) or "gleif-api" (GLEIF REST API fallback) |
| entity | object | null | Entity details — legal name, country, status, registration dates, managing LOU |
| lou | object | null | LOU details — LEI, name, country, and status of the issuing Local Operating Unit |
6. Endpoint 2: Search entities by name
The search endpoint lets you find legal entities by name using fuzzy matching powered by PostgreSQL's pg_trgm extension. It supports filtering by country and entity status, with paginated results sorted by similarity score.
Parameters
| Parameter | Required | Description |
|---|---|---|
| q | Yes | Search query, minimum 2 characters |
| country | No | ISO 3166-1 alpha-2 code, e.g. "US", "DE", "PL" |
| entityStatus | No | "ACTIVE" or "INACTIVE" |
| page | No | Page number, default 1 |
| limit | No | Results per page, default 20, max 100 |
Response
{ "results": [ { "lei": "784F5XWPLTWKTBV3E584", "legalName": "GOLDMAN SACHS GROUP INC", "country": "US", "entityStatus": "ACTIVE", "registrationStatus": "ISSUED", "category": null } ], "page": 1, "limit": 20, "total": 47 }
?q=goldmn sachs will still find Goldman Sachs. Results are sorted by similarity score, so the best matches come first.7. Endpoint 3: List all LOUs
No parameters needed. Returns all ~40 Local Operating Units — the organizations authorized by GLEIF to issue and maintain LEIs. Each LOU has a 4-character code that appears as the prefix of every LEI it issues.
Response
{ "lous": [ { "louCode": "784F", "lei": "EVK05KS7XY1DEII3R011", "name": "Bloomberg Finance L.P.", "country": "US", "status": "ACTIVE" } ], "total": 40 }
LOUs are responsible for the initial registration and annual renewal of LEIs. When a LEI lapses (the entity did not renew), it is the managing LOU that marks the registration as LAPSED. The LOUs endpoint is useful for building filter dropdowns, validating LOU prefixes in bulk, or understanding the geographic distribution of LEI issuance — most LOUs are concentrated in Europe and North America, but they serve entities globally.
8. Python code examples
Using the requests library — the de facto standard for HTTP in Python. Install it with pip install requests. All three endpoints share the same authentication pattern.
Example 1: Validate a single LEI
# lei_client.py import os import requests API_KEY = os.environ["ISVALID_API_KEY"] BASE_URL = "https://api.isvalid.dev" def validate_lei(lei: str) -> dict: """Validate a LEI using the IsValid API. Args: lei: A 20-character LEI string. Returns: Validation result as a dictionary. Raises: requests.HTTPError: If the API returns a non-2xx status. """ response = requests.get( f"{BASE_URL}/v0/lei", params={"value": lei}, headers={"Authorization": f"Bearer {API_KEY}"}, ) response.raise_for_status() return response.json() # ── Example usage ──────────────────────────────────────────────────────────── result = validate_lei("784F5XWPLTWKTBV3E584") if not result["valid"]: print("Invalid LEI") else: print(f"LEI : {result['lei']}") print(f"LOU : {result['louCode']}") print(f"Found : {result['found']}") print(f"Source : {result['dataSource']}") if result.get("entity"): print(f"Name : {result['entity']['legalName']}") print(f"Country : {result['entity']['country']}") print(f"Status : {result['entity']['entityStatus']}")
Example 2: Search for entities
def search_lei( query: str, *, country: str | None = None, entity_status: str | None = None, page: int = 1, limit: int = 20, ) -> dict: """Search for legal entities by name. Args: query: Search string (min 2 characters). country: ISO 3166-1 alpha-2 filter (e.g. "US", "DE"). entity_status: "ACTIVE" or "INACTIVE". page: Page number (default 1). limit: Results per page (default 20, max 100). Returns: Search results with pagination metadata. """ params: dict = {"q": query, "page": page, "limit": limit} if country: params["country"] = country if entity_status: params["entityStatus"] = entity_status response = requests.get( f"{BASE_URL}/v0/lei/search", params=params, headers={"Authorization": f"Bearer {API_KEY}"}, ) response.raise_for_status() return response.json() # Search for German banks banks = search_lei("deutsche bank", country="DE", entity_status="ACTIVE") print(f"Found {banks['total']} results (page {banks['page']})") for r in banks["results"]: print(f" {r['lei']} — {r['legalName']} ({r['country']})")
Example 3: List LOUs
def list_lous() -> dict: """Fetch all Local Operating Units. Returns: Dictionary with "lous" list and "total" count. """ response = requests.get( f"{BASE_URL}/v0/lei/lous", headers={"Authorization": f"Bearer {API_KEY}"}, ) response.raise_for_status() return response.json() data = list_lous() print(f"{data['total']} LOUs worldwide:") for lou in data["lous"]: print(f" {lou['louCode']} — {lou['name']} ({lou['country']})")
Full example: Flask integration
In a KYC onboarding form, you might use it like this with Flask:
# app.py (Flask) from flask import Flask, request, jsonify app = Flask(__name__) @app.post("/onboard") def onboard(): data = request.get_json() try: lei_check = validate_lei(data["lei"]) except requests.RequestException: return jsonify(error="LEI validation service unavailable"), 502 if not lei_check["valid"]: return jsonify(error="Invalid LEI format"), 400 if not lei_check["found"]: return jsonify(error="LEI not found in GLEIF database"), 400 entity = lei_check["entity"] if entity["registrationStatus"] == "LAPSED": return jsonify(error="LEI registration has lapsed — please renew"), 400 # Entity is valid, found, and current — proceed with onboarding return jsonify( success=True, entityName=entity["legalName"], country=entity["country"], lou=lei_check["lou"]["name"] if lei_check.get("lou") else None, )
9. cURL examples
Validate a LEI (Goldman Sachs):
curl -H "Authorization: Bearer YOUR_API_KEY" \ "https://api.isvalid.dev/v0/lei?value=784F5XWPLTWKTBV3E584"
Search by name (Goldman Sachs, US only):
curl -H "Authorization: Bearer YOUR_API_KEY" \ "https://api.isvalid.dev/v0/lei/search?q=goldman%20sachs&country=US"
Search with pagination (page 2, 5 results per page):
curl -H "Authorization: Bearer YOUR_API_KEY" \ "https://api.isvalid.dev/v0/lei/search?q=bank&limit=5&page=2"
List all LOUs:
curl -H "Authorization: Bearer YOUR_API_KEY" \ "https://api.isvalid.dev/v0/lei/lous"
10. Use cases — KYC, MiFID II, EMIR
LEI validation is a building block in many regulatory and compliance workflows. Here are the most common scenarios:
KYC & Onboarding
Verify counterparty identity during customer onboarding. Validate the LEI, check entity status (ACTIVE vs INACTIVE), and confirm the registration is not LAPSED. Combine with IBAN and VAT validation for a complete KYC check.
MiFID II Transaction Reporting
EU investment firms must include LEIs in transaction reports. The validate endpoint confirms both format and entity existence. Use the search endpoint to help users find their LEI when they don't know it — fuzzy matching makes this reliable even with partial or misspelled names.
EMIR Derivatives Reporting
Both counterparties to an OTC derivative trade must be identified by LEI. Validate LEIs before submission to trade repositories. LAPSED registrations are rejected by regulators — always check registrationStatus before accepting a LEI for reporting purposes.
Summary
See also
Try LEI validation instantly
Free tier includes 100 API calls per day. No credit card required. Validate, search, and look up LOUs — all included.