QUERY has been added as an HTTP method. My intuitive impression was "so GET can now have a body?", which is more or less what I expected, but I was curious what it means in practice, so I looked into it.
QUERY /orders HTTP/1.1
Content-Type: application/json
{
"status": ["paid", "shipped"],
"createdAt": {
"from": "2026-07-01",
"to": "2026-07-31"
}
}
Roughly speaking, this is an HTTP method for making safe queries with a body.
It was standardized as RFC 10008 in June 2026.
Until now, we made do with GET or POST
In practice, I have often built APIs like this.
POST /orders/search
Content-Type: application/json
{
"status": ["paid", "shipped"],
"from": "2026-07-01",
"to": "2026-07-31",
"sort": {
"field": "createdAt",
"order": "desc"
}
}
What it does is search, so it is essentially a read.
Still, once the conditions get complicated, passing them in the request body is easier to handle than cramming them into the URL.
For example, you might want to fetch only specific IDs, or conversely exclude specific IDs.
{
"includeIds": ["order_001", "order_002", "order_003"]
}
Or a condition like this is also possible.
{
"excludeIds": ["order_999", "order_888"]
}
If you try to express this with query parameters alone, it looks like this.
GET /orders?includeIds=order_001&includeIds=order_002&includeIds=order_003
This is still tolerable.
But as the number of IDs grows, or as it gets combined with other search conditions, the URL becomes harder and harder to read.
GET /orders?status=paid&includeIds=order_001&includeIds=order_002&excludeIds=order_999&sort=createdAt_desc
Furthermore, when search conditions become nested, or when you need things like "match any of this group of conditions" or "exclude this condition," expressing them with query parameters alone becomes quite painful.
In the past there were also cases where we ran into URL length limits.
For that reason, in practice we sometimes used a form likePOST /search and passed the search conditions as JSON.
POST /orders/search
Content-Type: application/json
{
"includeIds": ["order_001", "order_002"],
"excludeIds": ["order_999"],
"filters": {
"status": ["paid", "shipped"],
"createdAt": {
"from": "2026-07-01",
"to": "2026-07-31"
}
},
"sort": [
{
"field": "createdAt",
"order": "desc"
}
]
}
With this form, the API is much easier to work with.
The frontend can turn the state of the search form directly into JSON, and the backend can validate it easily.
However, it feels a bit off.
POST is generally strongly associated with "creating" or "changing state."
We are actually just searching, yet we use POST as the HTTP method. There is a mismatch: it is a read, but it is a POST.
Is it OK to use POST for a search?
POST /orders/search exists in practice.
But POST is, in principle, often used as a method that "creates something," "processes something," or "might change state."
Of course, that does not mean a POST always changes state. Even if you use POST for search, there is no real harm as long as the server does not change state.
Still, looking only at the HTTP method, clients, proxies, and caches have a hard time judging whether it is a safe read.
For example, is it OK to automatically retry when the network drops midway? Is it OK to cache it? Is it free of side effects even if it is executed again?
WithGET, you can judge "this is basically a safe read."
With POST, it is hard to judge unless you know the server's particular circumstances.
In other words, POST /search is convenient, but its meaning in HTTP terms was a bit ambiguous.
I think QUERY appeared this time to fill that ambiguity.
QUERY is a "safe query with a body"
In RFC 10008,QUERY is defined as a method that processes the content included in the request in a safe and idempotent way against the request target, and returns the result.
Safe roughly means "does not change the server's state." Idempotent means that executing the same request any number of times has the same effect in terms of side effects.
SoQUERY can send a body like POST, but semantically it leans toward reads and queries.
QUERY /orders
Content-Type: application/json
{
"status": ["paid", "shipped"],
"createdAt": {
"from": "2026-07-01",
"to": "2026-07-31"
}
}
This reads quite naturally.
It queries/orders with the conditions specified in the body.
But it is not creating or updating orders.
It is easy to understand if you think of it as being able to express, as an HTTP method, what we used to write as POST /orders/search a bit more accurately.
The original problem: a "safe method with body"
What is interesting is that the draft name before it became an RFC wassafe-method-w-body.
In other words, the problem being addressed was clear from the start.
We want a safe HTTP method that can carry a body.
That was the point.
GET is safe, but it is not suited to putting complex conditions in a body. POST can carry a body, but it is not necessarily safe.
The use case that sits between these two has always existed in practice.
GET = safe. But not suited to complex queries with a body
POST = can send a body, but is not necessarily a safe query
QUERY = can send a body and can be expressed as a safe query
Organized this way, it is easy to see what QUERY is trying to fill.
Wouldn't GET with a body have been enough?
The natural thought here is: couldn't we just have attached a body toGET?
GET /orders HTTP/1.1
Content-Type: application/json
{
"status": ["paid"]
}
Judging by looks alone, this seems fine.
However, bodies on GET have historically been handled unreliably. In practice it is even simpler: servers, proxies, libraries, and browser APIs do not necessarily handle it as expected.
It may be ignored. It may be dropped by some middleware along the way. The client library may not be able to send it at all.
Caching is also difficult. GET is basically handled with the URI at the center, so how to treat requests with the same URI but different bodies tends to vary between implementations.
So rather than forcibly stretching the meaning of GET, it is clearer to define a new safe query method that can carry a body.
That is how I understandQUERY.
Why QUERY and not SEARCH?
As a name,SEARCH also seems plausible.
In fact, for a search API, SEARCH /orders might look more intuitive.
However, HTTP already has existing methods, such as SEARCH from WebDAV.
Also, what we want to do here is not just search, but to make a query with a body against a target resource.
Thinking about it that way, QUERY is broader than SEARCH.
It can cover not only search but also aggregation, transformation, filtering, and executing a query language.
For example, something like the following could also fall within the scope ofQUERY.
QUERY /analytics
Content-Type: application/json
{
"groupBy": "month",
"metrics": ["sales", "orders"],
"where": {
"country": "JP"
}
}
This is closer to a "query" than a "search."
In that sense, the name QUERY is quite natural.
There is also an Accept-Query header
QUERY also defines a response header called Accept-Query.
It is used by the server to indicate "this resource accepts QUERY in this format."
For example, it looks like the following.
HTTP/1.1 200 OK
Accept-Query: application/json
Looking at this, the client can tell that it can send QUERY to this resource with an application/json body.
Of course, how much this will be used in practice depends on how support develops. At least on the spec side, they have thought through even "how to let clients discover QUERY."
Caching is possible, but harder than GET
SinceQUERY is safe and idempotent, its compatibility with caching has also been considered.
However, it is not as simple as GET.
With GET, most caches build the cache key around the URI. WithQUERY, on the other hand, the query conditions are in the body.
That is, even for QUERY against the same /orders, a different body means a different query.
QUERY /orders
Content-Type: application/json
{ "status": "paid" }
QUERY /orders
Content-Type: application/json
{ "status": "cancelled" }
These two should give different results even with the same URI.
Therefore, cachingQUERY needs to take into account not only the URI but also the request body and related metadata.
It is cacheable per the spec, but harder to implement than GET.
I think this needs attention when using it in practice.
CORS requires a preflight
When usingQUERY from a browser, you also need to think about the CORS preflight.
Because it is not treated as a so-called simple request the way GET and some POSTs are, an OPTIONS request is sent first when sending to a different origin.
In other words, on the API side you need to allow QUERY not only in the API itself but also in the CORS settings.
Access-Control-Allow-Methods: GET, POST, QUERY, OPTIONS
This is also a non-trivial hurdle for using QUERY in practice.
Should you use it in practice right away?
So, should you switch everything toQUERY from tomorrow? I think it is fine to be cautious there.
It has been standardized as a spec. But in practice, the surrounding support matters much more than the HTTP method itself.
For example, all of the following need to passQUERY through without trouble.
- Browsers
- Clients such as fetch / axios
- API Gateway
- CDN
- WAF
- Reverse proxies
- Web frameworks
- Documentation generation such as OpenAPI
- Logging infrastructure
- Monitoring tools
Even if it is correct as an HTTP method, it is hard to use if somewhere along the way it is rejected as an unknown method.
Especially for public APIs or APIs hit by many kinds of clients, I thinkPOST /search is still the safer choice in many situations.
So what is the value of QUERY?
I think the value ofQUERY is less about immediately replacing POST search and more about giving a name to a design that used to be ambiguous.
For search APIs, there were roughly three options until now.
GET /orders?status=paid
This is fine for simple searches.
POST /orders/search
Content-Type: application/json
{
"status": "paid"
}
For complex searches, this was the most common choice in practice.
QUERY /orders
Content-Type: application/json
{
"status": "paid"
}
And from now on, this option is available too.
Summarized, it looks like this.
| What you want to do | Suitable method |
|---|---|
| Simple retrieval expressible in the URL | GET |
| Safe query with a body | QUERY |
| Creation, updates, or processing with side effects | POST |
This breakdown is quite clear.
In particular,QUERY suits APIs whose search conditions are complex but which are clearly reads in meaning.
It is interesting from a REST API design perspective
In REST API design, the question often comes up: "is this a resource or an operation?"
For example, there is an API like the following.
POST /orders/search
This is natural in practice, but it also looks a bit RPC-like,
because the operation name search is in the path.
With QUERY, on the other hand, you can write this.
QUERY /orders
The target is just /orders.
It queries that collection of order resources.
This looks cleaner as a division of roles between HTTP method and resource.
Of course, being clean does not mean it is always better. In practice, support status and the team's understanding often matter more.
Even so, as a design it is quite convincing.
Is POST search a mistake?
So, was thePOST /search we have used until now a mistake?
Personally, I do not think so.
If anything, I think it was quite a reasonable design given real-world constraints.
With GET, the URL gets too long. GET with a body is worrying from an implementation standpoint. We want to send complex search conditions as JSON.
That left POST as the only choice.
It is probably best to seeQUERY not as a rejection of POST search, but as something that lets HTTP formally express part of the role POST search had been playing.
Until now we substituted POST. From now on, there will be situations where QUERY fits better in meaning.
That level of enthusiasm seems about right.
Summary
The HTTPQUERY method fills a subtle gap that existed between GET and POST.
GET is suited to simple retrieval expressible in the URL. POST is suited to creation, changes, and processing with side effects. But read APIs that just want to send complex search conditions in a body sat between the two.
Until now, we managed that middle ground withPOST /search.
QUERY is the method that gives that middle ground a name and a meaning.
However, just because it has been standardized does not mean it can be used across all APIs right away. You need to check the surrounding support: CDNs, WAFs, API Gateways, frameworks, client libraries, and so on.
So at this point, the following view seems good.
Simple search: GET
Complex search where compatibility matters: POST /search
Want to express the HTTP semantics accurately: QUERY
Rather than becoming mainstream right away, I think QUERY gives the "search API with a body" that we had been doing in practice more or less by feel an official place in HTTP.