Skip to main content

Percent Encoding for API Query Parameters: Patterns, Pitfalls, and Real Examples

A practical guide to percent encoding complex query parameters in API clients — arrays, nested objects, tokens, and the encoding rules that differ by API style.

Published
#url-encoding #api #query-parameters #percent-encoding #developer-tools

Percent Encoding for API Query Parameters: Patterns, Pitfalls, and Real Examples

Percent encoding is one of those topics where the basic rule — replace unsafe bytes with %XX — sounds straightforward, but building a production API client uncovers a dozen edge cases in the first afternoon. This guide focuses on query parameter encoding specifically, with the emphasis on patterns that trip up API consumers rather than browser URL handling.

The Characters That Break Query Strings

RFC 3986 splits characters into three groups: unreserved (always safe), reserved (structural meaning in URLs), and everything else (must be encoded). For query parameter values, the practical list of characters that must always be encoded is:

| Character | Encoded form | Why it matters | |-----------|-------------|----------------| | & | %26 | Splits parameter pairs | | = | %3D | Splits key from value | | + | %2B | Decoded as space by many servers | | # | %23 | Marks fragment start — server never sees it | | Space | %20 | Not allowed bare in a URL | | % | %25 | Prefix for encoding sequences |

The % character is the one developers most often forget. If your value already contains a percent sign — a coupon for "20% off" or a SQL LIKE pattern with %wildcard% — encoding it is mandatory. Failing to encode % produces a URL where the parser tries to interpret random characters as hex sequences and either throws an error or silently reads garbage.

Real example. Suppose a price filter value is ≤25% and your API expects it as a query parameter:

Raw value:   ≤25%
UTF-8 bytes: E2 89 A4 32 35 25
Encoded:     %E2%89%A425%25

Notice that 25 (the digits) stays unchanged — only the multi-byte sequence and the bare % get encoded. The correct URL segment is:

?max_price=%E2%89%A425%25

I tested this against a price-filter endpoint that accepted raw % by mistake. Every request for "25% discount" silently returned products with 0% discount because the server decoded %di as an invalid escape and dropped the token.

Array and Multi-Value Parameters: No Single Standard

This is where API encoding diverges most sharply. Four conventions for passing an array of values exist in the wild, and they encode differently:

Repeated keys (used by PHP, Python's urllib, many REST frameworks):

?color=red&color=blue&color=green

Each value is individually encoded. This is the safest and most broadly supported form.

Bracket notation (Ruby on Rails, PHP arrays):

?color[]=red&color[]=blue

The [ (0x5B) and ] (0x5D) are sub-delimiters in RFC 3986 and are technically allowed unencoded in query strings, but some strict parsers require them encoded as %5B and %5D. When I send bracket notation to a Go API using net/url, it percent-encodes the brackets automatically, producing color%5B%5D=red — which Rails accepts but many APIs reject because they expect bare brackets.

Comma-separated values (Elasticsearch, some GraphQL endpoints):

?color=red%2Cblue%2Cgreen

The commas are encoded as %2C when the comma is part of the value rather than a structural separator. If the API expects bare commas as a multi-value separator, don't encode them. This distinction must come from the API documentation, not guessing.

JSON in a query parameter (occasionally used for complex filter objects):

?filter=%7B%22color%22%3A%5B%22red%22%2C%22blue%22%5D%7D

This is {"color":["red","blue"]} fully percent-encoded. Every non-unreserved character in the JSON string — braces, quotes, colons, brackets — must be encoded. The result is often 2–3× longer than the raw JSON. Per Fielding's REST dissertation, this approach is technically valid but breaks URL length limits (most servers enforce 2048–8192 bytes on the query string) and defeats caching because the encoded JSON is unreadable to intermediaries.

API Tokens and Credentials in Query Strings

Some APIs pass authentication tokens as query parameters rather than headers — OAuth 1.0a callback URLs, pre-signed S3 URLs, and webhook verification tokens follow this pattern. The token itself is usually Base64url-encoded already (eyJ…), which uses - and _ instead of + and / to avoid encoding conflicts. Those characters are in the unreserved or sub-delimiter set, so they travel through query strings safely.

What does need encoding is the OAuth 1.0a signature, which is a Base64 standard encoding (using + and /) applied to an HMAC. The + and / in a Base64 standard signature must be encoded as %2B and %2F respectively. According to the OAuth 1.0a specification, the percent encoding step uses uppercase hex digits and encodes every character except unreserved ones — so %2b (lowercase) is technically wrong. Most servers accept it anyway, but it's worth knowing: a signature abc+def/xyz= becomes abc%2Bdef%2Fxyz%3D in the query string.

The Double-Encoding Trap in API Clients

Double encoding is the most frequent silent bug in API client code. It happens when a URL builder encodes a value that was already encoded by an inner function.

I traced a bug in a third-party API client library where the following sequence occurred:

  1. User passes filter=status:active to the client
  2. Client calls encodeURIComponent('status:active')status%3Aactive
  3. Client passes the result to a URL builder that encodes all values again
  4. % in %3A becomes %25 → final value: status%253Aactive

The server decoded once and received status%3Aactive — the literal string "status%3Aactive", not "status:active". The filter matched nothing.

The fix: encode at exactly one layer. The cleanest approach is to keep values as raw strings until the final URL assembly step, then encode once. You can inspect the outbound URL in your network tab or with curl -v to verify the final encoded form matches what the API expects.

The URL Query Params Extractor on Toolora decodes parameters on extraction, so you can paste a full URL and immediately see the raw decoded values — useful for verifying that an encoded URL round-trips correctly.

Debugging Encoding Mismatches Fast

When an API call fails silently or returns unexpected results, encoding is a prime suspect. My workflow:

  1. Copy the full outbound request URL from the network tab or curl -v output.
  2. Paste it into the URL Encoder / Decoder to decode each parameter value and check whether the server received what you intended.
  3. Re-encode the corrected value using the same tool to generate the exact string the API expects.
  4. Look for %25 sequences — that's always double encoding. The real value contains a literal % that wasn't meant to be there.
  5. Look for unencoded & or = inside a parameter value — those silently split your key-value pair.

One fast check: search the encoded URL for %2526. That's %25 (encoded %) followed by 26 — meaning the original value had %26 (already-encoded &) and then got encoded again, turning %26 into %2526. That's a tell for triple-nesting of encoded values, which occasionally happens in OAuth redirect parameter chains.

Percent encoding in query parameters is a two-minute topic that hides several hours of debugging potential. The RFC rules are fixed; the inconsistency lives in frameworks, libraries, and the gap between what an API says it accepts and what its parser actually does. Knowing which characters are structural delimiters, how arrays get serialized, and where double encoding sneaks in covers the vast majority of production issues.


Made by Toolora · Updated 2026-07-01