Parse URL query string into a key-value object
This URL query string parser turns either a complete absolute URL or a raw query component into a structured key-value object.
Run — free
It decodes percent escapes and form-style plus signs, preserves empty values, and collects repeated parameter names into arrays in their original order. Malformed escapes and invalid UTF-8 are rejected clearly instead of being replaced or passed through. Use it interactively when inspecting a link, or call the deterministic API for $0.002 per item when an application, test suite, or ingestion workflow needs the same result every time.
Choose the input form that matches your source
You can submit a complete absolute URL such as <code>https://example.com/search?q=red+shoes</code>, a raw query such as <code>q=red+shoes&page=2</code>, or the same raw query with a leading question mark. For an absolute URL, only the query component is parsed; the scheme, authority, path, and fragment do not become fields. A URL without a query returns an empty parameter object. Raw input is treated entirely as query data after an optional initial question mark, so it is useful when a framework, access log, webhook, or browser API has already separated the query from the rest of the address. Each ampersand starts another pair, while the first equals sign divides its name from its value. A name without an equals sign is retained with an empty value, as is a name followed by an explicit empty assignment. Empty segments between consecutive ampersands are ignored. The result also reports distinct-key and parsed-pair counts, making it easy to distinguish repeated fields from unique names without recounting the object yourself.
Understand decoding and repeated keys
Names and values are decoded independently using the conventions normally applied to URL query forms. A plus sign becomes a space, while <code>%HH</code> sequences are interpreted as UTF-8 bytes. That means <code>city=San+Jos%C3%A9</code> produces a readable Unicode value, and an encoded separator such as <code>%26</code> remains inside one value rather than being mistaken for the start of another field. Decoding happens exactly once, so <code>%2520</code> becomes <code>%20</code>, not a space. When a key occurs once, its value is a string. When the same decoded key occurs again, the value becomes an array containing every occurrence in source order. This rule represents checkbox groups, filters, tags, and other multi-select controls without discarding information or inventing numbered property names. Repetition is determined after decoding, so equivalent encoded spellings of the same name are grouped together. Blank repeated values are preserved in those arrays. The parser does not infer booleans, numbers, dates, or nested bracket notation; all scalar values remain strings so downstream code can apply its own domain-specific conversion deliberately.
Reject corruption before it enters a workflow
Loose query parsers can silently keep a stray percent sign, accept a one-digit escape, or substitute a replacement character when escaped bytes do not form valid UTF-8. Those behaviors make damaged identifiers appear usable and can create difficult differences between a browser, a server, and a signature-verification routine. This capability checks every percent sign for exactly two hexadecimal digits and then verifies that each decoded byte sequence is valid UTF-8. If either condition fails, the request returns an invalid-input error rather than a partial object. The parser is pure and deterministic: it performs no network request, follows no redirect, reads no clock, uses no randomness, and retains no state between calls. Treat the returned strings as data rather than trusted HTML, code, file paths, or database expressions; decoding restores characters but does not make their later use safe. In automation, the API costs $0.002 per item and can serve as a stable normalization step before validation, routing, comparison, or storage. In the browser, the same parsing logic is suitable for quickly examining copied links without contacting their destination.
What you can do with it
Inspect a copied URL
Turn a long search, campaign, or callback URL into readable fields without opening or contacting its destination.
Normalize webhook input
Parse a raw query component before applying application-specific validation while retaining every repeated parameter value.
Build deterministic test fixtures
Assert exact decoding, blank-value, and repeated-key behavior across integration tests and data ingestion pipelines.
FAQ
What does an API request cost?
Each parsed item costs $0.002 through the API. The interactive browser tool uses the same deterministic parsing logic.
What happens when a key appears more than once?
A key with one occurrence has a string value. On repetition, its value becomes an array containing all values in their original order.
Are plus signs converted to spaces?
Yes. Query strings commonly use form encoding, where a plus sign represents a space. Use %2B when the value must contain a literal plus sign.
Does the parser infer numbers or booleans?
No. Every scalar remains a string, including empty strings, numeric-looking text, true, and false. Convert values only after applying your own schema.
Which percent-encoding errors are rejected?
The parser rejects a percent sign without two hexadecimal digits and escaped byte sequences that do not decode as valid UTF-8.
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/web/query-string-parse \
-H "Authorization: Bearer $KIT_KEY" \
-H "Content-Type: application/json" \
-d '{"text":"https://example.com/search?q=red+shoes&tag=sale&tag=new&empty="}'const res = await fetch("https://api.kit.forhosting.com/web/query-string-parse", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.KIT_KEY}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
"text": "https://example.com/search?q=red+shoes&tag=sale&tag=new&empty="
})
});
const { task_id } = await res.json();import os, requests
res = requests.post(
"https://api.kit.forhosting.com/web/query-string-parse",
headers={"Authorization": f"Bearer {os.environ['KIT_KEY']}"},
json={
"text": "https://example.com/search?q=red+shoes&tag=sale&tag=new&empty="
},
)
task_id = res.json()["task_id"]<?php
$res = file_get_contents("https://api.kit.forhosting.com/web/query-string-parse", false, stream_context_create([
"http" => [
"method" => "POST",
"header" => "Authorization: Bearer " . getenv("KIT_KEY") . "\r\nContent-Type: application/json",
"content" => '{"text":"https://example.com/search?q=red+shoes&tag=sale&tag=new&empty="}',
],
]));
$task = json_decode($res, true);body := bytes.NewBufferString(`{"text":"https://example.com/search?q=red+shoes&tag=sale&tag=new&empty="}`)
req, _ := http.NewRequest("POST", "https://api.kit.forhosting.com/web/query-string-parse", body)
req.Header.Set("Authorization", "Bearer "+os.Getenv("KIT_KEY"))
req.Header.Set("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)Example request
{
"text": "https://example.com/search?q=red+shoes&tag=sale&tag=new&empty="
}Example response
{
"task_id": "tsk_a1b2c3d4e5f6a1b2c3d4e5f6",
"type": "web.query_string_parse",
"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.
Limits
timeout_sec | 30 |
max_crawl_pages | 25 |
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. |