Convert ISBN-10 to ISBN-13 in Python (and Back)
Prepending 978 is only half the job. The two formats use different checksum algorithms, so the last character must be thrown away and recomputed — here is tested Python for both directions.
Why conversion means recomputing the check digit
Before 2007, books carried 10-character ISBNs whose last character is a mod-11 checksum: the first nine digits are weighted 10, 9, 8, … 2, summed, and the check character makes the total divisible by 11. Because the modulus is 11, that character can be the value ten, written as X.
An ISBN-13 is a different animal: it is a GTIN, the same family as retail barcodes, and its final digit comes from the GS1 mod-10 algorithm with alternating weights 1 and 3. (Both algorithms are walked through digit by digit in how check digits work.)
So converting 0-306-40615-2 is not "978" + "0306406152". The correct recipe is: validate the ISBN-10, take its nine core digits (dropping the mod-11 check character), prepend 978, then compute a fresh mod-10 check digit for the 12-digit body. If you keep the old check character you produce a number that fails every barcode scanner and every marketplace upload. Our free ISBN converter does exactly this in the browser; below is the same logic in Python you can drop into your own pipeline.
The implementation
Three small helpers plus two public functions. Tested on Python 3; no imports needed.
def clean(isbn):
"""Strip hyphens and spaces, uppercase any x."""
return isbn.replace("-", "").replace(" ", "").upper()
def isbn10_check_char(nine):
"""Check character for a 9-digit ISBN-10 body: '0'-'9' or 'X'."""
total = sum(int(d) * (10 - i) for i, d in enumerate(nine))
check = (11 - total % 11) % 11
return "X" if check == 10 else str(check)
def isbn13_check_digit(twelve):
"""GS1 mod-10 check digit for a 12-digit ISBN-13 body."""
total = sum(int(d) * (3 if i % 2 else 1) for i, d in enumerate(twelve))
return str((10 - total % 10) % 10)
def isbn10_to_13(isbn10):
s = clean(isbn10)
if len(s) != 10 or not s[:9].isdigit() or s[9] not in "0123456789X":
raise ValueError(f"Not a well-formed ISBN-10: {isbn10!r}")
if s[9] != isbn10_check_char(s[:9]):
raise ValueError(f"Bad ISBN-10 check digit in {isbn10!r} "
f"(expected {isbn10_check_char(s[:9])})")
body = "978" + s[:9]
return body + isbn13_check_digit(body)
def isbn13_to_10(isbn13):
s = clean(isbn13)
if len(s) != 13 or not s.isdigit():
raise ValueError(f"Not a well-formed ISBN-13: {isbn13!r}")
if s[12] != isbn13_check_digit(s[:12]):
raise ValueError(f"Bad ISBN-13 check digit in {isbn13!r} "
f"(expected {isbn13_check_digit(s[:12])})")
if not s.startswith("978"):
raise ValueError(f"{isbn13!r} starts with {s[:3]}: "
"only 978 ISBN-13s have an ISBN-10 form")
body = s[3:12]
return body + isbn10_check_char(body)
Walking through the pieces:
clean()removes hyphens and spaces and uppercases a lowercasex. Hyphen positions vary by registration group, so the safest normalisation is to strip them entirely.isbn10_check_char()implements mod-11. Note(11 - total % 11) % 11: the outer% 11maps the “need 11 more” case back to 0. When the result is 10 it returns the letterX— that is why the function returns a string, never an int.isbn13_check_digit()implements GS1 mod-10. Positions 0, 2, 4… of the 12-digit body get weight 1 and positions 1, 3, 5… get weight 3, which is what3 if i % 2 else 1expresses.- Both converters validate before converting. A converter that silently transforms a mistyped ISBN just launders the typo into a new, equally wrong number.
Worked example: 0-306-40615-2
Take the classic test vector 0-306-40615-2 (ISBN-13 form 978-0-306-40615-7):
- Clean and validate. Stripped:
0306406152. Weighted sum of the first nine digits: (0×10)+(3×9)+(0×8)+(6×7)+(4×6)+(0×5)+(6×4)+(1×3)+(5×2) = 130. 130 mod 11 = 9, so the check must be 11 − 9 = 2. It is — the input is valid. - Build the 12-digit body. Drop the trailing 2, prepend 978:
978030640615. - New check digit. Apply the mod-10 weights to the 12-digit body — or let the code do it:
>>> isbn10_to_13("0-306-40615-2")
'9780306406157'
>>> isbn13_to_10("978-0-306-40615-7")
'0306406152'
>>> isbn10_to_13("0-8044-2957-X") # X = check value 10
'9780804429573'
>>> isbn13_to_10("9780804429573") # round-trips back to the X
'080442957X'
>>> isbn13_to_10("979-10-90636-07-1")
ValueError: '979-10-90636-07-1' starts with 979: only 978 ISBN-13s have an ISBN-10 form
By hand, step 3 works out as follows: digits 9,7,8,0,3,0,6,4,0,6,1,5 against weights 1,3,1,3,1,3,1,3,1,3,1,3 give products 9+21+8+0+3+0+6+12+0+18+1+15 = 93; the next multiple of 10 is 100, so the check digit is 7 and the result is 9780306406157. Exactly what the code printed.
The 979 caveat
The ISBN-10 range maps one-to-one onto the 978 prefix only. ISBN-13s beginning with 979 — including the 979-8 block now used for many new US registrations and self-published titles — were never issued in 10-digit form, so there is no ISBN-10 to convert back to. isbn13_to_10() therefore raises a ValueError for 979-10-90636-07-1 (a perfectly valid ISBN-13; its checksum passes). If your data model needs a value rather than an exception, return None — but never invent a 10-digit form by chopping off the prefix: the result would collide with a different book’s real ISBN-10 range.
Edge cases that bite in production
- Leading zeros.
int("0306406152")is 306406152 — nine digits, book destroyed. ISBNs are identifiers, not quantities: keep them as strings end to end. Excel is the usual culprit, silently reformatting ISBN columns on CSV open; see fixing leading zeros in Excel if your input files arrive truncated. - Lowercase x.
080442957xis the same ISBN as080442957X.clean()uppercases, so both validate; forget that and roughly 1 in 11 older ISBNs will randomly fail. - X in the wrong place. X is only legal as the tenth character of an ISBN-10, and never appears in an ISBN-13. The length-and-character guards reject
03064X6152and any 13-digit string containing X. - Keeping the old check digit. The classic bug:
"978" + isbn10yields 13 characters, looks plausible, and is wrong for about 9 out of 10 books (and always wrong when the ISBN-10 ends in X). Always slice to nine digits and recompute. - Wrong lengths. After cleaning, exactly 10 or exactly 13 characters. Anything else — SBNs (9 digits), truncated exports, GTIN-14s from a warehouse feed — should be rejected loudly, not padded. If you are dealing with general barcode numbers rather than books, use the GTIN check digit in Python guide instead.
Checksum, not registry
Everything above verifies internal consistency: that the digits agree with their own check digit. It does not tell you that a national ISBN agency ever assigned the number, or that a book exists under it. The fabricated 9780000000002 passes mod-10 cleanly, yet no such book needs to exist. Treat conversion and validation as the cheap first gate — the same stance we document in our methodology — and use bibliographic lookups when you need proof of registration.
Validating at scale?
If ISBNs arrive by the thousands — marketplace feeds, library imports, publisher onboarding — the same math (plus validation and the 979 handling) is available as a JSON API. One call:
curl -X POST https://codeclassify-api.rosariovitale0096.workers.dev/v1/isbn/convert \
-H "X-Api-Key: ccl_your_key" \
-H "Content-Type: application/json" \
-d '{"isbn":"0306406152"}'
{"ok":true,"input":"0306406152","valid":true,
"isbn10":"0306406152","isbn13":"9780306406157"}
Send a 13-digit ISBN and you get the ISBN-10 back (or "isbn10": null for 979 prefixes). The free tier includes 10 calls per month with no card required — details and sign-up on the API page. For a one-off spreadsheet, the bulk barcode validator checks pasted lists in the browser for free.
FAQ
Why does the last digit change when I convert an ISBN-10 to ISBN-13?
Because the two formats use different checksum algorithms. ISBN-10 ends in a mod-11 check character computed with weights 10 down to 2; ISBN-13 is a GTIN and ends in a GS1 mod-10 check digit computed with alternating weights 1 and 3. When you prepend 978 to the nine core digits, you must drop the old check character and compute a new one with the mod-10 rule. For example, ISBN-10 0306406152 becomes ISBN-13 9780306406157: the 2 disappears and a freshly computed 7 takes its place.
Can every ISBN-13 be converted back to an ISBN-10?
No. Only ISBN-13s that start with 978 have an ISBN-10 equivalent, because the ISBN-10 range was mapped one-to-one onto the 978 prefix. ISBN-13s starting with 979 (used for newer registrations, including the 979-8 block assigned to the United States) were never issued as ISBN-10s, so there is nothing to convert back to. A converter should return None or raise an error for 979 inputs rather than fabricating a 10-digit number.
Does a successful conversion mean the ISBN belongs to a real book?
No. The conversion only proves the number is internally consistent: its check digit matches the other digits. It does not confirm that the ISBN was ever assigned by a national ISBN agency or that a book was published under it. Checksum validation is a formatting filter; confirming that an ISBN is registered requires a lookup against bibliographic databases or the publisher.
Convert any ISBN in one click
Paste an ISBN-10 or ISBN-13 into the free ISBN converter — it validates the checksum, converts in either direction, and flags 979 prefixes automatically.
This guide is for general information only. Checksum validation and format conversion confirm internal consistency of a number, not that an ISBN is officially registered, assigned, or in use. For ISBN assignment consult your national ISBN agency.