jwtpeek
Decode and inspect a JWT in your terminal. See the header, the payload, and a human-readable expiry verdict — without pasting your token into a website. Zero dependencies, fully offline, nothing uploaded.
jwtpeek eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
Header
{
"alg": "HS256",
"typ": "JWT"
}
Payload
{
"sub": "1234567890",
"name": "John Doe",
"iat": 1516239022
}
Claims
issued 2018-01-18 01:30:22 UTC 8y 150d ago
Why
Debugging auth means constantly asking "what's actually in this token, and has it expired?" The usual answers are bad: paste it into jwt.io (your token — often a live credential — now lives in a browser tab and maybe a third party's logs), or hand-assemble a pipeline nobody remembers:
echo "$TOKEN" | cut -d. -f2 | base64 -d 2>/dev/null | python -m json.tool # and base64 -d vs -D differs across macOS/Linux…
jwtpeek is one command. It runs entirely on your machine, makes no network
requests, and prints the time claims (exp, iat, nbf, …) as real dates plus
"expires in 3h 21m" / "EXPIRED 2d ago".
Decode, not verify — read this
jwtpeek never checks the signature. It shows you what a token says, not whether it's authentic. Anyone can forge a token that decodes cleanly. Never trust decoded contents for an authorization decision — verify the signature against the issuer's key first (that needs a secret/public key, which is out of scope for a decoder).
Usage
jwtpeek <token> # header, payload & expiry (human-readable)
jwtpeek <token> --json # {header, payload, signature, expired, notYetValid}
jwtpeek <token> --header # only the decoded header (JSON)
jwtpeek <token> --payload # only the decoded payload (JSON)
pbpaste | jwtpeek # read the token from stdin
echo "$AUTH_HEADER" | jwtpeek # a leading "Bearer "/"Authorization:" is stripped
Pipe-friendly: the decoded output goes to stdout, the "not verified" safety
note goes to stderr, so jwtpeek "$t" --json | jq .payload stays clean.
Scripting with the exit code
0 decoded OK and not expired (or no exp claim)
1 decoded OK but the token is expired
2 not a valid JWT (decode error)
jwtpeek "$TOKEN" >/dev/null 2>&1 && echo "still valid" || echo "expired or invalid"
Install
pip install jwtpeek # then: jwtpeek <token>
# or, without installing:
pipx run jwtpeek <token>
Requires Python ≥ 3.8. There is also a byte-for-byte Node port:
npx jwtpeek <token> (jwtpeek).
License
MIT
Metadata
Release files for jwtpeek 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| jwtpeek-0.1.0.tar.gz | 10.2 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| jwtpeek-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 19.1 kB
Release files / jwtpeek-0.1.0.tar.gz
| Download URL | jwtpeek-0.1.0.tar.gz |
|---|---|
| Size | 10.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
8ee6b9af9e8a20469b0e3387a240cf88025b8a3cd208b9b7fedf07440d5d60a0
|
|
BLAKE2b-256 checksum How to use checksums |
bac228c5bc8c7dcc40a51ce8810809fe033f2df9a12370c30ea830b0065f840e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.11.7
|
Release files / jwtpeek-0.1.0-py3-none-any.whl
| Download URL | jwtpeek-0.1.0-py3-none-any.whl |
|---|---|
| Size | 8.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
055518335c47da1701ea152b82a1b25308c46aa9abaaedbf411d121ea580aa2e
|
|
BLAKE2b-256 checksum How to use checksums |
c96571f8d329b49ab8b8ef81c992c750869c5af6d52b26fd71188d1cf52b2e32
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.11.7
|