Guide · Node.js · SDK · REST API

LEI Validation, Search & LOU Lookup in Node.js

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.

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.

EntityLEI
Goldman Sachs784F5XWPLTWKTBV3E584
Deutsche Bank7LTWFZYICNSX8D621K86
Apple IncHWUPKR0MPOU8FGXBT394
ℹ️Unlike VAT numbers or REGON, LEI is a global standard — the same 20-character format works across all jurisdictions. A German bank and a Japanese trading firm use the same LEI structure.

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.

PartLengthDescriptionExample (Goldman Sachs)
LOU prefix4Identifies the Local Operating Unit that issued the LEI784F
Entity14Unique entity identifier (alphanumeric, no vowels in prefix)5XWPLTWKTBV3E5
Check digits2MOD-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):

1

Take the full 20-character LEI

2

Convert each letter to a number: A=10, B=11, ..., Z=35

3

Concatenate all digits into a single large number string

4

Calculate the remainder when dividing by 97

5

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, 5
  • 84 → 8, 4

The full digit string is: 7841553325212932202911313141584. Computing this number modulo 97 yields 1 — so the LEI is valid.

JavaScript implementation

function validateLeiChecksum(lei) {
  let digits = '';
  for (const ch of lei) {
    if (ch >= 'A' && ch <= 'Z') {
      digits += String(ch.charCodeAt(0) - 55); // A=10, B=11, ..., Z=35
    } else {
      digits += ch;
    }
  }

  // Block-wise modulo to avoid overflow
  let remainder = 0;
  for (const ch of digits) {
    remainder = (remainder * 10 + parseInt(ch, 10)) % 97;
  }

  return remainder === 1;
}

console.log(validateLeiChecksum('784F5XWPLTWKTBV3E584')); // true
console.log(validateLeiChecksum('784F5XWPLTWKTBV3E500')); // false
⚠️A valid checksum does not mean the entity exists or is currently active. LEIs expire annually — an entity that did not renew its LEI will still pass the checksum but have a LAPSED registration status. Always check 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:

1

Format check

20 alphanumeric characters (A-Z, 0-9)

2

MOD-97 checksum

ISO 7064 checksum — remainder must equal 1

3

Local database lookup

GLEIF Golden Copy (~2.3M records) — dataSource: "gleif-db"

4

GLEIF API fallback

If not found locally, query GLEIF REST API — dataSource: "gleif-api"

5

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
}
FieldTypeDescription
validbooleanWhether the LEI passed format and MOD-97 checksum validation
leistringThe normalised 20-character LEI (only present when valid: true)
louCodestringFirst 4 characters — the LOU prefix (only present when valid: true)
checkDigitsstringLast 2 characters — the MOD-97 check digits (only present when valid: true)
foundbooleanWhether the entity was found in the GLEIF database or API (only present when valid: true)
dataSourcestring | null"gleif-db" (local database) or "gleif-api" (GLEIF REST API fallback)
entityobject | nullEntity details — legal name, country, status, registration dates, managing LOU
louobject | nullLOU details — LEI, name, country, and status of the issuing Local Operating Unit


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. Node.js code examples

Using the @isvalid-dev/sdk package or the built-in fetch API available in Node.js 18+. All three endpoints share the same authentication pattern.

Example 1: Validate a single LEI

import { createClient } from '@isvalid-dev/sdk';

const iv = createClient({ apiKey: process.env.ISVALID_API_KEY });

const result = await iv.lei('784F5XWPLTWKTBV3E584');

if (!result.valid) {
  console.log('Invalid LEI');
} else {
  console.log('LEI     :', result.lei);
  console.log('Found   :', result.found);
  if (result.entity) {
    console.log('Name    :', result.entity.legalName);
    console.log('Country :', result.entity.country);
    console.log('Status  :', result.entity.entityStatus);
  }
}

Example 2: Search for entities

// Search for German banks
const banks = await iv.lei.search('deutsche bank', {
  country: 'DE',
  limit: 20,
});

console.log(`Found ${banks.total} results`);
banks.results.forEach(r => {
  console.log(`  ${r.lei} — ${r.legalName} (${r.country})`);
});

Example 3: List LOUs

const { lous, total } = await iv.lei.lous();

console.log(`${total} LOUs worldwide:`);
lous.forEach(l => {
  console.log(`  ${l.louCode} — ${l.name} (${l.country})`);
});
The API strips whitespace and converts to uppercase automatically. Pass raw user input directly — you do not need to pre-process it.

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

Do not rely on checksum alone — always check found and registrationStatus from the API
Do not assume a valid LEI is currently active — LEIs expire annually and can lapse
Do not store LEIs as lowercase — the format is uppercase alphanumeric
Use the validate endpoint for single-LEI checks with full entity details
Use the search endpoint to help users find LEIs by company name
Check registrationStatus — only "ISSUED" means the LEI is current and renewed

Node.js integration notes

In a TypeScript project, represent a validated LEI as a branded type so the compiler enforces that only checked values flow through your pipeline: type Lei = string & { readonly __brand: 'Lei' }. The IsValid SDK ships with complete TypeScript definitions for all response fields, which means your editor provides autocomplete on the parsed result — country codes, category names, registry data — without writing manual type declarations.

LEI validation often appears inside data ingestion pipelines: EDI feeds, supply-chain APIs, catalog imports, or financial data streams. In these contexts, validation happens at the boundary where external data enters your system. Model this as a transformation step: raw string in, validated branded type out. Use Promise.allSettled() to process batches and collect both valid and invalid results — invalid items can be quarantined for manual review without blocking the rest of the batch.

Express.js and Fastify middleware

For APIs that accept LEI as a path or query parameter, create a route middleware that validates the value before it reaches the handler. On success, attach the full parsed API response to req.validatedData so handlers can access enrichment fields (description, category, country) without making a second API call. Cache the parsed result in Redis keyed by the normalised identifier to avoid repeat API calls for the same value within a TTL window.

When LEI values arrive from user input or partner systems, normalise them before validation: trim surrounding whitespace, remove optional hyphens or spaces that some sources include for readability, and convert to the canonical case used by the relevant standard. Apply the same normalisation logic consistently across your codebase to prevent cache misses caused by formatting differences rather than value differences.

  • Read process.env.ISVALID_API_KEY once at startup and store it in a module-level constant
  • Use jest.mock() to stub the IsValid client in unit tests; CI pipelines should never make real API calls
  • Log the full validation response at debug level — it often contains fields useful for debugging data quality issues
  • For very high throughput, consider a local pre-filter that checks format with a regex before calling the API, reducing calls for obviously malformed inputs

When making HTTP calls to the IsValid API directly (without the SDK), the choice between fetch and axios is largely a matter of preference. The native fetch API is available in Node.js 18+ without any additional dependency and is sufficient for simple request/response flows. axios adds automatic JSON parsing, request/response interceptors, and a cleaner timeout API (axios.create({ timeout: 5000 })), which makes it easier to centralise the Authorization header and retry logic in one place. For high-throughput services that make many concurrent API calls, consider undici — the HTTP client underlying Node.js fetch — used directly for its connection pooling and lower overhead.

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.