HTTP has always had a bit of a problem. On one hand, a GET request is safe, idempotent, and cacheable, which is exactly the properties you want for a read-only lookup. The issue is that is has no defined semantics for a request body, so any parameters have to be squeezed into the URI as a query string. This could become a problem as parameters got added and there was a risk that a URI could breach their limitations. A POST request can carry a body of any size and shape, but it isn't safe or idempotent, so nothing in the chain i.e browsers, proxies, CDNs, caches is allowed to retry or cache it automatically.
That's fine for a simple filter, but it breaks down once a query gets complex: think a search endpoint with dozens of filters, a GraphQL-style query document, or a geospatial lookup with nested parameters. Cramming that into a URL runs into real problems:
QUERY (RFC 10008, published June 2026 by Julian Reschke, James M. Snell, and Mike Bishop under the IETF httpbis working group) closes that gap: a method that behaves like GET for caching, retries, and side effects, but carries its parameters in the body like POST.
Error handling gets two specific status codes worth knowing: 415 Unsupported Media Type when the server doesn't understand the query format, and 422 Unprocessable Content when the query is syntactically valid but fails on its merits e.g. a SQL query referencing a table that doesn't exist.
RFC 10008 introduces a response header, Accept-Query, that lets a server advertise which query formats an endpoint understands, using HTTP Structured Fields syntax:
Accept-Query: "application/jsonpath", application/sql;charset="UTF-8"The header applies to every URI sharing the same path and the query component of the URI is ignored, so a client can discover what a resource accepts before constructing a request.
The clearest way to see what QUERY adds is to line it up against the methods you already test against. It sits exactly between GET and POST: GET's guarantees, POST's payload.
| Method | Safe | Idempotent | Request body | Cacheable | Typical use |
|---|---|---|---|---|---|
| GET | Yes | Yes | No defined semantics | Yes | Retrieve a resource |
| HEAD | Yes | Yes | No | Yes | Retrieve headers only |
| OPTIONS | Yes | Yes | No | No | Discover allowed methods / CORS preflight |
| QUERY | Yes | Yes | Expected | Yes (cache key includes body) | Large or complex read-only queries |
| POST | No | Not guaranteed | Yes | Only if explicitly marked | Create a resource / non-idempotent actions |
| PUT | No | Yes | Yes | No | Replace a resource entirely |
| PATCH | No | Not guaranteed | Yes | No | Partially update a resource |
| DELETE | No | Yes | Optional | No | Remove a resource |
A few other details round out how QUERY behaves at the protocol level: 301 and 308 redirects preserve the method (like GET, unlike POST), a 303 signals that the same result is available via a plain GET, and conditional headers like If-Modified-Since work the same way they would against an equivalent GET. Servers can also expose a Content-Location (for the result) or Location (for the query itself) so a client can fetch the same thing again with a cheap GET instead of resending the whole query body.
A few things are worth building into your test approach before QUERY shows up in an API you're testing: