The search has to fit in the URL
URLs have practical length limits, end up in server logs and get saved in bookmarks and history. A complex filter does not fit cleanly in a query string, and you may not want it written down everywhere.
Backend Engineering Guides · HTTP QUERY
The HTTP QUERY method, explained so anyone can follow it.
RFC 10008 adds QUERY: an HTTP method that reads without changing anything, like GET, yet carries its question in a request body, like POST.
01Why HTTP needed it
A search is a read. It should be safe to repeat and safe to cache. Until now, the only way to send a large search was a method that promises neither.
A short question you can call across the counter. A long one you write down, but the only form on the desk is the returns form, and it makes the librarian think you are changing their records. QUERY is a proper enquiry slip: written down, and clearly just a question.
The same contact search, sent three ways. Switch the method and watch what the request can promise.
Both work for searches today. Each one pays for it somewhere.
URLs have practical length limits, end up in server logs and get saved in bookmarks and history. A complex filter does not fit cleanly in a query string, and you may not want it written down everywhere.
A body holds any search you like, but POST is neither safe nor idempotent by definition. Clients and proxies cannot assume a retry is harmless or that the response is fine to cache.
02Method comparison
QUERY takes the guarantees of GET and the body of POST. The highlighted column is the new one.
A postcard carries a few words on the outside for every postman to read. A parcel carries anything, but it is marked "may change things", so nobody dares send it twice. QUERY is a sealed envelope stamped "question only": it holds a lot, and anyone can safely resend it.
| Property | GET | QUERY | POST |
|---|---|---|---|
| Safe, no side effects | Yes | Yes | No |
| Idempotent, safe to retry | Yes | Yes | No |
| Has a request body | No | Yes | Yes |
| A URI for the query itself | By definition | Optional, via Location | No |
| Cacheable | Yes | Yes | Only for a later GET or HEAD |
Per RFC 10008 and RFC 9110.
03A QUERY on the wire
It reads like a POST. The method name is what changes the rules for everyone in between.
Ask the price of a kilo of rice twice and you get the same answer both times, and nothing in the shop changes. That is what idempotent means, and it is why a client may simply ask again after a dropped connection.
QUERY /contacts HTTP/1.1Host: example.orgContent-Type: application/x-www-form-urlencodedAccept: application/json select=surname,givenname,email&limit=10&match="email=*@example.*"
[ { "surname": "Smith", "givenname": "John", "email": "smith@example.org" }, { "surname": "Jones", "givenname": "Sally", "email": "sally.jones@example.com" }]
04Status codes
The responses you are most likely to meet when a server supports QUERY.
"Here it is" is a 200. "Collect it from counter 3" is a 303. "We do not take that form" is a 415, and "we understand, but we cannot do that" is a 422.
| Code | Meaning | Group |
|---|---|---|
| 200 | The query ran, and the results are in the body | Success |
| 303 | Fetch the results with a GET at the Location URI | Redirect |
| 304 | Not modified, a conditional request matched | Redirect |
| 400 | Content-Type is missing or does not match the body | Client error |
| 415 | The query format is not supported here | Client error |
| 422 | Well formed, but the query cannot run | Client error |
Green works, amber redirects, red fails.
Without one, the server cannot tell how to read the query and answers 400. Match it to the body you actually send.
05Questions and checklist
What comes up when a team starts using QUERY, then a checklist before you ship it.
Only where every hop supports it. Check your server framework, proxies and CDN first, and keep a POST fallback for clients that cannot send it.
Same body, different promise. QUERY says the request is safe and idempotent, so clients can retry it and caches can keep the answer. POST promises neither.
For short lookups that fit in a URL, and for anything people may want to bookmark or share.
Yes, and it can be revalidated with ETag and If-Modified-Since like a GET. The cache has to key on the request body as well as the URL, and many caches do not do that yet.
The server lists them, such as SQL, JSONPath or XSLT, in the Accept-Query header. A client can read it from an OPTIONS or HEAD response before it asks.
It answers 415 for a format it does not support, 422 for a well-formed query it cannot run, and 400 when the Content-Type is missing or wrong.
Yes, but it is not a safelisted method, so a browser sends a preflight OPTIONS first. The server must list QUERY in Access-Control-Allow-Methods.
Yes. The server can return a Location that repeats the same query with a plain GET, or a Content-Location that points at the result itself.