To Base64 decode in Python, call base64.b64decode() on the string and then .decode("utf-8") on the bytes it returns. For a quick look at a value without opening a REPL, paste it into the free AyeTab Base64 decoder, which runs in the browser and uploads nothing.
The two-line version
import base64
raw = base64.b64decode("aGVsbG8gd29ybGQ=")
text = raw.decode("utf-8") # 'hello world'
b64decode always returns bytes, not a string. That is on purpose: Base64 can carry an image or a gzip stream as easily as text, and Python will not guess. Call .decode() only when you know the payload is text, and name the encoding.
The errors you will actually hit
binascii.Error: Incorrect padding. Standard Base64 comes in blocks of four characters, with = filling the last block. Strings copied out of URLs, logs or JWTs often lose that padding. Add it back:
def b64decode_loose(s: str) -> bytes:
return base64.b64decode(s + "=" * (-len(s) % 4))
Junk characters silently dropped. By default b64decode discards characters outside the Base64 alphabet before checking padding, so a stray space or quote may not raise anything. Pass validate=True when you want bad input to fail loudly with binascii.Error.
UnicodeDecodeError on .decode(). The decoded bytes are not UTF-8 text. Either they are binary (write them to a file with open(path, "wb")) or they use another encoding such as Latin-1.
URL-safe Base64
RFC 4648 defines a second alphabet for URLs and file names: - replaces + and _ replaces /. JWTs, many API tokens and signed URLs use it. Decode those with:
base64.urlsafe_b64decode(token_part)
In recent Python versions urlsafe_b64decode no longer requires padding by default, but older versions do, so the padding helper above is still useful if your code runs on several interpreters.
Decoding a file
with open("payload.b64", "rb") as f:
data = base64.b64decode(f.read())
with open("payload.bin", "wb") as f:
f.write(data)
For large files the base64 command line tool (base64 -d on Linux) does the same job in one line, without loading the whole file into memory in your own code.
Decoding a field from an API response
The most common real case is a JSON response where one field is Base64. Decode the field, not the whole body:
import base64, json
body = json.loads(response_text)
payload = base64.b64decode(body["data"])
record = json.loads(payload) # if the field held JSON
json.loads accepts bytes directly, so you can skip .decode() when the decoded payload is itself JSON in UTF-8. If json.loads fails, print the first few bytes with payload[:20]. A leading b"\x1f\x8b" means the data is gzip-compressed, so pass it through gzip.decompress() first. A leading b"%PDF" or b"\x89PNG" means you have a file, and it belongs on disk rather than in a string.
This pattern turns up often in message queues, cloud function events and webhook relays, where the platform wraps your payload in Base64 so binary content survives the trip.
Checking your output without code
When you are debugging an agent run, a webhook or an API response, you often just want to see what a Base64 field holds before writing anything. Here is where each option fits:
| Situation | Best tool |
|---|---|
| One string, need to read it now | AyeTab Base64 Encode/Decode |
| Decoding inside a script or pipeline | Python base64 module |
| Large file on disk | base64 -d |
| JWT segments | AyeTab JWT Debugger |
The AyeTab page has Auto, Encode and Decode buttons and a Text and a Base64 pane. It decodes to UTF-8, shows "Decode failed" on invalid input, and keeps the string in your browser tab, which matters when the value is a token or a customer payload. No account, free, and it works offline after the page loads.
If you decode values all day, the AyeTab Chrome extension puts the tool on your new tab page and in a side panel, and smart paste offers the decoder as soon as you paste a Base64 string.
When Python is the better choice
For anything repeatable, such as decoding a field across thousands of log lines, write the code. A browser tool is for looking; Python is for processing. For exotic variants like MIME with line breaks or PEM blocks, Python's base64 and binascii modules, or a site like base64.guru, give you more control.