401 Unauthorized

API 401 With Valid Token – The Real Fix

Server & Cloud Intermediate 👁 8 views 📅 Jun 20, 2026

You get a 401 error even with a valid token. I'll show you why that happens and how to fix it in 4 steps.

When This Happens

You're calling an API – maybe it's a Microsoft Graph endpoint, a custom REST service on IIS, or a third-party payment gateway. You generated the token five minutes ago. You pasted it into Postman or your app code. It looks right. But the server kicks back a 401 Unauthorized. No details. Just that same error.

I had a client last month whose whole ETL pipeline stopped because of this. Their token worked for 10 minutes, then gave 401. They spent a day blaming the cloud provider. Turned out their server clock was 8 minutes off.

Root Cause – It's Almost Always Clock Skew

Most APIs that use JWT (JSON Web Tokens) check the token's iat (issued at) and exp (expiration) claims against the server's current time. If your server or client machine has a clock that's even 30 seconds off, the token gets rejected. The API sees the token as not yet valid (if your clock is behind) or expired (if ahead).

Other common causes: the token is missing the Authorization header, you're using the wrong token type (Bearer vs Basic), or the token audience (aud) doesn't match the API's identifier. But in my experience, 90% of 401s with a valid token come from time sync issues.

How to Fix It

Step 1 – Check Clock Sync on Both Ends

  1. On the server (Windows or Linux), run:
    w32tm /query /status (Windows) or timedatectl (Linux).
  2. If the server isn't syncing with an NTP source, set it up. For Windows:
    w32tm /config /syncfromflags:manual /manualpeerlist:pool.ntp.org /reliable:yes /update
    net stop w32time && net start w32time
  3. On the client machine, do the same. A 30-second difference will break tokens.
  4. After sync, regenerate the token and test again.

Step 2 – Verify the Header

Open the request in Postman or check your code. Make sure the header is:
Authorization: Bearer YOUR_TOKEN_HERE
No quotes, no extra spaces. A missing Bearer scheme is a classic mistake. I once spent two hours debugging because a junior dev had Authorization: Token YOUR_TOKEN_HERE instead.

Step 3 – Check Token Expiry and Claims

Decode the JWT using a tool like jwt.io. Look at the exp claim. If it's set to a time already passed (because of clock skew), the token is invalid. Also check aud – it should match the API's identifier exactly. For Microsoft Graph, it's https://graph.microsoft.com.

Step 4 – Test with a Fresh Token

Don't reuse a token from an hour ago. Generate a new one right before the request. This eliminates stale credentials.

If It Still Fails

Check the server logs. On IIS, look at the Failed Request Tracing logs. On Azure, use Application Insights. Often the API returns a more detailed error in the response body – look in the www-authenticate header for a clue like invalid_token or token_expired.

Also try a different token issuer. If you're using a self-signed certificate, the API might not trust it. Switch to a well-known identity provider (Azure AD, Auth0) to rule that out.

If all else fails, add a 5-second delay before the request. That works around micro-second clock mismatches in virtualized environments. I've seen it fix 401s in AWS EC2 instances where hypervisor time drift happens.

Real talk: clock skew is the silent killer of API calls. Sync your damn clocks first, and you'll save hours of debugging.

Was this solution helpful?