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.
In this guide
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
ISBN-10 structure
| Format | Digits | Example | Check algorithm |
|---|---|---|---|
| ISBN-10 | 10 | 0-306-40615-2 | mod-11, weights 10..1 |
| ISBN-13 | 13 | 978-0-306-40615-7 | mod-10, weights 1,3 alternating |
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)
| Digit | 0 | 3 | 0 | 6 | 4 | 0 | 6 | 1 | 5 | 2 |
| Weight | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 |
| Product | 0 | 27 | 0 | 42 | 24 | 0 | 24 | 3 | 10 | ? |
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
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
| Digit | 9 | 7 | 8 | 0 | 3 | 0 | 6 | 4 | 0 | 6 | 1 | 5 | 7 |
| Weight | 1 | 3 | 1 | 3 | 1 | 3 | 1 | 3 | 1 | 3 | 1 | 3 | — |
| Product | 9 | 21 | 8 | 0 | 3 | 0 | 6 | 12 | 0 | 18 | 1 | 15 | ? |
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.
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
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 }
| Field | Type | Description |
|---|---|---|
| valid | boolean | Whether the ISBN has the correct format and checksum |
| format | string | ISBN-10 or ISBN-13 |
| isbn10 | string | null | ISBN-10 representation (digits only). null for 979-prefix ISBN-13s |
| isbn13 | string | ISBN-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
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.