Guide · Python · SDK · REST API

SEDOL Validation in Python

Format checks and check digit algorithms catch typos, but they cannot tell you whether a SEDOL identifies a live security. Here's how to validate SEDOLs properly in Python with a single API call.

1. What is a SEDOL?

A SEDOL (Stock Exchange Daily Official List) is a 7-character alphanumeric identifier assigned by the London Stock Exchange Group (LSEG) to securities. Introduced in 1979, SEDOLs are the primary identifier in the UK financial infrastructure — used by settlement systems, regulatory filings, and data vendors alike.

CREST (the UK and Irish central securities depository, operated by Euroclear) references SEDOLs in settlement instructions. The FCA requires SEDOLs in MiFIR transaction reports alongside ISINs. The SEDOL Masterfile — the authoritative source maintained by LSEG — contains over 100 million securities from exchanges worldwide.

Despite its UK origins, SEDOL is widely used in global financial data pipelines, particularly for European and international equities processed through UK-based custodians and prime brokers.


2. SEDOL anatomy

Every SEDOL is exactly 7 characters: 6 alphanumeric base characters followed by a 1-digit check digit.

PartPositionsLengthDescription
Base code1 – 66Alphanumeric identifier assigned by LSEG (digits 0–9 and consonants B–Z in modern codes)
Check digit71Single digit (0–9) computed using a weighted modulo-10 algorithm

Some well-known examples:

SEDOLBase (1–6)Check (7)Security
07980590798059BP PLC
05405280540528HSBC Holdings PLC
07188750718875Rio Tinto PLC
ℹ️A SEDOL identifies a specific listing, not just a company. The same company may have multiple SEDOLs for different share classes or exchange listings. This is different from an ISIN, which is exchange-agnostic.

3. The check digit algorithm

The SEDOL check digit uses a weighted modulo-10 algorithm with fixed position weights [1, 3, 1, 7, 3, 9]. Character values: digits 0–9 map to 0–9, letters A–Z map to 10–35 (A=10, B=11, … Z=35).

Steps:

  1. Convert each of the first 6 characters to its numeric value.
  2. Multiply each value by the corresponding weight from [1, 3, 1, 7, 3, 9].
  3. Sum all 6 products to get S.
  4. Check digit = (10 - S % 10) % 10.

Here is what a naive Python implementation looks like:

# sedol_check.py — naive implementation (format + check digit only)
import re

WEIGHTS = [1, 3, 1, 7, 3, 9]
SEDOL_RE = re.compile(r'^[A-Z0-9]{6}[0-9]$')


def char_value(ch: str) -> int:
    """Return numeric value of a SEDOL character.
    Digits 0-9 → 0-9, letters A-Z → 10-35.
    """
    if ch.isdigit():
        return int(ch)
    if ch.isalpha():
        return ord(ch) - ord('A') + 10
    raise ValueError(f"Invalid SEDOL character: {ch!r}")


def compute_check_digit(base6: str) -> int:
    """Compute the SEDOL check digit for the first 6 characters."""
    total = sum(char_value(base6[i]) * WEIGHTS[i] for i in range(6))
    return (10 - total % 10) % 10


def validate_sedol(sedol: str) -> dict:
    sedol = sedol.replace(' ', '').upper()

    if len(sedol) != 7 or not SEDOL_RE.match(sedol):
        return {'valid': False, 'reason': 'format'}

    expected = compute_check_digit(sedol[:6])
    actual = int(sedol[6])

    if expected != actual:
        return {'valid': False, 'reason': 'check_digit'}

    return {
        'valid': True,
        'sedol': sedol,
        'check_digit': sedol[6],
    }


# ── Examples ──────────────────────────────────────────────────────────────────
print(validate_sedol('0798059'))   # valid — BP PLC
print(validate_sedol('0540528'))   # valid — HSBC Holdings
print(validate_sedol('0798051'))   # invalid check digit
print(validate_sedol('07980'))     # invalid format
⚠️This validates the structure only. A passing result does not mean the SEDOL maps to a live, tradeable security — you need a data source for that.

4. Why manual validation is not enough

No free public database

The SEDOL Masterfile is a commercial product — there is no freely downloadable list of all valid SEDOLs. Maintaining your own lookup table is impractical; an API backed by OpenFIGI is the standard alternative for most applications.

Delistings and corporate actions

A structurally valid SEDOL may refer to a company that was acquired, merged, or delisted years ago. A correct check digit cannot distinguish an active BP PLC share from a retired SEDOL that was once assigned to a now-defunct fund.

Legacy codes with vowels

LSEG stopped using vowels in new SEDOLs in 2004, but millions of older codes with vowels remain valid and in active use. A validator that rejects vowels incorrectly flags valid legacy identifiers.


5. The right solution: one API call

The IsValid SEDOL API combines format validation, check digit verification, and live OpenFIGI enrichment in a single GET /v0/sedol request.

Full
Validation
Format + weighted check digit
Live
Enrichment
Name, ticker, FIGI, sector & more
100/day
Free tier
no credit card

Get your free API key at isvalid.dev.

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


6. Python code example

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

# sedol_validator.py
import os
from isvalid_sdk import IsValid

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

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

result = iv.sedol("0798059")  # BP PLC

if not result["valid"]:
    print("Invalid SEDOL: failed format or check digit")
else:
    print(f"SEDOL       : {result['sedol']}")
    print(f"Check digit : {result['checkDigit']}")
    if result.get("found"):
        print(f"Name        : {result.get('name')}")
        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 0798059:

SEDOL       : 0798059
Check digit : 9
Name        : BP PLC
Ticker      : BP/
Exchange    : LN
FIGI        : BBG000BT4FC2
Sector      : Equity

In a Django or FastAPI view:

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

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


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

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

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

    return {
        "sedol": result["sedol"],
        "checkDigit": result["checkDigit"],
        "found": result.get("found"),
        "name": result.get("name"),
        "ticker": result.get("ticker"),
        "exchCode": result.get("exchCode"),
        "figi": result.get("figi"),
    }
The API strips whitespace and uppercases the input automatically — no need to pre-process the value before sending it.

7. cURL example

Validate a SEDOL from the command line:

curl -H "Authorization: Bearer YOUR_API_KEY" \
  "https://api.isvalid.dev/v0/sedol?value=0798059"

HSBC Holdings:

curl -H "Authorization: Bearer YOUR_API_KEY" \
  "https://api.isvalid.dev/v0/sedol?value=0540528"

Invalid check digit:

curl -H "Authorization: Bearer YOUR_API_KEY" \
  "https://api.isvalid.dev/v0/sedol?value=0798051"

8. Understanding the response

Response for a valid SEDOL found in OpenFIGI:

{
  "valid": true,
  "sedol": "0798059",
  "checkDigit": "9",
  "found": true,
  "dataSource": "openfigi",
  "name": "BP PLC",
  "ticker": "BP/",
  "exchCode": "LN",
  "securityType": "Common Stock",
  "marketSector": "Equity",
  "figi": "BBG000BT4FC2",
  "compositeFIGI": "BBG000BT4FC2"
}

Response for an invalid SEDOL:

{
  "valid": false
}
FieldTypeDescription
validbooleanFormat + check digit validation result
sedolstringNormalised (uppercased) SEDOL
checkDigitstringThe 7th character (always a digit 0–9)
foundboolean | nulltrue — found in OpenFIGI; false — not found; null — OpenFIGI unavailable
namestring | nullFull instrument name
tickerstring | nullExchange ticker symbol
exchCodestring | nullBloomberg exchange code (e.g. LN for London)
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 SEDOL. found: null means the lookup failed (timeout, rate limit). In the null case, the SEDOL is still structurally valid — treat it as inconclusive and retry.

SEDOL vs. ISIN — know which you have

UK ISINs follow the pattern GB00 + SEDOL + ISIN check digit. If you receive a 12-character code starting with GB, use /v0/isin instead of /v0/sedol.


10. Summary

WhatDetail
Format7 characters: 6 alphanumeric + 1 check digit
Check algorithmWeighted sum with weights [1,3,1,7,3,9], modulo 10
Issuing authorityLSEG (London Stock Exchange Group)
CoverageUK, Ireland, and globally traded instruments
Enrichment sourceOpenFIGI (Bloomberg) via ID_SEDOL
API endpointGET /v0/sedol?value=0798059