Skip to main content
Collection

Debug an API request, step by step

A request is failing and the error message isn't telling you why. These eleven tools take you from rebuilding the call by hand to reading the response, decoding the token and reproducing the whole thing — all in your browser, nothing uploaded.

11 tools5 stepsFree, no signup

The short answer

To debug a failing API request: rebuild the call as a curl command so you can run it in isolation, check the response status code and headers before touching your code, pull the exact request out of a HAR export if it only fails in the browser, decode the auth token to confirm it hasn't expired, then convert the working call back into code. Most failures are auth, headers or a redirect — not your logic.

Rebuild the call

get it running in isolation

Before changing any code, reproduce the request outside your app. A curl command you can paste into a terminal is the smallest thing that either fails the same way or doesn't — and that alone tells you whether the bug is in the request or in everything around it.

Read the response

before you touch the code

The status code and the response headers usually name the problem outright. A 401 with no WWW-Authenticate, a 301 you didn't follow, a CORS header that isn't there — all visible before you open a debugger.

Dig through the network log

when it only fails in the browser

If the call works in curl but fails in the app, the difference is in what the browser actually sent. Export a HAR from DevTools and read it here rather than scrolling the Network tab.

Check the auth

the usual culprit

Most 401s and 403s are a token that expired, a token for the wrong audience, or a signature computed over the wrong bytes. All three are readable without asking anyone for credentials.

Reproduce and hand off

make it someone else's repro too

Once you know what the working call looks like, get it back into code — and if you're blocked on an endpoint that isn't built yet, stand in a fake response so you can keep moving.

Work outside-in, not inside-out

The instinct when a request fails is to open the code that makes it. That's usually the last place worth looking. An HTTP call has four layers that can each break independently — the request you built, the network path it took, the server's response, and the code that interpreted that response — and only the last one is yours.

So start at the outside. Rebuild the request as a standalone command with cURL Command Builder and run it. If it fails identically, the bug is in the request or the server, and every line of your client code is innocent. If it succeeds, the bug is in how your app builds or sends the call, and you've just eliminated the entire server side. One command, half the search space gone.

The status code is a diagnosis, not a label

Status codes get skimmed. That's a mistake, because the distinctions between neighbouring codes are exactly the information you're missing. Look the exact one up in HTTP Status Codes rather than pattern-matching on the first digit.

Common mistakes
  1. 1
    Treating 401 and 403 as the same problem

    A 401 means the server couldn't authenticate you — missing or unreadable credentials. A 403 means it authenticated you fine and that identity isn't allowed. One points at your token, the other at scopes.

  2. 2
    Debugging a 422 as though it were a 400

    A 400 says the request was malformed. A 422 says it parsed cleanly and the contents were rejected. If you're getting 422s your JSON is fine and your values aren't — stop reformatting the body.

  3. 3
    Not following a redirect, then blaming auth

    A 301 or 308 your client didn't follow looks like a response with no body. Worse, if the redirect crossed from http to https your Authorization header may have been dropped in transit, producing a 401 that has nothing to do with your token. URL Canonicalizer will show you where the URL actually lands.

When it works in curl but fails in the browser

This is the most common shape of an API bug, and it almost always comes down to something the browser adds or refuses that curl doesn't.

CORS is the usual answer. The browser sends a preflight OPTIONS request first, and if the response doesn't carry the right Access-Control-Allow-* headers, the real request never leaves — which surfaces in your code as a network error with no status at all. Curl doesn't preflight, so it sails through and tells you nothing. Paste the response headers into HTTP Header Analyzer and the missing one is obvious.

Cookies are the second answer. Credentials attach automatically in the browser according to SameSite rules and the fetch credentials mode, and not at all in curl unless you say so.

Worked example — A 401 that had nothing to do with the token
Symptom
POST /v2/orders returns 401 in the app, 200 from cURL Command Builder
HAR entry
Two requests logged, not one — an OPTIONS then the POST
OPTIONS response
204, but no access-control-allow-headers line
Cause
Preflight rejected the custom Authorization header, so the POST was never sent
Fix
Server-side: add authorization to Access-Control-Allow-Headers

The browser reported this as a 401 because the failed preflight surfaced as an auth error. The token was valid the whole time — JWT Expiry Checker would have cleared it in seconds.

The way through both is to stop guessing at the difference and read it: export a HAR from the Network tab, narrow it with HAR File Formatter, and compare the headers the browser actually sent against the ones you think you sent. If the log is more about speed than correctness, HAR File Performance Analyzer reads the same file for timing instead.

Read the token before you regenerate it

When auth fails the reflex is to issue a fresh token. Read the old one first with JWT Decoder — it takes ten seconds and it tells you whether the token was even the problem.

A JWT is three base64url segments, and the middle one is not encrypted. Anyone holding the token can read its claims, which is precisely why you can debug with it and why you should never put anything secret in there. Check three things: exp, for whether it's expired; nbf, for whether it isn't valid yet — clock skew between two servers produces genuinely baffling intermittent failures; and aud, for whether the token was minted for the service you're calling. A perfectly valid token for the wrong audience is rejected exactly like a forged one.

Webhook signatures fail for a different reason: almost always the HMAC was computed over a re-serialised copy of the JSON rather than the raw bytes that arrived. Check the pair with Webhook Signature Validator, and if you're implementing the signing side, HMAC Generator produces the same digests to compare against.

Which one do I want?

Several tools here overlap. The short version:

JWT Decoder vs JWT Expiry CheckerYou want to read the whole payload — audience, scopes, custom claims, issuer.You only want the yes/no: is this token live right now?
HAR File Formatter vs HAR to cURL ExtractorYou're still hunting for which of 300 requests is the broken one.You've found it and want it as a runnable command to share or rerun.
cURL Command Builder vs HTTP Request Header BuilderYou're composing a full request — method, body, auth, flags.The request is fine and you're working out which headers to send.
Mock HTTP Response Builder vs cURL to Code ConverterThe endpoint doesn't exist yet, or you need to force an error path.The call works and you want it as code in your language.

Why this set

Tokens never leave the tab

Decoding, signature checks and HAR parsing all run client-side. Nothing you paste is uploaded, which is what makes it safe to debug with a real token instead of a sanitised one.

No install, no signup

Every tool here opens and runs immediately. There's nothing to add to your machine to inspect one header on someone else's laptop.

They chain together

The output of one step is the input to the next — HAR entry to curl command, curl command to code — so the workflow reads in one direction.

Questions

Is it safe to paste a real access token into a JWT decoder?

Into this one, yes — decoding happens entirely in your browser and the token is never sent anywhere. That said, treat any token you paste into any online tool as spent: rotate it afterwards if it's long-lived or production-scoped, since you can't audit what a page you don't control does with it.

Why does my request work in curl but fail in the browser?

Almost always CORS or cookies. The browser sends a preflight OPTIONS request that curl doesn't, and if the response lacks the right Access-Control-Allow-* headers the real request is blocked before it's sent. Cookies also attach automatically in the browser under SameSite rules and not at all in curl. Export a HAR and compare what was actually sent.

What's the difference between a 401 and a 403?

A 401 means the server couldn't authenticate you — missing, malformed or unreadable credentials. A 403 means it authenticated you fine and that identity isn't permitted to do this. A 401 points at your token; a 403 points at permissions or scopes.

Can I check whether a webhook signature is valid without writing code?

Yes. Paste the raw payload body exactly as received, your signing secret and the signature from the provider's header. The most common cause of a mismatch is computing the HMAC over a re-serialised version of the JSON rather than the raw bytes — even a whitespace difference changes the result.

Do these tools work offline?

Once a page has loaded, the tools that only transform what you paste — decoding, header parsing, HAR filtering, code conversion — keep working without a connection, because the processing runs in your browser rather than on a server.

Related articles

All articles

Love the tools? Lose the ads.

One payment clears every ad from your account, for good. No subscription, no tracking.