In RESTful API development, URL encoding is a step that appears simple but is often done incorrectly. Improper URL encoding handling can lead to: parameter values being truncated, data parsing failures, double-encoding resulting in garbled text, SQL injection risk, and cross-language API integration issues.
This article provides systematic, code-driven guidance on the correct practices for URL encoding in API development.
The query string is the part of a URL after the ?, in the format key=value&key2=value2. Each key and value must be URL-encoded separately.
Common Mistake: String Concatenation
const url = '/api/search?q=' + userInput;
If userInput contains &, =, #, or other characters, the URL structure will be broken.
Correct Approach: Use encodeURIComponent or URLSearchParams
const url = '/api/search?q=' + encodeURIComponent(userInput);
Or use the URLSearchParams API (preferred).
// Method 1: URLSearchParams (recommended)
const params = new URLSearchParams({
q: 'New York coffee shops',
category: 'food&drink',
page: '1'
});
const url = `/api/search?${params.toString()}`;
// → /api/search?q=New+York+coffee+shops&category=food%26drink&page=1
// Method 2: Manually encode each parameter value
const url2 = `/api/search?q=${encodeURIComponent('New York coffee shops')}&category=${encodeURIComponent('food&drink')}`;
// Parsing URL parameters (backend / Node.js)
const { URL } = require('url');
const reqUrl = new URL(req.url, 'http://localhost');
const q = reqUrl.searchParams.get('q');
// Automatically decoded, directly gives 'New York coffee shops'
import requests
# The requests library handles URL encoding automatically
response = requests.get(
'https://api.example.com/search',
params={
'q': 'New York coffee shops',
'category': 'food&drink',
'page': 1
}
)
# Actual request: /search?q=New+York+coffee+shops&category=food%26drink&page=1
# Flask backend receiving (auto-decoded)
from flask import request
q = request.args.get('q') # → 'New York coffee shops' (already decoded)
<?php
// Build query string
$params = http_build_query([
'q' => 'New York coffee shops',
'category' => 'food&drink',
'page' => 1
]);
$url = "https://api.example.com/search?" . $params;
// → https://api.example.com/search?q=New+York+coffee+shops&category=food%26drink&...
// Receiving ($\_GET auto-decodes)
$q = $_GET['q']; // → 'New York coffee shops'
?>
// Sending a request (using UriComponentsBuilder)
UriComponents uriComponents = UriComponentsBuilder
.fromUriString("https://api.example.com/search")
.queryParam("q", "New York coffee shops")
.queryParam("category", "food&drink")
.build()
.encode(); // Encodes automatically
// Spring MVC receiving
@GetMapping("/search")
public ResponseEntity<?> search(@RequestParam String q) {
// q is already decoded, use it directly
return ResponseEntity.ok(searchService.search(q));
}
Double encoding is the most common URL encoding bug in API development. It occurs when a component encodes an already-encoded string a second time:
// Wrong: double encoding
const value = 'hello world';
const encoded = encodeURIComponent(value); // → 'hello%20world'
const doubleEncoded = encodeURIComponent(encoded); // → 'hello%2520world'
// % itself is encoded as %25 — the server decodes once and still gets garbage!
// Correct: only encode the raw value once
const url = `/api/greet?name=${encodeURIComponent('hello world')}`;
// → /api/greet?name=hello%20world
Common Double Encoding Scenarios
• The backend receives a parameter and places it directly into another HTTP request's URL without decoding it first.
• nginx / Apache URL rewrite rules have already encoded the URL, but the application layer encodes it again.
• Using axios's params option while also manually calling encodeURIComponent on the values.
| Scenario | Recommended Method | Notes |
|---|---|---|
| Query string parameter value | encodeURIComponent() |
Strictest — encodes &, =, + as well |
| Complete URL | encodeURI() |
Preserves URL structural characters |
| Building multiple parameters | URLSearchParams |
Automatically encodes all parameters |
| HTML form data | Browser handles automatically | Space encoded as +, others as %XX |
| axios params option | Pass object directly | axios encodes automatically — do not double-encode |
| Python requests params | Pass dict directly | requests encodes automatically |
Spaces can be encoded in two ways, each with a different context:
Modern APIs should standardize on %20 (encodeURIComponent) to avoid ambiguity around how + is interpreted in different contexts. If you must support HTML form encoding, verify how your backend decodes it.
import axios from 'axios';
// Correct: use a params object — axios encodes automatically
axios.get('/api/search', {
params: {
q: 'New York coffee shops',
category: 'food&drink'
}
});
// Actual request: /api/search?q=New%20York%20coffee%20shops&category=food%26drink
// Wrong: manual encoding + params (double encoding)
axios.get('/api/search', {
params: {
q: encodeURIComponent('New York coffee shops') // already encoded; axios encodes again!
}
});
Most modern frameworks (Express, Spring, Django, Laravel) automatically decode route parameters and query strings when parsing them. Developers do not need to manually call a decode function.
Do not manually decode in frameworks that auto-decode
If the framework already decoded the value, calling decodeURIComponent(req.query.q) again may throw an error (URIError: malformed URI sequence) when the original value contains a literal % character.
Use our free tool to test URL encoding in real time and ensure your API parameters are correct.
Go to the Tool