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:

Worked example: 0-306-40615-2

Take the classic test vector 0-306-40615-2 (ISBN-13 form 978-0-306-40615-7):

  1. 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.
  2. Build the 12-digit body. Drop the trailing 2, prepend 978: 978030640615.
  3. 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

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.