I lost an hour once to a query string. A third-party webhook was sending filter[status]=open&filter[assignee]=me, my handler was reading filter as a flat string, and I could not work out why every request came through unfiltered. The moment I pasted the raw URL into a query string parser and saw it resolve to a nested object, the bug was obvious: the sender used bracket notation and my parser did not. This guide covers how I use the Query String Parser on toolz.dev, what the tricky parts of query strings actually are, and why the same key encoded two different ways can quietly break an integration.
TL;DR: A query string is the part of a URL after the question mark that carries parameters as key-value pairs. A query string parser converts that into structured JSON, decoding percent-encoding and handling repeated keys and bracket arrays, and can build a correctly encoded query string back from JSON. The Query String Parser does both directions in your browser, so you can inspect a messy URL or assemble a clean one without leaving the page.
What is a query string?
A query string is the section of a URL that begins after the first question mark and ends at the fragment, the part after a hash. It carries parameters as key=value pairs joined by ampersands, so ?q=json&page=2 passes two parameters. Servers and client code read those to filter a search, track a campaign, paginate a list, or carry state between pages. The generic rules for what is and is not legal in that component come from RFC 3986, the URI standard, and the specific rules browsers follow for form-style parameters come from the WHATWG URL Standard.
The catch is that RFC 3986 defines the syntax of the query component but not its meaning. It says which characters are allowed and how they must be percent-encoded, but it says nothing about how key=value pairs map to a data structure. That interpretation was inherited from HTML form submission, and different platforms extended it in incompatible ways. This is why the same query string can mean slightly different things to a PHP back end, a Rails controller, and a JavaScript front end, and why a parser has to make its conventions explicit.
Every key and value in a query string is URL-encoded so that reserved characters survive. A space becomes %20 or a plus sign, an ampersand inside a value becomes %26 so it is not mistaken for a separator, and a slash becomes %2F. Parsing means splitting the pairs and then decoding each side, and building means encoding each side and joining the pairs. Get the encoding wrong in either direction and values silently corrupt.
What does a query string parser actually do?
A parser takes a URL or a bare query string and turns it into structured, readable data. On the way it strips everything up to and including the question mark, ignores the fragment after the hash, splits the pairs, decodes each key and value, and decides how to represent repeated keys and bracketed keys. The output is a JSON object you can read and copy, plus a flat table of every decoded pair so duplicates and empty values are obvious.
On toolz.dev the tool runs in two directions. In parse mode you paste a full URL or just its query string and get the JSON and the parameter table. In build mode you paste a JSON object and get a correctly encoded query string, with a choice of how arrays are represented. Load the sample and you can watch a real URL with a fragment, a repeated-style array, and an encoded space resolve into clean JSON, then rebuild it.
The reason to use a dedicated parser rather than eyeballing the URL is that query strings hide their structure. A long URL with encoded characters, repeated keys, and bracket notation is nearly impossible to read correctly by scanning it, and the parts that are hardest to read, the encoding and the duplicates, are exactly the parts that cause bugs. Seeing the decoded table next to the JSON removes the guesswork.
How are repeated keys handled?
The same key can legally appear more than once in a query string, as in tags=react&tags=laravel, and there is no single correct way to interpret that, which is the root of a lot of confusion. Different systems resolve duplicates differently, so a parser has to let you choose.
The most common convention, and the default in the toolz.dev tool, is to collect repeated keys into an array, so tags=react&tags=laravel becomes {"tags":["react","laravel"]}. This matches how the browser's own URLSearchParams.getAll exposes the values and how most modern back ends behave. But some frameworks keep only the first occurrence and some keep only the last, so the tool offers keep-first and keep-last options as well. When you are debugging an integration, matching the parser's behavior to the system that produced or consumes the URL is what makes the JSON meaningful.
This ambiguity is not academic. A classic security and correctness issue called HTTP parameter pollution exists precisely because two systems in a request path can resolve id=1&id=2 differently, one seeing 1 and the other seeing 2. Being able to see, explicitly, how a given parser resolves duplicates is the fastest way to reason about that class of bug.
What do brackets like tags[] or filter[color] mean?
Bracket notation is a convention for encoding arrays and nested objects inside a flat query string, and it is where parsers disagree most. A trailing empty bracket marks an array, so tags[]=react&tags[]=laravel builds {"tags":["react","laravel"]}. A named bracket marks a nested object, so filter[color]=red&filter[size]=l builds {"filter":{"color":"red","size":"l"}}. Brackets can nest, so a[b][c]=1 builds {"a":{"b":{"c":"1"}}}.
This syntax comes from how PHP and Ruby on Rails serialize form data, and many JavaScript libraries such as qs follow it. It is not part of any core URL standard, which is exactly why a plain URLSearchParams in the browser will not expand it, giving you a literal key of tags[] instead of an array. The toolz.dev parser understands the convention and expands it into the matching structure, and it lets you turn that behavior off when you want the literal keys instead.
Here is how the main conventions line up, both when parsing and when building:
| Array style | Encoded as | Parses to | Common in |
|---|---|---|---|
| Repeated key | tags=a&tags=b |
["a","b"] |
Browsers, most back ends |
| Empty bracket | tags[]=a&tags[]=b |
["a","b"] |
PHP, Rails, qs library |
| Indexed bracket | tags[0]=a&tags[1]=b |
["a","b"] |
qs library, ordered data |
| Comma-joined | tags=a,b |
one string to split | Some APIs, compact URLs |
When you build a query string with the tool, you pick which of these to emit, so the output matches whatever the receiving system expects. When you parse, the tool detects repeated keys and bracket notation for you, and comma-joined values stay as a single string because only you know whether a comma is a separator or part of the data.
Why do plus signs turn into spaces?
In the query string of a URL, a literal space is very often encoded as a plus sign rather than %20. This is a rule inherited from the application/x-www-form-urlencoded format that HTML forms use, and the WHATWG URL Standard codifies it: when parsing form-encoded data, a + is decoded to a space. So q=json+parser should parse to json parser, and the toolz.dev tool does this by default.
The subtlety is that this rule applies to the query component, not to the path. A + in a path segment is a literal plus. And occasionally your data genuinely contains plus signs that should be preserved, such as a phone number or a search for C++. For those cases the tool has a switch to turn off plus-as-space, so the plus survives the round trip. This is the kind of detail that a general-purpose URL Encoder will not decide for you, because it does not know whether it is looking at a query or a path.
Getting this right matters when building, too. When the tool encodes a value, it percent-encodes reserved characters and, by default, uses a plus for spaces in the form-encoded style, so the string it produces is one that browsers and back ends will decode the way you intended.
How does this differ from a full URL parser?
A query string parser and a full URL parser overlap but answer different questions, and using the right one saves a step. A URL parser breaks a complete link into its components, the scheme, host, port, path, query, and fragment, and is what you want when you are debugging where a request is going or why a redirect or a CORS check behaves oddly. A query string parser focuses on the query alone and turns it into structured, editable JSON, including arrays and nested keys, and can build the query back.
The URL Parser on toolz.dev is the tool for anatomy: give it a link and it shows you the host versus origin distinction, the default port, and the pieces of the path. The query string parser is the tool for working with parameters: give it the same link and it gives you the parameters as JSON you can edit, and then rebuilds a query string from your edits. In practice I use them together, the URL parser to understand a link and the query string parser to change its parameters, and if I am assembling a campaign link I reach for the UTM Builder instead, which is a query string builder specialized for analytics tags.
When do I actually reach for this?
The honest answer is whenever a URL is doing more than pointing at a page. I use it to debug webhooks and OAuth redirects, where the parameters carry the entire payload and one mis-encoded value breaks the flow. I use it to read the tracking parameters on a marketing link so I can see exactly what a campaign is passing. I use it to convert a query string a colleague pasted in a chat into JSON I can drop into a test fixture, and to do the reverse, turning a small object into a query string for a quick manual request.
A worked example makes the payoff concrete. An OAuth provider redirects back to your app with something like ?code=abc123&state=xyz789&scope=read%20write&error=. Pasted into the parser, that resolves to a clean object: code and state as their literal values, scope decoded to read write because %20 is a space, and error as an empty string rather than a missing key, which tells you the provider sent the parameter but left it blank. Reading that from the raw URL by eye, you would likely miss the encoded space in scope and misread the empty error, and both of those are exactly the details that decide whether your callback handler branches correctly. Seeing the decoded table removes the ambiguity, and if you then need to reproduce the request, build mode turns your edited object back into a valid callback URL in one step.
Because so much of that work involves URLs that carry tokens, signed values, and tracking identifiers, doing it in a tool that runs entirely in your browser matters. Nothing you paste is transmitted, logged, or stored, and the tool keeps working with the network off, so you can safely inspect a signed callback URL from production rather than a sanitized copy. I make the broader case for keeping this kind of work client-side in the data privacy in online tools guide, and this parser sits alongside the other link and text utilities I described in the web developer toolkit. If part of your job is turning messy input into clean slugs and identifiers, the Slug Generator is a natural companion for the output side of URL work.
Frequently asked questions
What is a query string?
A query string is the part of a URL after the question mark that carries parameters as key-value pairs joined by ampersands, such as ?q=json&page=2. Servers and client code read it to filter results, track campaigns, or pass state. Each key and value is URL-encoded so that spaces and reserved characters survive, and the fragment after a hash is not part of it.
How are repeated keys handled when parsing?
By default a key that appears more than once is combined into an array, so tags=react&tags=laravel parses to {"tags":["react","laravel"]}. You can switch to keep only the first value or only the last value instead, because different back ends resolve duplicates differently and you want the JSON to match the system you are targeting.
What do brackets like tags[] or filter[color] mean?
Bracket notation encodes arrays and nested objects inside a flat query string. tags[]=react&tags[]=laravel builds an array, and filter[color]=red&filter[size]=l builds the nested object {"filter":{"color":"red","size":"l"}}. It is common in PHP, Rails, and form libraries, so the parser expands it into the matching structure, and you can turn that off to keep the literal keys.
Why does a plus sign become a space?
In the query string of a URL, a literal space is often encoded as a plus sign, a rule inherited from HTML form submission, so the parser converts + back into a space by default. If your data contains real plus signs that must be kept, such as C++ or a phone number, turn off the plus-as-space option and the plus is preserved.
Can I build a query string from JSON?
Yes. Switch to build mode and paste a JSON object of key-value pairs. The tool percent-encodes each key and value and joins them with ampersands, and you can choose how arrays are encoded: repeated keys, empty brackets, indexed brackets, or a comma-separated list, so the output matches whatever the receiving system expects.
What is the difference between this and a full URL parser?
A full URL parser breaks a whole URL into scheme, host, port, path, query, and fragment. A query string parser focuses on the query alone, turns it into structured editable JSON including arrays and nested keys, and can build the query back. Use the URL parser to inspect a link, and this tool to read or change its parameters.
Does it handle a full URL or only the query part?
Both. If you paste a full URL, the parser drops everything up to and including the question mark and ignores the fragment after the hash, so you get just the parameters. If you paste a bare query string with no question mark, it is parsed as-is, which is handy when you only copied the parameters.
Is my URL sent anywhere?
No. Parsing, decoding, and encoding all run as JavaScript in your browser, so nothing is transmitted, logged, or stored. You can confirm it by watching the network tab while you parse a URL, or by disconnecting from the internet, because the tool keeps working offline once the page has loaded.
Inspect or assemble parameters with the free Query String Parser. It parses a URL into JSON and builds a query string back, handling repeated keys, bracket arrays, and encoding, entirely in your browser.



