Call the free Base32 decode API endpoint
One POST to the free Base32 decode API endpoint gives you the decoded bytes with nothing wrapped around them. Put the encoded string in a JSON body under the key data.
curl -X POST https://aisenseapi.com/services/v1/base32_decode \
-H "Content-Type: application/json" \
-d '{"data":"JBSWY3DPEB3W64TMMQ======"}'Hello worldThat is eleven bytes, printed straight to the terminal. A plain text body works as well. Post the encoded string on its own and the same eleven bytes come back.
curl -X POST https://aisenseapi.com/services/v1/base32_decode \
-H "Content-Type: text/plain" \
-d 'JBSWY3DPEB3W64TMMQ======'Text round-trips as UTF-8. IJWMHJLCYOTHE=== decodes to the eight UTF-8 bytes of Blåbær, and IFESAU2FJZJUK=== decodes to AI SENSE. To go the other way, use the Base32 Encode API Endpoint.
What comes back
There is no response field to read here. The body is the decoded data. What tells you how to handle it are the headers.
| Header | Value | Description |
|---|---|---|
| content-type | application/octet-stream | Always this, on every successful decode. The endpoint ignores the Accept header, so sending Accept: application/json still returns octet-stream bytes. |
| content-disposition | attachment; filename="decoded_data.bin" | A browser offers the result as a download named decoded_data.bin. Command line clients ignore the header and print the bytes. |
| content-length | Size of the decoded output in bytes | 11 for the Hello world example above. |
Because the output is raw, redirect it to a file whenever the bytes are not printable. Add -o decoded.bin to the curl command and you have the original binary on disk.
Why bytes instead of JSON
- Base32 carries arbitrary data
An encoded string may hold a key, a small image or a compressed blob, not only text. Bytes like those have no safe home inside a JSON string without encoding them a second time, which defeats the point of decoding. Returning the bytes as they are keeps the round trip honest.
- The content type says so
Every successful decode is labelled application/octet-stream. Content negotiation is not supported on this route, so an Accept header changes nothing about the response.
- Errors break the pattern
A failure is the one case that returns JSON, with a 400 status and
application/json; charset=utf-8as the content type. Status code and content type agree, so either one is enough to branch on.
Common uses
Inspecting TOTP secrets
Two-factor secrets in otpauth:// enrollment URIs are conventionally Base32. Decode a throwaway test secret to see how many bytes it really carries before you wire up a generator.
Reading DNS labels
Resolvers may change the case of a hostname, so data hidden in a label is usually Base32. Decode a captured label to recover what it held.
Recovering filenames
Case-insensitive filesystems push tools towards Base32 names. Turn one back into the identifier it stands for.
Checking dictated codes
Voucher codes and pairing strings get read down a phone line. Decode what was typed in to confirm it matches what was printed.
What the free Base32 decode API endpoint accepts
The decoder is relaxed about presentation and strict about the alphabet. Case does not matter and padding is optional, so all four of these return the same eleven bytes:
JBSWY3DPEB3W64TMMQ======
jbswy3dpeb3w64tmmq
JbSwY3dPeB3W64tMmQ
JBSWY3DPEB3W64TMMQ==Any character outside A-Z and 2-7 fails the whole request. JBSWY3DPEB3W64TMM0====== ends in a zero, which the alphabet has no symbol for. A space inside the string is rejected the same way. Both return HTTP 400:
{"error":"Invalid Base32 input."}An empty data value, a missing data key and a GET request all return 400 too, with a different message:
{"error":"No data to decode or invalid input."}Check the status before treating the body as data. There is no partial decode.
Privacy and limits
Base32 is a representation, not protection. Anyone holding the string can decode it. Never send a live two-factor secret, key or credential to a public endpoint - decode those in your own process.
Payloads travel in the POST body rather than the URL, so they stay out of request paths and server logs of paths. The service-wide limit is 5000 requests per IP per 24 hours, with no key and no account. Browse the Encoding APIs hub for the neighbouring encode and decode routes, or the full list of free public REST APIs for everything else on the same base URL.