For years, API developers have often had to make a choice that never felt entirely right:
- use
GETand place every filter in the URL; - or use
POSTto send a complex query in the request body.
The first option follows the correct semantics of a read operation, but becomes increasingly awkward as the query grows.
The second solves the size and structure problem, but communicates a different intention to clients, proxies, caches and the wider HTTP infrastructure.
In June 2026, the IETF published RFC 10008 — The HTTP QUERY Method, introducing an official solution for the space between GET and POST.
Yes, HTTP now has a method called:
QUERY
And no, it is not simply POST /search with a more elegant name.
The Problem QUERY Is Trying to Solve
Consider a simple search API:
GET /products?category=laptops&brand=example&minPrice=500&maxPrice=1500
So far, so good.
Now imagine that the search needs to support:
- multiple groups of filters;
ANDandORconditions;- date ranges;
- sorting by several fields;
- pagination;
- aggregations;
- geographical filters;
- dynamically selected fields;
- nested rules.
The URL can quickly begin to look like an attempt to write a programming language using only &, %20 and a great deal of optimism.
GET /products?filter=%7B%22and%22%3A%5B%7B%22category%22...
As well as being difficult to read and maintain, very large URLs encounter practical limits in browsers, proxies, servers, gateways, firewalls and other intermediary systems.
The RFC also points out that URLs are more likely to appear in:
- access logs;
- browser histories;
- bookmarks;
- analytics tools;
- intermediary systems.
A common solution is to replace GET with POST:
POST /products/search
Content-Type: application/json
{
"category": "laptops",
"price": {
"minimum": 500,
"maximum": 1500
},
"brands": ["example", "another-brand"],
"sort": [
{
"field": "price",
"direction": "asc"
}
]
}
Technically, it works.
The problem is semantic.
The POST method does not communicate, by itself, that the operation is safe and can be repeated without modifying system state.
To a client or intermediary component that does not already understand the API, POST could mean almost anything:
- create an order;
- charge a card;
- send a message;
- start a process;
- modify information;
- or simply search for products.
This is where QUERY comes in.
How the QUERY Method Works
The same request could be represented like this:
QUERY /products HTTP/1.1
Host: api.example.com
Content-Type: application/json
Accept: application/json
{
"category": "laptops",
"price": {
"minimum": 500,
"maximum": 1500
},
"brands": ["example", "another-brand"],
"sort": [
{
"field": "price",
"direction": "asc"
}
]
}
The query remains in the request body, just as it would with POST.
However, the method now explicitly communicates that the operation:
- is a query;
- is safe;
- is idempotent;
- can be retried automatically;
- may have its response cached.
You can think of QUERY as having the body-carrying capabilities of POST, while retaining semantic properties similar to GET.
QUERY Does Not Mean “GET with a Body”
A natural reaction might be:
Why not simply send a body with a GET request?
Because HTTP does not define general semantics for content sent in a GET request.
Some implementations allow it, but clients, servers, proxies, caches and libraries may treat that body inconsistently or ignore it entirely.
QUERY removes that ambiguity.
Its body is not an accidental implementation detail. The content and its Content-Type are part of the query definition.
QUERY Is Safe
In HTTP terms, a safe method is one where the client does not request or expect a change to the state of the resource being queried.
That means a request such as:
QUERY /orders
should not cancel, update or create orders as part of the operation requested by the client.
This does not prevent the server from performing incidental internal actions such as:
- writing logs;
- collecting metrics;
- populating caches;
- updating operational statistics;
- creating a temporary resource representing the result.
The important point is that the purpose of the request is to retrieve information, not modify the resource.
In that respect, QUERY belongs to the same semantic category as GET, HEAD and OPTIONS.
QUERY Is Idempotent
An idempotent operation can be repeated without producing additional intended effects beyond those caused by the first execution.
This matters particularly when a network failure occurs.
Imagine that a client sends a request but loses the connection before receiving the response. With an idempotent operation, the infrastructure can retry it more safely.
QUERY /telemetry
Content-Type: application/json
{
"spacecraftId": "satellite-001",
"metrics": [
"battery.voltage",
"payload.temperature"
]
}
Repeating this query should not alter the state of the satellite or initiate a new operational command.
It simply requests the result again.
This property allows HTTP clients and intermediary systems to implement automatic retries with less risk than they would have with a POST.
Content-Type Is Required
In RFC 10008, the request body is an essential part of the query.
For that reason, the server should reject a QUERY request when the Content-Type header is missing or does not match the content being sent.
A valid example:
QUERY /customers
Content-Type: application/json
{
"country": "GB",
"status": "active"
}
The format does not have to be JSON.
A server could accept other query formats:
Content-Type: application/sql
Content-Type: application/jsonpath
Content-Type: application/vnd.example.query+json
The RFC does not define a universal query language. It defines the HTTP method and allows each resource to determine the formats and query rules it supports.
The Accept-Query Header
The specification also introduces the Accept-Query response header.
It allows a server to advertise which query formats are accepted by a particular resource:
Accept-Query: application/json, "application/jsonpath"
A client can therefore discover that an endpoint supports QUERY and which formats it may send.
A response might look like this:
HTTP/1.1 200 OK
Allow: GET, HEAD, QUERY
Accept-Query: application/json
It is worth noting that Accept-Query uses the syntax defined by HTTP Structured Fields. Although it may look like a simple comma-separated header, it should be parsed according to the Structured Fields rules.
Error Handling
The RFC suggests suitable HTTP status codes for different classes of error.
400 Bad Request
This may be used when:
Content-Typeis missing;- the body is malformed;
- the content does not match the declared media type.
HTTP/1.1 400 Bad Request
Content-Type: application/problem+json
{
"title": "Invalid query document",
"detail": "The request body is not valid JSON."
}
415 Unsupported Media Type
This may be used when the resource does not support the submitted query format:
HTTP/1.1 415 Unsupported Media Type
Accept-Query: application/json
422 Unprocessable Content
This is appropriate when the format and syntax are valid, but the query cannot be processed.
HTTP/1.1 422 Unprocessable Content
Content-Type: application/problem+json
{
"title": "Invalid query",
"detail": "The field 'customerRank' does not exist."
}
406 Not Acceptable
This may be returned when the server cannot produce a response in a format requested by the client through the Accept header.
QUERY Responses Can Be Cached
Responses to QUERY requests may be cached.
However, there is an important difference compared with GET.
With a GET request, the URI is one of the main elements used to construct the cache key.
With QUERY, the cache must also take into account:
- the request body;
- the
Content-Type; - relevant metadata;
- content negotiation information.
Consider these two requests:
QUERY /products
Content-Type: application/json
{
"category": "laptops"
}
QUERY /products
Content-Type: application/json
{
"category": "monitors"
}
Although they use the same URI, they represent different queries and must not incorrectly share the same cached response.
The cache key therefore needs to incorporate the submitted content.
This also makes caching QUERY requests more complex. An intermediary may need to read the complete request body before it can determine the correct cache key.
Cache Key Normalisation
The RFC allows caches to remove semantically irrelevant differences before generating the cache key.
For example, these two JSON documents may represent the same query:
{"status":"active","country":"GB"}
{
"country": "GB",
"status": "active"
}
A cache that properly understands the format could normalise them to improve efficiency.
However, this must be handled very carefully.
If the cache applies a different normalisation model from the server, two distinct queries could be treated as equivalent, causing the wrong response to be returned.
In multi-tenant systems or environments dealing with sensitive information, this type of mistake could become a serious security vulnerability.
Location and Content-Location
One of the more interesting parts of the RFC is the ability for the server to assign URIs to the query or its result.
Content-Location for the Result
The server may provide a URI representing the specific result of the query:
HTTP/1.1 200 OK
Content-Type: application/json
Content-Location: /query-results/abc123
The client could later retrieve that result using:
GET /query-results/abc123
This may be useful for:
- expensive reports;
- large datasets;
- temporary results;
- shareable responses;
- analytical processing.
Location for an Equivalent Query
The server may also provide a URI representing the query itself:
HTTP/1.1 200 OK
Content-Type: application/json
Location: /queries/active-uk-customers
A later request might then be:
GET /queries/active-uk-customers
In this case, the server is indicating that the URI can repeat or represent the query without requiring the client to resend the original body.
The distinction is subtle but important:
Content-Locationmay identify the result that was produced;Locationmay identify a resource equivalent to the executed query.
Redirects
Redirect behaviour is also defined.
For 301, 302, 307 and 308 responses, the client may repeat the QUERY request at the new location.
The historical behaviour that sometimes converts a POST into a GET following a 301 or 302 must not be applied to QUERY.
A 303 See Other response, on the other hand, indicates that the result may be retrieved with GET:
HTTP/1.1 303 See Other
Location: /reports/abc123
The client would then follow with:
GET /reports/abc123
This provides an interesting solution for queries that generate persistent or precomputed results.
Conditional Requests
The method can also use conditional request headers such as:
If-None-Match: "query-result-v42"
If the result has not changed, the server may respond with:
HTTP/1.1 304 Not Modified
This can reduce the cost of expensive analytical queries or results that change infrequently.
What About Security?
Moving query parameters from the URL into the request body may reduce accidental exposure.
URLs commonly appear in:
- access logs;
- browser histories;
- analytics systems;
- monitoring tools;
- tracing platforms;
- bookmarks;
- referrer headers.
However, this does not make the content secret.
The request body may still be recorded by:
- API gateways;
- proxies;
- web application firewalls;
- observability platforms;
- debugging tools;
- backend applications.
HTTPS, authentication, authorisation, data redaction and sensible logging policies remain essential.
Care must also be taken when the server creates a URI representing a query or its result. Sensitive information from the original request body should not simply be copied into that URI.
QUERY and CORS
In browsers, QUERY is not included in the list of CORS-safelisted methods.
A cross-origin request will therefore require a preflight request:
OPTIONS /products HTTP/1.1
Origin: https://app.example.com
Access-Control-Request-Method: QUERY
Access-Control-Request-Headers: content-type
The server must explicitly allow the method:
Access-Control-Allow-Origin: https://app.example.com
Access-Control-Allow-Methods: GET, QUERY
Access-Control-Allow-Headers: Content-Type
For frontend applications, this introduces an additional implementation detail that must be considered.
QUERY Versus GET Versus POST
A simplified comparison looks like this:
| Property | GET | QUERY | POST |
|---|---|---|---|
| Safe | Yes | Yes | Not necessarily |
| Idempotent | Yes | Yes | Not necessarily |
| Query in request body | No general semantics | Yes | Yes |
| Response can be cached | Yes | Yes | With limitations |
| Safe automatic retry | Yes | Yes | Depends on the operation |
| Query identified by URI | Yes | Optional | Usually not |
QUERY does not replace GET or POST.
For small, simple queries that are naturally represented in the URL, GET remains an excellent choice:
GET /users/42
For commands and operations that modify state, POST, PUT, PATCH and DELETE continue to serve their respective purposes.
QUERY is most useful when a read operation requires input that is too complex to represent conveniently in the URI.
A Real-World Example
Imagine an observability platform that searches logs across several services:
QUERY /logs
Content-Type: application/vnd.example.log-query+json
Accept: application/json
{
"services": [
"payment-api",
"order-api",
"notification-worker"
],
"period": {
"from": "2026-06-24T08:00:00Z",
"to": "2026-06-24T12:00:00Z"
},
"conditions": {
"operator": "or",
"rules": [
{
"field": "level",
"operator": "equals",
"value": "error"
},
{
"field": "durationMs",
"operator": "greaterThan",
"value": 2000
}
]
},
"groupBy": [
"service",
"errorCode"
],
"limit": 100
}
Representing all of this in a URL would be possible, but hardly pleasant.
Using POST /logs/search would work, but it would not communicate generically that the operation is safe and idempotent.
QUERY expresses that intention precisely.
Can I Start Using It Now?
Technically, the method has been defined and officially registered.
In practice, the publication of an RFC does not mean immediate support across the entire ecosystem.
Before adopting QUERY in production, you would need to test at least:
- browsers and HTTP clients;
- frontend libraries;
- backend frameworks;
- web servers;
- reverse proxies;
- load balancers;
- CDNs;
- web application firewalls;
- API gateways;
- tracing tools;
- caching systems;
- SDK generators;
- documentation tools;
- observability platforms;
- CORS policies.
Some tools may reject unknown methods. Others may allow them but fail to recognise their safety, idempotency or caching semantics.
There is also a risk that an intermediary may accept the request but fail to include the body correctly in the cache key.
That would be a far more serious issue than simply returning 405 Method Not Allowed.
For that reason, the first practical uses of QUERY are likely to appear in controlled environments where the client, server and infrastructure are managed by the same organisation.
What About OpenAPI?
Another practical consideration is support from API description tools.
Even if a server accepts QUERY, the surrounding ecosystem must be able to describe it correctly:
- OpenAPI documents;
- Swagger interfaces;
- client generation;
- schema validation;
- mocks;
- testing tools;
- specification-driven gateways.
Until these tools support the method consistently, many teams will continue to use POST /search, even where QUERY would be semantically more appropriate.
Standards do not succeed purely because they are technically elegant. They need to be absorbed by the ecosystem.
Will QUERY Replace POST for Search Endpoints?
Probably not immediately.
The POST /search pattern is already deeply established. It is understood by frameworks, gateways, libraries and documentation tools.
QUERY offers better semantics, but adoption will depend on clear practical benefits:
- safer automatic retries;
- intermediary caching;
- clearer expression of intent;
- format discovery through
Accept-Query; - greater standardisation across APIs.
For many internal APIs, simply replacing POST with QUERY without taking advantage of these properties may offer little immediate value.
On the other hand, public platforms, analytical APIs and sophisticated distributed systems may benefit significantly from the additional semantic clarity.
My View
RFC 10008 addresses a genuine problem.
Developers have been using request bodies for complex queries for years. What was missing was not a way to perform those queries, but a standardised way to communicate their intention to the rest of the HTTP ecosystem.
QUERY does not make something possible that was previously impossible.
It makes explicit something we were already doing ambiguously.
That matters because HTTP is not merely a transport mechanism between a frontend and a backend. Its semantics influence:
- retries;
- caches;
- proxies;
- security;
- observability;
- failure recovery;
- interoperability.
When we choose POST /search, we know that the request is only a query. The rest of the infrastructure may not.
With QUERY, that information becomes part of the protocol itself.
It is still too early to know whether the method will be widely adopted or remain limited to specific APIs and platforms. Its success will depend less on the elegance of the RFC and more on support from browsers, frameworks, gateways, caches and documentation tools.
But the proposal makes sense.
After decades of choosing between enormous URLs and a POST that “does not actually change anything”, HTTP finally has an option designed specifically for complex queries.
Now the entire ecosystem only needs to agree to use it.
No pressure.
References
- RFC 10008 — The HTTP QUERY Method
- RFC 9110 — HTTP Semantics
- RFC 9111 — HTTP Caching


A is the root but it also a parent. In graph theory, a loop (also called a self-loop or a “bucle”) is an edge that connects a vertex to itself. A tree don’t have loop.

