Guide · Python · SDK · REST API

ISBN Validation in Python — ISBN-10, ISBN-13 & Checksum

Every book published worldwide carries an ISBN — a unique identifier used by publishers, bookstores, and libraries. Here's how ISBN-10 and ISBN-13 checksums work, how the two formats relate, and how to validate any ISBN reliably in your Python application.

1. What is an ISBN?

ISBN stands for International Standard Book Number. It is a unique numeric identifier assigned to every edition of every commercially published book. Publishers, bookstores, libraries, and distributors use ISBNs to catalogue, order, and track books worldwide.

There are two formats in use today. ISBN-10 was the original standard, used from 1970 until 2007. ISBN-13 is the current standard, introduced on 1 January 2007. Every ISBN-13 starts with prefix 978 or 979.

ISBN-13 is a subset of the EAN-13 barcode system — the barcode printed on the back cover of a book is its ISBN-13 encoded as an EAN-13 barcode. The two numbers are identical.


2. ISBN anatomy

Both ISBN-10 and ISBN-13 are divided into groups separated by hyphens (or spaces). The hyphens are optional and carry no information — they exist purely for readability.

ISBN-13 structure

978-0-306-40615-7
978GS1 prefix
0Group (English)
306Publisher
40615Title
7Check digit

ISBN-10 structure

0-306-40615-2
0Group (English)
306Publisher
40615Title
2Check digit
FormatDigitsExampleCheck algorithm
ISBN-10100-306-40615-2mod-11, weights 10..1
ISBN-1313978-0-306-40615-7mod-10, weights 1,3 alternating
ℹ️An ISBN-10 with prefix group 0 (English-speaking countries) can be converted to ISBN-13 by prepending 978 and recalculating the check digit. The body digits (group + publisher + title) remain identical.

3. The check digit algorithms

ISBN-10 and ISBN-13 use completely different checksum algorithms. This is one of the main reasons you need a proper validator rather than a hand-written regex.

ISBN-10 — mod-11 with weights 10 down to 1

Let's walk through 0-306-40615-2:

Step 1 — Multiply each digit by its weight (10 down to 1)

Digit0306406152
Weight10987654321
Product02704224024310?

Step 2 — Sum the first 9 products

0 + 27 + 0 + 42 + 24 + 0 + 24 + 3 + 10 = 130

Step 3 — Check digit = (11 - sum mod 11) mod 11

(11 - (130 % 11)) % 11 = (11 - 9) % 11 = 2

Last digit is 2 — valid ISBN-10

⚠️If the calculated check digit is 10, ISBN-10 uses the letter X (uppercase) as the check character. This is the only non-digit character allowed in an ISBN-10.

ISBN-13 — mod-10 with alternating weights 1 and 3

Let's walk through 978-0-306-40615-7:

Step 1 — Apply alternating weights (1 and 3) to the first 12 digits

Digit9780306406157
Weight131313131313
Product9218030612018115?

Step 2 — Sum the products

9 + 21 + 8 + 0 + 3 + 0 + 6 + 12 + 0 + 18 + 1 + 15 = 93

Step 3 — Check digit = (10 - sum mod 10) mod 10

(10 - (93 % 10)) % 10 = (10 - 3) % 10 = 7

Last digit is 7 — valid ISBN-13

# isbn_checksum.py — validate ISBN-10 or ISBN-13 check digits
import re


def validate_isbn10(raw: str) -> bool:
    digits = re.sub(r"[-\s]", "", raw)
    if not re.match(r"^\d{9}[\dXx]$", digits):
        return False

    total = 0
    for i in range(9):
        total += int(digits[i]) * (10 - i)

    last = digits[9].upper()
    total += 10 if last == "X" else int(last)

    return total % 11 == 0


def validate_isbn13(raw: str) -> bool:
    digits = re.sub(r"[-\s]", "", raw)
    if not re.match(r"^\d{13}$", digits):
        return False

    total = sum(
        int(d) * (1 if i % 2 == 0 else 3)
        for i, d in enumerate(digits[:12])
    )

    check = (10 - (total % 10)) % 10
    return check == int(digits[12])


print(validate_isbn10("0-306-40615-2"))      # True   ISBN-10
print(validate_isbn13("978-0-306-40615-7"))  # True   ISBN-13
print(validate_isbn10("0-306-40615-3"))      # False  bad check digit

4. Why manual validation isn't enough

Writing ISBN validation from scratch seems simple at first glance, but there are several pitfalls that trip up hand-rolled implementations:

Two different algorithms

ISBN-10 uses mod-11 with descending weights (10 down to 1), while ISBN-13 uses mod-10 with alternating weights (1, 3). You need to detect the format first, then apply the correct algorithm. A single-algorithm validator will silently produce wrong results for the other format.

ISBN-10 to ISBN-13 conversion

Converting between formats is not just about prepending 978 — you must also recalculate the check digit using the ISBN-13 algorithm. Many implementations forget this step and produce invalid ISBN-13s.

Hyphens are optional

ISBNs appear with and without hyphens in the wild. Your validator must strip formatting before computing the checksum, but also accept both forms as valid input.

979 prefix has no ISBN-10 equivalent

Only ISBN-13s starting with 978 can be converted to ISBN-10. The newer 979 prefix has no ISBN-10 representation. Code that blindly converts will produce garbage for 979-prefixed ISBNs.


5. The right solution

The IsValid ISBN API handles all of this for you — format detection, checksum validation, and automatic ISBN-10/ISBN-13 conversion in a single call.

ISBN-10/13
Dual format
both variants supported
10 ↔ 13
Auto conversion
returns both representations
mod-11 / mod-10
Checksum
correct algorithm per format

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


6. Python code example

from isvalid import create_client

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

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

result = iv.isbn("978-0-306-40615-7")

if not result["valid"]:
    print("Invalid ISBN")
else:
    print("Format:", result["format"])    # → 'ISBN-13'
    print("ISBN-10:", result["isbn10"])    # → '0306406152'
    print("ISBN-13:", result["isbn13"])    # → '9780306406157'

In a book import pipeline:

# Validate ISBNs before inserting into a book catalogue
def import_books(rows: list[dict]) -> list[dict]:
    results = []

    for row in rows:
        if not row.get("isbn"):
            results.append({**row, "isbn_status": "missing"})
            continue

        check = iv.isbn(row["isbn"])

        if not check["valid"]:
            results.append({**row, "isbn_status": "invalid"})
            continue

        results.append({
            **row,
            "isbn10": check["isbn10"],    # normalised ISBN-10 (or None for 979)
            "isbn13": check["isbn13"],    # normalised ISBN-13
            "isbn_format": check["format"],
            "isbn_status": "valid",
        })

    return results
Always store the ISBN-13 as the canonical identifier in your database — it is the current standard and every ISBN-10 can be converted to ISBN-13. Use the isbn10 field when you need to display the legacy format or match against older catalogues.

7. cURL example

Validate an ISBN-13:

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

Validate an ISBN-10:

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

With hyphens (URL-encoded automatically by most tools):

curl -H "Authorization: Bearer YOUR_API_KEY" \
  "https://api.isvalid.dev/v0/isbn?value=978-0-306-40615-7"

Invalid ISBN (bad check digit):

curl -H "Authorization: Bearer YOUR_API_KEY" \
  "https://api.isvalid.dev/v0/isbn?value=978-0-306-40615-0"

8. Understanding the response

Valid ISBN-13 (978 prefix):

{
  "valid": true,
  "format": "ISBN-13",
  "isbn10": "0306406152",
  "isbn13": "9780306406157"
}

Valid ISBN-10:

{
  "valid": true,
  "format": "ISBN-10",
  "isbn10": "0306406152",
  "isbn13": "9780306406157"
}

ISBN-13 with 979 prefix (no ISBN-10 equivalent):

{
  "valid": true,
  "format": "ISBN-13",
  "isbn10": null,
  "isbn13": "9791234567896"
}

Invalid ISBN:

{
  "valid": false
}
FieldTypeDescription
validbooleanWhether the ISBN has the correct format and checksum
formatstringISBN-10 or ISBN-13
isbn10string | nullISBN-10 representation (digits only). null for 979-prefix ISBN-13s
isbn13stringISBN-13 representation (digits only). Always present when valid

9. Edge cases

(a) ISBN-10 with X check digit

When the mod-11 checksum results in a remainder of 10, the check digit is represented as the letter X. For example, 0-8044-2957-X is a valid ISBN-10. The API accepts both uppercase and lowercase X.

# X check digit — perfectly valid
result = iv.isbn("0-8044-2957-X")
print(result["valid"])   # → True
print(result["format"])  # → 'ISBN-10'
print(result["isbn13"])  # → '9780804429573'

(b) 979 prefix — no ISBN-10 equivalent

ISBN-13s starting with 979 cannot be converted to ISBN-10 because the ISBN-10 system only accommodated the 978 prefix. The API returns isbn10: null for these ISBNs. Always check for null before displaying or storing the ISBN-10.

result = iv.isbn("979-12-345-6789-6")
print(result["isbn10"])  # → None
print(result["isbn13"])  # → '9791234567896'

(c) ISBN vs EAN-13

ISBN-13 is a subset of EAN-13 — specifically, it covers EAN-13 codes with prefix 978 or 979. The checksum algorithm is identical (mod-10 with alternating weights 1 and 3). If your application handles both product barcodes and books, use the EAN endpoint for general barcodes and the ISBN endpoint when you specifically need ISBN-10/13 conversion and book-specific validation.

(d) Hyphens and formatting

The API accepts ISBNs with or without hyphens. Hyphens separate the group, publisher, title, and check digit segments but carry no computational significance. The response always returns normalised ISBNs (digits only, no hyphens) so you can store them consistently. If you need to display hyphens, use a lookup table for the variable-length group and publisher segments — the position of hyphens cannot be computed from the digits alone.


10. Summary

Support both ISBN-10 and ISBN-13 — they use different checksum algorithms
Store ISBN-13 as the canonical identifier — it is the current standard
Use the isbn10 field for legacy catalogue matching when available
Always check isbn10 for null — 979-prefix ISBNs have no ISBN-10 equivalent
Do not assume every ISBN-13 can be converted to ISBN-10 — only 978-prefix can
Do not parse ISBNs as integers — ISBN-10 can contain the letter X

See also

Validate ISBNs instantly

Free tier includes 100 API calls per day. No credit card required. Supports ISBN-10 and ISBN-13 with automatic conversion.