OrbitTest
Dev Tools Mobile Client

API Testing

After 16 Years, HTTP Has a New Method — and It Ends the POST /search Hack

Every API with a complex search endpoint has been telling the same small lie: POST /search — a write verb doing read-only work, because GET couldn't carry the query. RFC 10008 retires that hack with QUERY, the first new core HTTP method since 2010. Here's what the spec actually guarantees, the caching and redirect details most write-ups skip, and a concrete checklist for testing your first QUERY endpoint.

Comparison of the HTTP methods GET, POST, and QUERY — QUERY is the only one that combines a request body with safe, idempotent, cacheable semantics
The HTTP method line-up after RFC 10008: QUERY is the first method that pairs a real request body with GET's safety, idempotency, and cacheability.

The HTTP QUERY method, standardized in RFC 10008 in June 2026, is a request that is safe, idempotent, and cacheable like GET — but carries a request body like POST. It exists for one job: read-only operations whose parameters are too large or too structured to live in a URL. If your API has a POST /search endpoint that never writes anything, QUERY is the method that endpoint always wanted to be.

It’s also the first new general-purpose HTTP method since PATCH in 2010, which makes it a genuine event in protocol terms. But the interesting part isn’t the novelty — it’s the operational details: how caching works when the query lives in the body, what happens on redirects, and what you now need to test that you didn’t before. That’s what this guide covers.

The lie every search endpoint tells

Sixteen years of API design forced a choice on anyone building a search, filter, or reporting endpoint.

Option one: keep it a GET and encode everything into the URL. That works for /products?category=shoes, and it falls apart the moment the query becomes a nested filter, a date-range report, or a geospatial polygon. URLs have practical length limits (most servers and CDNs cap them somewhere between 2 KB and 8 KB), they’re awkward for structured data, and they leak: full query strings end up in access logs, proxy logs, browser history, and Referer headers.

Option two: switch to POST /search. Now the query travels as a clean JSON body — but you’ve changed what you’re telling the infrastructure. POST means “this request may change state.” So shared caches refuse to store the response, clients and proxies won’t automatically retry a request that died mid-flight (retrying a POST could create a duplicate order — they can’t know yours is harmless), and every tool that reads your API — gateways, monitors, security scanners — treats a read as a write.

Nearly everyone picked option two and stopped thinking about it. Elasticsearch’s _search API, GraphQL over HTTP, most reporting endpoints — all POST, all read-only, all semantically mislabeled. QUERY exists because the protocol finally admitted this wasn’t an edge case; it was a missing method.

What RFC 10008 actually specifies

The core definition is compact: a QUERY request asks the server to process the enclosed content in a safe and idempotent manner and respond with the result. The client requests no state change, so the request “can be retried or repeated when needed” — by the client, by a library, or by an intermediary — without any risk of partial or duplicated side effects.

A QUERY request looks like this:

QUERY /products HTTP/1.1
Host: api.example.com
Content-Type: application/json
Accept: application/json

{
  "filter": {
    "category": "shoes",
    "priceRange": { "min": 50, "max": 200 },
    "inStock": true
  },
  "sort": [{ "field": "price", "direction": "asc" }],
  "limit": 25
}

Three details in the spec are easy to miss and worth knowing before you build:

The Content-Type header is mandatory. The server MUST fail the request if Content-Type is missing or inconsistent with the body. An unsupported query format gets a 415 Unsupported Media Type — one more entry for your status-code vocabulary. The body’s media type is what defines the query language: JSON filters, application/x-www-form-urlencoded pairs (the RFC’s own example), SQL, JSONPath — the method doesn’t care, the media type does.

The URL still matters. QUERY doesn’t replace the target URI; the query component of the URL still participates in identifying the resource. The body describes what to ask, the URL describes what to ask it of.

The server can mint a URL for your query. A QUERY response may include a Location header pointing at a resource that represents the query itself — so clients can re-run it later with a plain GET — or a Content-Location pointing at a resource holding the results. The spec’s reasoning is a quiet piece of web philosophy: any important resource ought to have a URI, including a search you’ll want to run again.

QUERY vs GET vs POST

GETPOSTQUERY
Request bodyNo (undefined meaning)YesYes
Safe (read-only)YesNoYes
Idempotent (retry-safe)YesNoYes
Cacheable responseYesNot by defaultYes
Parameters visible in URL/logsYesNoNo

The decision rule that falls out of it is short. Reading data with a handful of simple parameters? GET, as always. Creating or changing anything? POST/PUT/PATCH/DELETE, as always. Reading data with a query too large or structured for a URL? That used to be a shrug and a POST — now it’s QUERY.

The details most write-ups skip

Most coverage of RFC 10008 stops at “GET with a body.” The parts below are what actually change how you build and test.

Accept-Query: the discovery header

A server can advertise which query formats a resource understands with the new Accept-Query response header — the QUERY analogue of Accept-Patch. It’s a Structured Field listing media types, straight from the RFC:

Accept-Query: "application/jsonpath", application/sql;charset="UTF-8"

This makes QUERY endpoints self-describing: documentation generators, API clients, and gateways can discover supported query languages without out-of-band knowledge.

Caching: the killer feature, and its sharp edge

The single biggest practical win over POST /search is that QUERY responses are cacheable — a CDN or reverse proxy can serve popular searches without touching your backend. But the mechanics are new. With GET, the cache key is essentially the URL. With QUERY, the spec requires the cache key to incorporate the request content as well.

That sentence hides two operational consequences. First, your caching layer must support body-aware cache keys at all — many proxies don’t yet. Second, {"a":1,"b":2} and { "b": 2, "a": 1 } are the same query to your application and different byte streams to a naive cache. The RFC explicitly permits caches to normalize the body — stripping content encoding, applying media-type conventions for +json formats — but only for key generation, and only if they do it correctly: the spec’s own security considerations note that faulty normalization can return the wrong response to the wrong request. The pragmatic move is to normalize at the source: give your clients one canonical serialization (sorted keys, no insignificant whitespace) so identical questions produce identical bytes.

Redirects behave differently than you’re used to

Browsers and HTTP libraries have a famous historical quirk: a POST redirected with 301 or 302 gets rewritten into a GET. RFC 10008 states plainly that this exception does not apply to QUERY — a redirected QUERY stays a QUERY, body and all. A 303 See Other is the deliberate exception, telling the client “fetch the result from this URL with a plain GET.” If your framework or HTTP client has redirect handling with POST-era assumptions baked in, this is where it will show.

Conditional requests work like GET

Because QUERY is a read, the full conditional-request machinery applies: a response can carry an ETag, and a client can re-send the same QUERY with If-None-Match and get a 304 Not Modified back. Polling a saved search just got dramatically cheaper — something that was never available to POST /search.

How to test a QUERY endpoint

New method, new failure modes. When QUERY endpoints start appearing in your codebase — and given who wrote the RFC, they will — this is the checklist I’d run. It’s also a decent spec-reading exercise: every item traces back to a MUST or a security consideration in RFC 10008.

  1. Verify it’s actually safe. Send the same QUERY five times; assert that nothing changed — row counts, audit logs, side-effect queues. “Safe” is a promise the server makes, and nothing in the protocol enforces it. An endpoint that logs “recent searches” into user-visible state on QUERY is a spec violation waiting to become a caching bug.
  2. Verify retry behavior. Kill the connection mid-request and let your client retry. This should be boring — that’s the point of idempotency. Then confirm your HTTP client does auto-retry QUERY like it retries GET; libraries that special-case “methods with bodies” as unsafe may refuse.
  3. Attack the Content-Type handling. No Content-Type → the request must fail. An unsupported one → 415. A body that doesn’t match its declared type → an error, not a silent best-effort parse.
  4. Test the cache key in both directions. Same logical query with shuffled key order and whitespace — do you get cache hits (efficiency)? Two different queries that a sloppy normalizer might collapse — do they ever share a cache entry (correctness)? The second failure serves user A’s results to user B. That’s the one to lose sleep over.
  5. Re-check your security tooling. WAFs and API gateways historically inspect URLs for GETs and bodies for POSTs. Confirm yours inspects QUERY bodies too — an injection payload shouldn’t become invisible because the method is new. Treat QUERY input with exactly the scrutiny you give POST input: “safe” describes the server’s promise, not the client’s intentions.
  6. Pin the response contract. A QUERY endpoint is still an API contract. Snapshot the response schema with a JSON-to-Schema generator and fail the build when it drifts — the same contract-testing discipline you’d apply to any read endpoint.
  7. Check CORS if browsers are involved. QUERY isn’t a CORS-safelisted method, so every cross-origin QUERY triggers a preflight OPTIONS. Your server needs to list QUERY in Access-Control-Allow-Methods, and your latency budget needs to absorb the extra round trip (or an Access-Control-Max-Age).

Trying it by hand is one flag away in curl:

curl -X QUERY https://api.example.com/products \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{"filter":{"category":"shoes","inStock":true},"limit":25}'

Where support stands (July 2026)

The RFC is weeks old, so the honest status is: arriving, unevenly.

The plumbing is mostly ready. Node.js’s HTTP parser has accepted QUERY since 2024, back when the spec was still a draft, so Express/Fastify/Koa handlers can route it today. Browsers’ fetch() passes custom methods through, so fetch(url, { method: "QUERY", body }) already works. Most HTTP clients (Python’s httpx, Go’s net/http, Java’s HttpClient) never restricted method names in the first place.

Framework ergonomics are the lagging half — first-class routing like app.query(...) or RequestMethod.QUERY is still working through the usual proposal pipelines in Spring, Rails, and friends, so expect a season of route.match(method: "QUERY") escape hatches. On the infrastructure side it’s reasonable to expect CDNs to move quickly — two of the RFC’s three authors work at Cloudflare and Akamai — but body-aware caching is exactly the feature to verify rather than assume, per the checklist above.

The migration path is graceful, which is the nicest thing about it: add QUERY alongside your existing POST /search, advertise it with Accept-Query, and let clients that understand it opt in. Nothing breaks for clients that don’t.

Frequently asked questions

What is the HTTP QUERY method?

QUERY is a standard HTTP method, published in RFC 10008 in June 2026, for read-only operations whose parameters travel in the request body instead of the URL. It is defined as safe (it doesn’t change server state), idempotent (it can be retried without side effects), and cacheable — the same guarantees as GET — while carrying a structured body like POST. It’s the first new general-purpose HTTP method since PATCH in 2010.

Can a GET request have a body instead?

Technically a GET body isn’t forbidden, but HTTP gives it no meaning: servers, proxies, and CDNs may ignore it, strip it, or reject the request outright, and caches won’t key on it. That’s exactly the gap QUERY closes — it’s the standards-blessed way to send a read-only request with a body, with defined semantics at every hop.

Is QUERY just POST /search with a different name?

No — the difference is the guarantee, not the payload. A POST tells every intermediary “this might change something,” so responses aren’t cached and failed requests aren’t safely retried. QUERY promises the operation is read-only and repeatable, which lets caches store results, lets clients and proxies retry automatically after a network failure, and makes the API’s intent explicit to every tool that reads it.

Are QUERY responses cacheable?

Yes — that’s one of its biggest advantages over POST. But unlike GET, where the URL is the cache key, a QUERY cache key must incorporate the request body. Two logically identical queries serialized differently (key order, whitespace) can miss the cache, and a cache that keys incorrectly can serve the wrong response. Normalize how clients serialize query bodies, and test the cache behavior explicitly.

The takeaway

HTTP grew a new verb because an entire industry spent sixteen years describing reads as writes and paying for it in lost caching, unsafe retries, and dishonest APIs. QUERY doesn’t add a capability you couldn’t hack around before — it adds a guarantee, and guarantees are what infrastructure is built on.

You don’t need to migrate anything this quarter. But the next time you catch yourself writing POST /search for an endpoint that never writes, you now know it’s a workaround with an expiry date — and when QUERY endpoints reach your test plan, safety, cache keys, and Content-Type handling are where the new bugs will live.


Sources

Put Your API's Read Paths Under Test

Send real requests, inspect status, headers, and timing in one place, and write post-run checks that fail the moment a response drifts — whether the endpoint speaks GET, POST, or QUERY.

Abhay Kumar
Abhay Kumar Creator of OrbitTest

QA engineer building OrbitTest, Orbittest Studio, and Orbittest Client — intent-first browser testing, Android automation, and an API testing workspace for real QA workflows.

Connect on LinkedIn