Why Your API Calls Break: A Developer's Guide to URL Encoding
URL encoding is one of those things that seems trivial until it isn't. You fire off an API call, the server returns a 400, and you stare at the URL wondering what went wrong. Nine times out of ten, the answer is encoding.
Here are the three URL encoding mistakes that cost developers hours of debugging — and how to fix them in 30 seconds.
1. The Double-Encoding Trap
This is the most insidious bug. You encode a URL once, pass it through a framework or middleware that encodes it again, and suddenly hello%20world becomes hello%2520world (the % got encoded to %25).
The symptom: Downstream services receive garbled data. OAuth callback URLs fail silently. API gateways forward corrupted parameters.
The fix: Before encoding, decode first to establish a clean baseline:
// Bad: might double-encode
const url = "https://api.example.com?q=" + encodeURIComponent(input);
// Good: decode first if input might already be encoded
const clean = decodeURIComponent(input); // throws if malformed
const url = "https://api.example.com?q=" + encodeURIComponent(clean);
If you suspect double-encoding, look for %25 in your output — that's the smoking gun (% encoded as %25).
2. The + vs %20 Confusion
Both represent a space, but they're NOT interchangeable across contexts.
| Context | Encoding | Works? |
|---|---|---|
| Query string (form data) | + |
✅ |
| Query string (manual) | %20 |
✅ |
| Path segment | + |
❌ Broken |
| Fragment identifier | + |
❌ Broken |
| Everywhere | %20 |
✅ Always |
The fix: Use %20 when you're not sure. It works universally. Use + only for application/x-www-form-urlencoded form submissions.
JavaScript's encodeURIComponent() outputs %20 — the safe choice. Python's urllib.parse.quote() does the same by default.
3. Encoding the Wrong Thing
The most common pattern I see in code reviews:
// Wrong: encodeURI on a full URL
const url = encodeURI("https://api.example.com/search?q=hello world");
// Result: "https://api.example.com/search?q=hello%20world" (works, but fragile)
// Wrong: encodeURIComponent on a full URL
const url = encodeURIComponent("https://api.example.com/search?q=hello world");
// Result: "https%3A%2F%2Fapi.example.com%2Fsearch%3Fq%3Dhello%20world" (broken)
The rule: encodeURI() for entire URLs (preserves ://, ?, &, #). encodeURIComponent() for individual parameter values. Never mix them up.
Quick Debugging Workflow
When an API call fails with encoding issues, I use this 3-step process:
- Decode the URL to see what's actually there — strip away the percent-encoding and read the raw string
- Re-encode a clean version — start fresh from the decoded text
- Compare — if the re-encoded version works but the original doesn't, you had double-encoding or mixed encoding
I keep a free URL Encoder/Decoder bookmarked for exactly this: paste a broken URL, hit Decode, and instantly see what went wrong. No signup, no server uploads — everything runs in your browser.
🔗 Try it: codetoolbox.pro/tools/url-encoder.html
Modern Alternative: URLSearchParams
If you're writing modern JavaScript, the URL and URLSearchParams APIs handle encoding automatically:
const params = new URLSearchParams({ q: "hello world", page: "1" });
const url = "https://api.example.com/search?" + params.toString();
// "https://api.example.com/search?q=hello+world&page=1"
This is cleaner, safer, and immune to double-encoding. Use it for new code.
What URL encoding bugs have burned you in production? Drop a comment — I'm collecting war stories.