---
title: Is this JWT expired? Read exp and iat with the Epoch Converter
description: Users get 401 "token expired" a few minutes after logging in. The answer is inside the token: its exp and iat claims are epoch times, seconds since 1970 UTC. Convert them and you know when the token was issued, when it dies, and whether the server or the token is wrong. Here's how with the free Epoch Converter, plus the jq one-liner that decodes a JWT locally.
canonical: https://watchfor.io/blog/how-to-convert-epoch-time
---

# Is this JWT expired? Read exp and iat with the Epoch Converter

2026-10-04 · WatchFor Team · Guides · guides, api, security

Users get 401 "token expired" a few minutes after logging in. The answer is inside the token: its exp and iat claims are epoch times, seconds since 1970 UTC. Convert them and you know when the token was issued, when it dies, and whether the server or the token is wrong. Here's how with the free Epoch Converter, plus the jq one-liner that decodes a JWT locally.

To convert epoch time to a date, read the number as seconds since
1970-01-01 00:00:00 UTC; for a JWT, that's exactly what the `exp`
(expires) and `iat` (issued at) claims hold. Subtract `iat` from `exp` and
you have the token's lifetime; compare `exp` with the current epoch and you
know whether it has expired.

The free [Epoch Converter](/epoch-converter) shows any epoch value in UTC,
your local time and as "in 39 seconds" or "3 minutes ago", next to the
current epoch ticking live. It also converts a date back to epoch seconds
and milliseconds, and runs entirely in your browser.

## What this tool answers

*When is this epoch value, and how far is it from now?* Reach for it with a
token's `exp`, `iat` or `nbf`, a cookie or signed-URL expiry, a cache entry's
deadline, a certificate field dumped as a number, or whenever a system
hands you a 10- or 13-digit number where a date should be.

## A real example

### Converting epoch time in a JWT's exp claim

The complaint: API calls start failing with `401 token expired` a few
minutes after login, and refreshing the page fixes it for another few
minutes. Is the token really expiring that fast, or is a server clock off?

We minted a test token the way a typical auth service would (HS256,
throwaway secret) and decoded its payload. A JWT is three base64url parts
separated by dots; the middle part is the claims, and decoding it needs no
key:

```json
{
  "sub": "user_8412",
  "iss": "https://auth.example.com",
  "aud": "api.example.com",
  "iat": 1791064975,
  "exp": 1791065275
}
```

We pasted the `exp` value into the [Epoch Converter](/epoch-converter):

![The Epoch Converter with 1791065275 entered and the current Unix timestamp 1791065236 at the top: detected unit seconds, UTC 2026-10-03T22:07:55Z, local Sat Oct 3 2026 22:07:55, relative in 39 seconds](/blog/how-to-convert-epoch-time-exp.webp)

- **Detected unit: seconds**, as RFC 7519 requires for JWT dates.
- **UTC 2026-10-03T22:07:55Z**, and **Relative: in 39 seconds**, against
  the current epoch at the top (1791065236).
- `iat` 1791064975 converts to **22:02:55Z**.

**The answer:** the token lives for 1791065275 − 1791064975 = **300
seconds, five minutes**, and it had 39 seconds left when we looked. The
401s are the token doing what it was told, not clock drift. Two fixes,
depending on intent: refresh the access token before `exp` (with a refresh
token, a minute or so early), or raise the lifetime where the token is
minted. If the code says `expiresIn: 300` and someone meant five hours,
that's the bug; the
[jsonwebtoken](https://github.com/auth0/node-jsonwebtoken) library reads a
plain number as seconds and a string without a unit, like `"300"`, as
milliseconds.

If `exp − iat` had looked right (say, an hour) and tokens still died early,
the next suspect is the clock on the server that validates them: compare its
`date +%s` with the current epoch the converter shows.

## How to read the results

| What you see | What it means |
| --- | --- |
| **Current Unix timestamp** | Now, in seconds and milliseconds, updated every second. For a token: if `exp` is smaller than this number, it has expired. |
| **Detected unit** | Seconds up to 11 digits, milliseconds up to 14, microseconds up to 17, nanoseconds beyond. |
| **UTC** | The instant in ISO 8601. JWT, OAuth and most logs mean UTC. |
| **Local (zone)** | The same instant in your browser's timezone. |
| **Relative** | "in 39 seconds" means still valid; "3 minutes ago" means already expired. |
| **Seconds / Milliseconds** | The value in both units, for code that expects the other one. |
| **Date → timestamp** | A local date and time turned into epoch seconds and milliseconds, for setting an `exp` or `nbf` by hand. |

## More ways to use it

- **An OAuth `expires_in` versus `expires_at`.** `expires_in: 3600` is a
  duration; `expires_at: 1791068400` is an epoch. Only the second goes in
  the converter.
- **Signed URLs that stopped working.** Many signed-URL schemes carry an
  absolute expiry such as `Expires=1791068400` in the query string; paste it
  to see whether the link died at the moment you'd expect.
- **Session cookies.** Browser dev tools show cookie expiry as a date, but
  server-side session stores often keep it as epoch; check the two agree.
- **Cache and lock keys.** A Redis or Valkey value storing a deadline as
  epoch milliseconds converts the same way.

## Tips and gotchas

- **JWT dates are seconds, never milliseconds.** RFC 7519 defines
  `NumericDate` as seconds since the epoch. A token minted with
  `Date.now()` gets a 13-digit `exp`; the converter will happily read it as
  milliseconds, but a JWT library reads it as seconds, about 56,700 years in
  the future, so the token effectively never expires.
- **Allow a little clock skew.** RFC 7519 lets validators accept a small
  leeway, "usually no more than a few minutes", because servers' clocks
  differ. If tokens fail only on one server, check that server's NTP sync.
- **`nbf` fails the other way.** "Not before" in the future rejects a brand
  new token when the issuer's clock runs ahead of the validator's.
- **Don't paste live tokens into websites.** The claims are readable by
  anyone, and a valid token is a credential. Decode locally (below) and
  paste only the numbers. This converter never sends what you type anywhere,
  but the habit matters more than the tool.

## Check it from the command line too

Decode the payload of a token in `$TOKEN` with `jq`, converting the dates
on the way. base64url uses `-` and `_`, so swap them back first:

```bash
jq -R 'split(".")[1] | gsub("-";"+") | gsub("_";"/") | @base64d | fromjson
  | {iat: (.iat|todate), exp: (.exp|todate), lifetime_s: (.exp-.iat)}' <<<"$TOKEN"
```

```text
{
  "iat": "2026-10-03T22:02:55Z",
  "exp": "2026-10-03T22:07:55Z",
  "lifetime_s": 300
}
```

Seconds left, from the shell; a negative number means expired:

```bash
echo $(( 1791065275 - $(date +%s) ))
date -u -d @1791065275
```

```text
-169
Sat Oct  3 10:07:55 PM UTC 2026
```

We ran that a few minutes after the screenshot, so the token had been dead
for 169 seconds. On macOS use `date -u -r 1791065275`. For the general case
of converting timestamps from headers and logs, including the
seconds-versus-milliseconds trap, see
[how to convert a Unix timestamp](/blog/how-to-convert-a-unix-timestamp).
How tokens fit into API checks is covered in the
[API monitoring guide](/blog/api-monitoring-guide).

## FAQ

**How do I check if a JWT is expired?**
Decode the middle part of the token (base64url), read `exp`, and compare it
with the current epoch. If `exp` is smaller, it has expired. The
[Epoch Converter](/epoch-converter) shows both numbers and the difference
in words.

**What format is the exp claim in a JWT?**
Seconds since 1970-01-01 00:00:00 UTC, the `NumericDate` type from RFC
7519. It is not milliseconds and has no timezone.

**What is the current epoch time?**
The number of seconds since 1970-01-01 00:00:00 UTC, right now. Run
`date +%s` in a terminal or look at the top of the
[Epoch Converter](/epoch-converter), which ticks every second.

---

Canonical page: https://watchfor.io/blog/how-to-convert-epoch-time · All articles: https://watchfor.io/blog/all.md · Site guide: https://watchfor.io/llms.txt
