How to Properly Encode Query Parameters in REST APIs
REST API query parameters must be percent-encoded per RFC 3986 to safely include special characters like &, =, spaces, and Unicode. Use encodeURIComponent() in JavaScript, urllib.parse.quote() in Python, or URLEncoder.encode() in Java.
Why Query Parameters Need Encoding
Query strings in REST APIs use a structured format where key-value pairs are separated by & and keys are separated from values by =. If a parameter value contains any of these delimiter characters, or other reserved characters like #, +, or spaces, the query string structure breaks and the server receives incorrect data.
Consider this example: you want to search for "salt & pepper" in an API. Without encoding, the URL /search?q=salt & pepper tells the server there are two parameters: q=salt and pepper (with no value). With proper encoding, /search?q=salt%20%26%20pepper correctly sends a single parameter q with the value "salt & pepper".
Beyond correctness, proper encoding also prevents security vulnerabilities. Unencoded user input in URLs can lead to injection attacks, cache poisoning, and other exploits. Always encode parameter values before including them in URLs.
Encoding in Different Languages
Every major programming language provides built-in functions for URL encoding. Here is how to properly encode query parameters in the most popular languages.
// JavaScript
// Use encodeURIComponent() for individual parameter values
const query = 'price >= 100 & category = books';
const url = 'https://api.example.com/search?q=' + encodeURIComponent(query);
// "https://api.example.com/search?q=price%20%3E%3D%20100%20%26%20category%20%3D%20books"
// Or use URLSearchParams for multiple parameters
const params = new URLSearchParams({
q: 'price >= 100 & category = books',
page: '1',
sort: 'price'
});
const url2 = 'https://api.example.com/search?' + params.toString();# Python
from urllib.parse import urlencode, quote
# Using urlencode for multiple parameters
params = {
'q': 'price >= 100 & category = books',
'page': 1,
'sort': 'price'
}
query_string = urlencode(params)
url = f'https://api.example.com/search?{query_string}'
# Using quote for a single value
value = quote('price >= 100 & category = books', safe='')
url = f'https://api.example.com/search?q={value}'// Java
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
String query = "price >= 100 & category = books";
String encoded = URLEncoder.encode(query, StandardCharsets.UTF_8);
// "price+%3E%3D+100+%26+category+%3D+books"
// Note: URLEncoder uses + for spaces (form encoding)
// For RFC 3986 encoding, replace + with %20
String rfc3986 = encoded.replace("+", "%20");
String url = "https://api.example.com/search?q=" + rfc3986;// C#
using System.Net;
using System.Web;
string query = "price >= 100 & category = books";
// Uri.EscapeDataString follows RFC 3986
string encoded = Uri.EscapeDataString(query);
// "price%20%3E%3D%20100%20%26%20category%20%3D%20books"
// HttpUtility.UrlEncode uses form encoding (+ for spaces)
string formEncoded = HttpUtility.UrlEncode(query);
// "price+%3e%3d+100+%26+category+%3d+books"// Go
package main
import (
"fmt"
"net/url"
)
func main() {
// url.QueryEscape encodes for query strings (+ for spaces)
encoded := url.QueryEscape("price >= 100 & category = books")
// "price+%3E%3D+100+%26+category+%3D+books"
// url.PathEscape encodes for path segments (%20 for spaces)
pathEncoded := url.PathEscape("my file name.pdf")
// "my%20file%20name.pdf"
// Using url.Values for building query strings
params := url.Values{}
params.Set("q", "price >= 100 & category = books")
params.Set("page", "1")
queryString := params.Encode()
fmt.Println(queryString)
}Encoding Arrays and Nested Objects
REST APIs often need to accept arrays or nested objects as query parameters. There is no single standard for encoding these complex types, so different APIs use different conventions.
// Repeated keys (most widely supported)
// GET /api/items?tag=javascript&tag=python&tag=go
const params = new URLSearchParams();
['javascript', 'python', 'go'].forEach(tag => params.append('tag', tag));
// Bracket notation (common in PHP, Rails, Express with qs)
// GET /api/items?tags[]=javascript&tags[]=python&tags[]=go
// Must be manually encoded:
const tags = ['javascript', 'python', 'go'];
const qs = tags.map(t => 'tags[]=' + encodeURIComponent(t)).join('&');
// Comma-separated (simple but limited)
// GET /api/items?tags=javascript,python,go
const tagList = encodeURIComponent('javascript,python,go');
// Nested objects (bracket notation)
// GET /api/search?filter[status]=active&filter[min_price]=10
// In Python:
params = {
'filter[status]': 'active',
'filter[min_price]': '10'
}Always check your API documentation for the expected format. When designing a new API, the repeated keys approach is the most portable across programming languages and HTTP libraries.
Server-Side Decoding Best Practices
When handling incoming requests on the server side, most web frameworks automatically decode query parameters for you. However, there are important considerations to keep in mind for robust server-side handling.
- Most frameworks (Express, Django, Spring, ASP.NET) decode query parameters automatically. Do not manually decode them again, or you will double-decode.
- Validate decoded values against expected types and ranges before using them.
- Set maximum URL length limits at the web server level (commonly 2048 or 8192 characters).
- Handle the case where
+might mean either a space or a literal plus sign, depending on the content type. - Log the original (encoded) URL for debugging, as decoded values can be misleading in logs.
// Express.js - parameters are automatically decoded
app.get('/search', (req, res) => {
const query = req.query.q; // Already decoded
// Do NOT do: decodeURIComponent(req.query.q)
// Validate the input
if (typeof query !== 'string' || query.length > 200) {
return res.status(400).json({ error: 'Invalid query parameter' });
}
// Use the decoded value safely
const results = search(query);
res.json(results);
});Security Considerations
Proper query parameter encoding is a key part of API security. Here are the most important security practices to follow.
- Never trust client-side encoding. Always validate and sanitize decoded parameters on the server, even if the client encoded them correctly.
- Watch for double-encoding attacks. An attacker might encode malicious characters twice to bypass input filters that only decode once. Ensure your security filters see the fully decoded values.
- Prevent path traversal. Encoded sequences like
%2e%2e%2f(../) in path parameters can be used to access files outside the intended directory. Decode and validate paths before file operations. - Use parameterized queries. Even after URL decoding, never concatenate user input directly into SQL or NoSQL queries. Use parameterized queries or an ORM.
- Limit URL length. Extremely long query strings can cause denial-of-service. Configure your web server to reject URLs beyond a reasonable length.
- Encode output too. When reflecting query parameter values back in HTML responses, apply HTML entity encoding to prevent cross-site scripting (XSS) attacks.