Aman M.

Query

28th July, 2026

Query

HTTP is getting a new method. That almost never happens.

The methods we use every day have been stable for decades, so a new one from the IETF deserves a closer look. This one is called QUERY.

A read can still be complicated

The main HTTP methods are familiar:

  • GET fetches data
  • POST creates something or triggers an action
  • PUT replaces or updates a resource
  • DELETE removes something

The method also tells the rest of the HTTP stack how to treat a request. Two properties matter here:

  • A method is safe if it does not change anything on the server. It only reads data. GET is safe because fetching a resource does not modify it.
  • A method is idempotent if sending the same request once or ten times leaves the server in the same final state as sending it once. GET, PUT, and DELETE are idempotent.

POST is neither safe nor idempotent. Sending the same request twice might create two records.

Those properties explain the gap that QUERY is meant to fill.

Why GET stops working

Searching is one of the most common jobs for an API. A real search might need filters, sorting, pagination, date ranges, nested conditions, boolean logic, arrays of IDs, or an entire query language.

Even when the query is complicated, it is still a read. You are asking the server for data, not changing anything.

For a simple search, GET is exactly right:

http
GET /users?role=admin&active=true&page=2

The query is in the URL, which makes it easy to share and gives caches a straightforward key.

The trouble starts when the query outgrows the URL.

URLs have practical limits

The limit depends on the infrastructure handling the request. A URL may pass through several servers, proxies, and CDNs, each with its own maximum length.

The HTTP specification recommends that servers support at least 8,000 characters. Go beyond what a server accepts and you might receive a 414 URI Too Long response.

Structured data is awkward in a URL

It is possible to flatten structured data into query parameters, but the result quickly becomes difficult to read and maintain:

json
{
  "where": {
    "and": [{ "status": "active" }, { "createdAfter": "2026-01-01" }]
  }
}

You can percent-encode the whole object, but that does not make the URL easier to work with.

URLs travel widely

URLs appear in browser history, access logs, analytics systems, monitoring tools, and sometimes referrer headers. If a query contains sensitive information, putting it in the URL sends that information to all of those places.

So why not put the filters in a GET request body?

You technically can attach one. The problem is that HTTP gives a GET body no defined meaning.

A proxy, cache, or server can ignore it, drop it, or reject the request. Some infrastructure will forward it, but there are no guarantees.

A GET request with a body is therefore not reliable enough to build an API around.

POST is the usual workaround

Because GET cannot reliably carry a payload, most APIs use POST for complex searches instead:

http
POST /users/search
Content-Type: application/json

{
  "where": {
    "and": [
      { "status": "active" },
      { "createdAfter": "2026-01-01" }
    ]
  },
  "sort": [{ "createdAt": "desc" }],
  "page": 2
}

This solves the URL problem. The payload is in the body, so JSON stays structured and there is no practical URL limit to worry about.

The tradeoff is that POST describes the request incorrectly.

Every proxy, cache, and CDN between the client and server sees POST, but it cannot know that this particular request only reads data. It has to assume the request might change something.

That affects three useful behaviors.

Caching

Caches and CDNs generally do not cache POST responses because the request might have modified the server.

As a result, identical searches go back to the application and recompute the same result, even when the underlying data has not changed.

Retrying safely

Imagine the connection drops while the request is being processed and you never receive a response.

Can you retry it?

With POST, the answer is unclear. The first request might have reached the server. If it was a write, retrying could create a duplicate record.

Clients and proxies have to be cautious, even when the request was only a read.

Clear semantics

Anyone looking at the API sees a POST request and reasonably assumes it causes a mutation.

Tools inspecting the traffic make the same assumption. The method says one thing while the endpoint does another.

Enter QUERY

This is the gap RFC 10008 is intended to close.

A QUERY request looks structurally similar to POST:

http
QUERY /users
Content-Type: application/json

{
  "where": {
    "and": [
      { "status": "active" },
      { "createdAfter": "2026-01-01" }
    ]
  },
  "sort": [{ "createdAt": "desc" }],
  "page": 2
}

The method name tells the infrastructure what the request is doing.

QUERY is safe and idempotent. Those properties are part of the method definition, rather than something a proxy has to infer from the endpoint.

The request can carry a structured body while making it clear that the server is only being asked for data.

The body and its format

The body is central to a QUERY request, so the Content-Type header is required.

The specification does not define one universal query language. An endpoint might accept JSON:

http
QUERY /products
Content-Type: application/json

{
  "category": "books",
  "price": {
    "lessThan": 50
  }
}

Another endpoint could accept GraphQL or a SQL-like format.

QUERY defines the method. The endpoint still decides which content type and query language it supports.

Discovering supported formats

RFC 10008 also introduces a response header called Accept-Query.

A server can use it to advertise whether it supports QUERY and which query formats it accepts:

http
HTTP/1.1 200 OK
Accept-Query: application/json, application/graphql

A client can check this before sending a request instead of guessing and being rejected later.

How caching works

Because QUERY is safe, its responses can be cached. There is one important difference from GET.

For a normal GET, the URL is usually enough to build the cache key:

text
GET /users?role=admin

With QUERY, two requests can use the same URL and have different bodies:

http
QUERY /users

{ "role": "admin" }
http
QUERY /users

{ "role": "customer" }

The cache therefore cannot use the URL alone. It also needs to include the request body and its relevant metadata.

The same query should map to the same cache entry. A different query should not.

That gives read-heavy APIs a way to use HTTP caching without putting complex filters in the URL or describing every search as a write.

Conditional queries

QUERY also works with conditional request headers such as If-None-Match and If-Modified-Since.

If the result has not changed, the server can return 304 Not Modified instead of sending it again. That makes repeated complex searches cheaper in the same way as conditional GET requests.

Giving a query its own URL

A server can also make a query or its result available through a URL. Location can point to a resource that runs the same query again, while Content-Location can point to the specific result that was just returned.

The client can then use an ordinary GET request instead of resending the query body. These URLs may be temporary, but they make useful queries easier to revisit.

Is it ready to use?

QUERY is official and has reached the proposed standards stage, where implementations are beginning to appear.

OpenAPI 3.2 can describe QUERY endpoints. ASP.NET Core support is in progress. Since HTTP methods are ultimately strings, runtimes such as Go and Node.js can already send a QUERY request without any new dependencies:

ts
const response = await fetch("/users", {
  method: "QUERY",
  headers: {
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    where: {
      status: "active",
    },
  }),
});

That does not mean every browser, proxy, CDN, and server supports it today.

HTTP infrastructure changes slowly, so adoption will take time.

The problem it addresses is real:

  • GET has the right meaning but cannot reliably carry a body.
  • POST can carry the body but describes the request incorrectly.

For years, APIs have had to choose between those two compromises. QUERY is an attempt to remove that choice: this request has a complex payload, but it only reads data.

HTTP does not get a new method very often. This one is worth watching.