---
title: "How do I validate an ISIN before calling an API?"
description: "An ISIN is 12 characters: a two-letter country code, nine alphanumerics and a Luhn check digit computed over the letters expanded to digits. Validation code in Python, TypeScript and SQL, and why it saves requests."
canonical: https://fundfactsapi.com/answers/how-to-validate-an-isin
published: 2026-09-12
updated: 2026-09-12
publisher: FundFacts API
---

# How do I validate an ISIN before calling an API?

**Short answer:** Check three things: the length is 12; the first two characters are letters and the next nine are letters or digits; and the last character equals the Luhn check digit computed after expanding every letter to its number (A=10 … Z=35). A syntactically valid ISIN costs a request if it is not a fund (404 fund_not_found), while an invalid one is rejected free (400 invalid_isin), so validating locally mainly saves round trips. The ISIN validator page on this site runs the same check in the browser.

## The algorithm

1. Uppercase; require `^[A-Z]{2}[A-Z0-9]{9}[0-9]$`.
2. Replace each letter with its number: A=10, B=11, …, Z=35, producing a digit string.
3. Apply Luhn to that string: from the right, double every second digit, subtract 9 from doubles above 9, sum everything; the total must be divisible by 10.

## Python

```python
import re

def is_valid_isin(s: str) -> bool:
    s = s.strip().upper()
    if not re.fullmatch(r"[A-Z]{2}[A-Z0-9]{9}[0-9]", s):
        return False
    digits = "".join(str(ord(c) - 55) if c.isalpha() else c for c in s)
    total, double = 0, False
    for ch in reversed(digits):
        n = int(ch)
        if double:
            n = n * 2
            if n > 9:
                n -= 9
        total += n
        double = not double
    return total % 10 == 0

assert is_valid_isin("IE00B4L5Y983") and not is_valid_isin("IE00B4L5Y984")
```

## TypeScript

```typescript
export function isValidIsin(input: string): boolean {
  const s = input.trim().toUpperCase();
  if (!/^[A-Z]{2}[A-Z0-9]{9}[0-9]$/.test(s)) return false;
  const digits = [...s].map((c) => (c >= "A" ? String(c.charCodeAt(0) - 55) : c)).join("");
  let sum = 0, dbl = false;
  for (let i = digits.length - 1; i >= 0; i--) {
    let n = Number(digits[i]);
    if (dbl) { n *= 2; if (n > 9) n -= 9; }
    sum += n; dbl = !dbl;
  }
  return sum % 10 === 0;
}
```

## SQL (PostgreSQL)

```sql
-- format only; use the application for the check digit
select isin from positions where isin !~ '^[A-Z]{2}[A-Z0-9]{9}[0-9]$';
```

## What validation does not tell you

A valid ISIN can be a stock, a bond or an index. Only the API (or a search by name) tells you whether it is a covered fund; a `404 fund_not_found` on a valid ISIN is the answer "not a fund", and it is counted. The [ISIN validator](https://fundfactsapi.com/tools/isin-validator) also computes a missing check digit, and [what is an ISIN](https://fundfactsapi.com/blog/what-is-an-isin) covers the structure and the national identifiers inside it.

## FAQ

### Which country codes are funds?

Mostly IE, LU, FR, DE, GB, CH, AT, NL, BE and the Nordics for UCITS; XS for some structured products; US for US-listed funds and, mostly, stocks. The code says where the security was issued, not what it is.

## About FundFacts API

FundFacts turns any fund or ETF ISIN into one dated factsheet (fees and KID costs, the 1–7 risk indicator, holdings, sector and country exposure, returns) read from the fund house's own documents: as a client-ready PDF under your firm's name for advisers and wealth managers, as JSON through its API for developers, or inside ChatGPT and Claude through its MCP server.

FundFacts works inside ChatGPT and Claude as a connector (a remote MCP server): ask about any fund or ETF by ISIN, ticker or name and the assistant answers from FundFacts' dated figures, fees, risk, holdings, exposure and returns, with fund cards, side-by-side comparisons, holdings overlap and portfolio look-through.

- Free API key, no card: https://fundfactsapi.com/signup
- Documentation: https://fundfactsapi.com/docs (OpenAPI: https://fundfactsapi.com/openapi.json)
- MCP server: https://fundfactsapi.com/api/mcp (set-up for every client: https://fundfactsapi.com/mcp-server)
- Pricing: https://fundfactsapi.com/pricing
- Site index for AI assistants: https://fundfactsapi.com/llms.txt
- Informational data, not investment advice.
