Blog
Wild & Free Tools

Decode JWT in Python Without a Library (Just base64)

Last updated: March 2026 4 min read
Quick Answer

Table of Contents

  1. The 5-Line Decoder
  2. Why Padding Matters
  3. Using urlsafe_b64decode
  4. When to Use PyJWT Instead
  5. Frequently Asked Questions

Decoding a JWT in Python requires zero libraries — just the standard base64 and json modules. A JWT is three base64url-encoded segments. Reading the payload is a 5-line operation. Here is everything you need.

Decode JWT Payload in Python — No Library Needed

import base64
import json

def decode_jwt_payload(token):
    payload_b64 = token.split('.')[1]
    # base64url uses - and _ instead of + and /; also needs padding
    padding = 4 - len(payload_b64) % 4
    payload_b64 += '=' * padding
    payload_bytes = base64.b64decode(payload_b64.replace('-', '+').replace('_', '/'))
    return json.loads(payload_bytes)

# Usage
token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTYiLCJleHAiOjE3MzU2ODk2MDB9.signature"
payload = decode_jwt_payload(token)
print(payload)
# {'sub': '1234567', 'exp': 1735689600}

To decode the header instead, change split('.')[1] to split('.')[0]. The same padding and character replacement logic applies.

The Base64url Padding Problem

The most common error when decoding JWT segments is an Incorrect padding exception. This happens because JWT uses base64url encoding, which strips the trailing = padding characters that standard base64 requires.

The fix is to calculate how many = characters are needed and add them back:

padding = 4 - len(payload_b64) % 4
payload_b64 += '=' * padding

The second issue is character substitution. Base64url uses - and _ where standard base64 uses + and /. Python's base64.b64decode expects standard base64, so you need:

payload_b64.replace('-', '+').replace('_', '/')

Python 3.4+ also has base64.urlsafe_b64decode() which handles the character substitution automatically, but still needs the padding fix.

Sell Custom Apparel — We Handle Printing & Free Shipping

Cleaner Version Using urlsafe_b64decode

import base64
import json

def decode_jwt_payload(token):
    segment = token.split('.')[1]
    # urlsafe_b64decode handles - and _ but still needs padding
    segment += '=' * (4 - len(segment) % 4)
    decoded = base64.urlsafe_b64decode(segment)
    return json.loads(decoded)

payload = decode_jwt_payload(your_token)

# Get specific claims
print(payload.get('sub'))       # user ID
print(payload.get('exp'))       # expiry timestamp
print(payload.get('email'))     # if present

This is slightly cleaner. Both approaches produce identical results.

When You Should Use PyJWT or python-jose

The base64 approach above only decodes — it does NOT verify the signature. That is fine for:

You should use PyJWT or python-jose when:

Never use decoded-but-unverified claims for authorization decisions in production code. Decoding tells you what the token says — verification confirms it was signed by who you trust.

Prefer a Visual Decoder? Try the Browser Tool

Paste any JWT above — instant decode with labeled claims, no Python setup required.

Open Free JWT Decoder

Frequently Asked Questions

Does this work with Python 2?

The base64.urlsafe_b64decode approach works in Python 2 as well, but Python 2 is end-of-life. Stick to Python 3.6+ for new code.

Can I decode a JWT without knowing the algorithm?

Yes — decoding does not require knowing the algorithm. The algorithm (alg) is in the header, but you do not need it to base64-decode the payload. The algorithm is only needed for signature verification.

What if the JWT has three dots instead of two?

A standard JWT has exactly two dots (header.payload.signature). If you have more dots, the token may be a nested JWT or a JWE (encrypted token). JWE cannot be decoded without the private key.

David Rosenberg
David Rosenberg Technical Writer

David spent ten years as a software developer before shifting to technical writing covering developer productivity tools.

More articles by David →
Launch Your Own Clothing Brand — No Inventory, No Risk