Query string parser
name=Ada&city=New%20York&q=hello+world splits into three pairs, name Ada, city New York and q hello world. Parse a query string into ordered names and values, or build a correctly encoded one from your pairs. Free in your browser.
- Free
- Runs in your browser
- No sign-up
- Your data stays on your device
Run — free
Runs in your browser. Free, unlimited — your data never leaves this page.
URL query strings carry filters, campaign tags, pagination, and form fields after the question mark of an address. Reading them by hand is error-prone: spaces may appear as plus signs or percent-twenty, ampersands separate fields, equals separates names from values, and a broken percent escape can poison a whole log line. This query string parser converts either direction with a single contract. Parse mode accepts a raw query (with or without a leading question mark), decodes each key and value, and returns an ordered list of pairs plus a simple object map for unique keys. Serialize mode accepts the same pairs or a plain object map and emits a correctly percent-encoded query string ready to append after a path.
How query strings encode key-value pairs
A typical query fragment looks like name equals Ada ampersand city equals New percent-twenty York. The ampersand is the field separator; the first equals in each field separates the parameter name from its value. Values (and sometimes names) are percent-encoded so spaces, ampersands, non-ASCII letters, and reserved characters survive transit through browsers, proxies, and analytics tools.
Many servers also treat a bare plus as a space under the application/x-www-form-urlencoded rules used by HTML forms; this capability does that by default when parsing, and you can disable it if you need a literal plus. Empty fields from trailing or doubled ampersands are skipped so you do not invent phantom empty keys. A parameter without an equals sign is treated as a key with an empty string value, which matches common server libraries.
When the same key appears more than once, every occurrence is kept in the ordered pairs list so multi-select filters and repeated tags are not lost; the companion object map uses last-wins so simple clients still get a flat dictionary. Encode uses encodeURIComponent on each key and value so reserved separators never leak into the assembled string.
Where a query string parser earns its keep
Backend services that log inbound requests need a reliable way to turn the search portion of a URL into structured fields for metrics and support tools without writing a one-off splitter that mishandles plus signs or Unicode. Frontend apps that rebuild filter chips from the current location search need ordered pairs so multi-value facets survive a round-trip through encode and decode. Support teams paste half-encoded links from tickets and need a decoder that fails loudly on truncated escapes instead of inventing characters.
Curriculum authors teaching web foundations want a dedicated landing for reading and writing query parameters rather than burying the topic under a generic percent-encoder or UTM builder, because search intent differs: people looking for a query string parser want structure, not only character escaping. Failed validation does not consume balance. Keep requests under the documented length limits, pin the ordered pairs in tests, and you get reproducible parameter maps for dashboards, LMS plugins, and edge workers without standing up a full URL toolkit of your own.
What you can do with it
Inspect inbound URL parameters
Parse request query strings into ordered key-value pairs for logging, support tools, and analytics without mishandling plus signs or percent escapes.
Build safe outbound query strings
Serialize filters, tags, and form fields into a correctly percent-encoded query string so reserved characters never invent extra parameters.
FAQ
How do I parse a URL query string into key-value pairs?
Set mode to parse (or omit it) and send text with the query fragment, with or without a leading ?.
How do I serialize pairs back into a query string?
Set mode to serialize and send pairs as an array of {key, value} objects or a plain object map. The response includes encoded (and query) with each name and value percent-encoded and joined by &.
Is a plus sign read as a space?
Yes, by default, because web forms send spaces as plus signs, so hello+world becomes hello world. You can switch that off when you need a plus sign to stay a literal plus.
What happens when the same name appears twice?
Every occurrence is kept, in order, so multi-select filters and repeated tags are not lost. The simple name-to-value view keeps only the last value for each name.
What happens with a broken percent sequence?
Parsing stops with a message that says where the problem is. A lone percent sign at the end, or a percent followed by characters that are not hexadecimal, is refused instead of being half decoded.
Related tools
For developers — API access
Everything on this page is available programmatically. This section is for teams who want to wire it into their own systems; everyone else can just use the tool above.
API endpoint
Prefer to automate it? One authenticated POST creates the task; the result comes back by webhook or a signed link. The same capability also runs here on the web, by email and from Telegram — and soon from our app too.
Call it from your stack
curl -X POST https://api.kit.forhosting.com/enc/query-string \
-H "Authorization: Bearer $KIT_KEY" \
-H "Content-Type: application/json" \
-d '{"mode":"parse","op":"parse"}'const res = await fetch("https://api.kit.forhosting.com/enc/query-string", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.KIT_KEY}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
"mode": "parse",
"op": "parse"
})
});
const { task_id } = await res.json();import os, requests
res = requests.post(
"https://api.kit.forhosting.com/enc/query-string",
headers={"Authorization": f"Bearer {os.environ['KIT_KEY']}"},
json={
"mode": "parse",
"op": "parse"
},
)
task_id = res.json()["task_id"]<?php
$res = file_get_contents("https://api.kit.forhosting.com/enc/query-string", false, stream_context_create([
"http" => [
"method" => "POST",
"header" => "Authorization: Bearer " . getenv("KIT_KEY") . "\r\nContent-Type: application/json",
"content" => '{"mode":"parse","op":"parse"}',
],
]));
$task = json_decode($res, true);body := bytes.NewBufferString(`{"mode":"parse","op":"parse"}`)
req, _ := http.NewRequest("POST", "https://api.kit.forhosting.com/enc/query-string", body)
req.Header.Set("Authorization", "Bearer "+os.Getenv("KIT_KEY"))
req.Header.Set("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)Example request
{
"mode": "parse",
"op": "parse"
}Example response
{
"task_id": "tsk_a1b2c3d4e5f6a1b2c3d4e5f6",
"type": "enc.query_string",
"status": "queued",
"_links": {
"result": "/tasks/tsk_…/result"
}
}The API is asynchronous: the call returns a task_id immediately and the result arrives by webhook. Polling is capped at 1 req/s per task.
Pricing
Published price — no tokens, no invented credits. A failed task is never charged.
Errors
| HTTP | Code | Meaning |
|---|---|---|
401 | unauthorized | Missing or invalid API key. |
402 | insufficient_balance | Your balance doesn't cover the task price. |
404 | unknown_type | That task type doesn't exist. |
429 | rate_limited | Too many requests. Use the webhook instead of polling. |
Technical notes
Malformed percent sequences fail with invalid_input instead of half-decoded garbage.
How query strings encode key-value pairs
Decode validates every percent sequence before host decode runs: a trailing lone percent or non-hex digits after a percent raise invalid_input with a position-aware message. The path is pure string work with no network, no random seeds, and no wall-clock timestamps, so identical inputs always produce identical JSON. Successful API pricing is the published base of $0.002 per item, and validation failures do not consume prepaid balance.
Input contract, response shape, and error behaviour
An optional leading question mark is stripped; a hash fragment after the query is ignored so pasting a full search from a browser still works. For serialize, provide pairs as either an array of objects with key and value fields or a plain object map. Classic checks include parsing name equals Ada ampersand city equals New percent-twenty York, serializing pairs that contain spaces and ampersands, and rejecting a value that ends with a bare percent. Send a JSON body with mode set to parse or serialize. For parse, provide text (aliases query, query_string, qs) containing the query fragment. Optional plus_as_space defaults to true for form-urlencoded plus-to-space conversion. On success, parse returns mode, plus_as_space, the original text, a normalized query view, pairs as an array of key and value strings in order, object as a last-wins map of unique keys, count as the number of pairs, and encoded as a re-serialized string from those pairs so you can compare round-trips. On success, serialize returns mode, plus_as_space, the normalized pairs, object, count, encoded, and query (the same encoded string without a leading question mark). Missing text in parse mode, missing pairs in serialize mode, unsupported mode values, nested objects inside a map, oversized strings, and malformed percent sequences all fail with invalid_input and a clear English message. Validation failures do not consume prepaid balance. Successful API pricing is the published base of $0.002 per item.
Integration use cases
Round-trip tests and fixtures
Generate golden parse and serialize vectors for CI so multi-value keys and Unicode values stay stable across runtimes.
Technical questions
Are plus signs treated as spaces?
Yes by default when parsing (application/x-www-form-urlencoded). Set plus_as_space to false if you need bare + kept literal before percent-decoding.
What happens if a percent sequence is malformed?
Parse fails with invalid_input, naming the parameter and the bad escape (for example an incomplete % or non-hex digits). Validation failures are not billed.
What does the query string parser cost?
The browser tool is free. API requests use prepaid KIT balance at $0.002 per successful call; invalid input is not charged.