CheckOps home

Guide

HTTP QUERY Method (RFC 10008): An API Testing Guide

RFC 10008 defines the HTTP QUERY method, a safe, idempotent request with a body. What it changes for API testing, status codes, caching, CORS, plus a checklist.

15 June 2026 · 11 min · CheckOps

For as long as most of us have tested APIs, complex searches have forced an awkward choice. Put a large query into a GET URL and hit length limits and logging problems, or send it in a POST body and lose the promise that the request is safe to repeat. In June 2026 the IETF published RFC 10008, The HTTP QUERY Method, which adds a third option.

The abstract is short: “A QUERY requests that the request target process the enclosed content in a safe and idempotent manner and then respond with the result of that processing. This is similar to POST requests, but QUERY requests can be automatically repeated or restarted without concern for partial state changes.”

This guide explains what QUERY is, how it differs from GET and POST, and, most importantly for readers of this site, what you need to test when your team starts to adopt it. If you are still choosing tools, our best API testing tools of 2025 is a good companion.

What is the HTTP QUERY method?

RFC 10008 is a Standards Track document from the IETF HTTP working group. Its authors are Julian Reschke (greenbytes), James M. Snell (Cloudflare) and Mike Bishop (Akamai). It registers a new method, QUERY, in IANA’s HTTP Method Registry as both safe and idempotent, and a new response header, Accept-Query.

The RFC’s own comparison table is the clearest summary:

Property GET QUERY POST
Safe yes yes potentially no
Idempotent yes yes potentially no
URI for query itself yes (by definition) optional (Location response field) no
URI for query result optional (Content-Location) optional (Content-Location) optional (Content-Location)
Cacheable yes yes yes, but only for future GET or HEAD requests
Content (body) “no defined semantics” expected expected

In short, QUERY behaves like GET in its guarantees, and like POST in where the input goes.

Why QUERY exists: the GET vs POST problem

The RFC lists four problems with putting large queries in the URI:

  • Size limits are often unknown, because a request can pass through many systems. RFC 9110 only recommends supporting at least 8,000 octets.
  • Encoding some kinds of data into a valid URI is inefficient.
  • “Request URIs are more likely to be logged than request content and may also turn up in bookmarks.”
  • Every combination of query inputs becomes a distinct resource.

The common workaround is POST. But, as the RFC says, with POST “it is not readily apparent … that a safe, idempotent query is being performed.” Clients, proxies and retry logic have to treat a POST as potentially changing state. That blocks automatic retries and caching.

Here is the RFC’s own example. A GET search:

GET /feed?q=foo&limit=10&sort=-published HTTP/1.1
Host: example.org

The same thing as QUERY:

QUERY /feed HTTP/1.1
Host: example.org
Content-Type: application/x-www-form-urlencoded

q=foo&limit=10&sort=-published

The rules that matter for testers

The RFC is short, but several of its rules translate directly into test cases.

Content-Type is mandatory

“Servers MUST fail the request if the Content-Type request field … is missing or is inconsistent with the request content.” The server is not allowed to sniff the body and guess.

Status codes for failures

Section 2.1 recommends specific codes:

Situation Recommended status
No media type on the request 4xx, such as 400
Media type not supported by the resource 415, with Accept-Query to list supported types
Media type inconsistent with the actual content 400
Valid format but the query cannot be processed 422, e.g. a valid SQL query naming a non-existent table
Requested response type (Accept) not supported 406

This table is practically a test plan for negative cases.

Safe and idempotent means retryable

QUERY requests “can be retried or repeated when needed, for instance, after a connection failure.” If your server’s QUERY handler writes to a database, logs an audit event that changes behaviour, or decrements a quota in a way that changes results, that is a bug against the specification. The RFC does allow servers to create additional resources, such as a stored result reachable by GET, without breaking safety.

Discovery: OPTIONS, Allow and Accept-Query

Clients can discover support with OPTIONS and the Allow header:

OPTIONS /contacts HTTP/1.1
Host: example.org

HTTP/1.1 200 OK
Allow: GET, QUERY, OPTIONS, HEAD

And discover supported query formats with the new Accept-Query header, for example:

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

Accept-Query is a Structured Field, and the RFC says it “MUST be processed as specified” by RFC 9651. That means a naive comma split is not good enough for a client or a test parser.

Location and Content-Location

A successful response can include Content-Location, pointing to a GET-able copy of the result, or Location, pointing to a GET-able resource that repeats the query. Servers that do this give clients a way to switch to GET for later requests.

Redirects

A 301 or 308 means the resource moved permanently. The RFC is explicit that “the exceptions for redirecting a POST as a GET request after a 301 or 302 response do not apply to QUERY requests.” A 303 means the query can be accomplished with a GET to the Location URI.

Caching

QUERY responses are cacheable, and the cache key “MUST incorporate the request content” and related metadata. Caches may normalise the body, for example by removing content encoding or normalising JSON, but the RFC warns that incorrect normalisation “can return an incorrect response if normalization results in a false positive.”

CORS

“A QUERY request from user agents implementing Cross-Origin Resource Sharing (CORS) will require a ‘preflight’ request, as QUERY does not belong to the set of CORS-safelisted methods.” Browser clients calling a QUERY endpoint on another origin will send an OPTIONS preflight first.

An API test checklist for QUERY endpoints

Here is a practical checklist built from the RFC. Use it in your Postman, Bruno, REST Assured or Karate suites, or with plain curl.

Functional

  1. A valid QUERY with a supported Content-Type returns 200 and the expected results.
  2. The same QUERY sent twice returns equivalent results, and nothing changes in between.
  3. The body defines the query; query parameters in the URI behave as documented by your API.
  4. Pagination or result limits work, using the query format’s own features. The RFC expects formats to handle paging themselves rather than through HTTP Range requests.

Negative

  1. Missing Content-Type returns a 4xx such as 400.
  2. Unsupported Content-Type returns 415 with Accept-Query.
  3. Content-Type that doesn’t match the body returns 400. The server must not guess.
  4. A well-formed query that can’t be processed returns 422.
  5. An unsupported Accept header returns 406.
  6. Endpoints that don’t support QUERY return 405 with an Allow header.

Safety and idempotency

  1. A QUERY does not create, update or delete business data. Check the database or a side-effect log before and after.
  2. Repeating a QUERY after a simulated connection failure does not double any effect.
  3. Rate limiting or quota accounting, if any, is documented and doesn’t change results.

Discovery and headers

  1. OPTIONS returns an Allow header that includes QUERY.
  2. Accept-Query is valid Structured Field syntax and lists the media types you actually support.
  3. If you return Location or Content-Location, a GET to that URI returns the expected result.
  4. Any temporary result URI does not leak sensitive parts of the query body, as the security section requires.

Caching and intermediaries

  1. Two different bodies to the same URI never return each other’s cached responses.
  2. Equivalent bodies, for example JSON with different whitespace, are handled as you intend.
  3. Your CDN, gateway and WAF pass QUERY through rather than blocking it as an unknown method.

Browsers

  1. Cross-origin QUERY triggers a CORS preflight, and your server’s preflight response allows the method.

Testing QUERY with common tools

Support for new HTTP methods depends on each tool’s ability to send a custom method. The good news is that most API tools have always allowed arbitrary method names. Check yours before you rely on it.

curl

curl sends any method name with -X:

curl -i -X QUERY https://api.example.org/contacts \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json' \
  --data '{"select": ["surname", "email"], "limit": 10}'

For discovery:

curl -i -X OPTIONS https://api.example.org/contacts

Playwright API testing

Playwright’s request context has a generic fetch that accepts a method:

import { test, expect } from '@playwright/test';

test('QUERY returns contacts and is repeatable', async ({ request }) => {
  const body = { select: ['surname', 'email'], limit: 10 };
  const first = await request.fetch('/contacts', {
    method: 'QUERY',
    headers: { 'Content-Type': 'application/json' },
    data: body,
  });
  expect(first.status()).toBe(200);

  const second = await request.fetch('/contacts', {
    method: 'QUERY',
    headers: { 'Content-Type': 'application/json' },
    data: body,
  });
  expect(await second.json()).toEqual(await first.json());
});

Load testing

If you load test QUERY endpoints with k6, its HTTP module has a generic http.request(method, url, body, params) function. Pay special attention to caching behaviour under load: a cache that keys only on the URI will return wrong results very quickly.

Describing QUERY endpoints in OpenAPI

Contract tests are only as good as the contract. The good news is that the OpenAPI Specification 3.2.0, released in September 2025, added a query field to the Path Item Object, described as “a definition of a QUERY operation”. It was written against the IETF draft that became RFC 10008. A minimal description looks like this:

openapi: 3.2.0
info:
  title: Contacts API
  version: 1.0.0
paths:
  /contacts:
    query:
      operationId: searchContacts
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                select: { type: array, items: { type: string } }
                limit: { type: integer, maximum: 100 }
      responses:
        "200":
          description: Matching contacts
        "415":
          description: Unsupported query format
        "422":
          description: Query could not be processed

The query field is not valid in OpenAPI 3.0 or 3.1 documents, so your tooling has to understand 3.2 before it can validate or mock these operations. Before you rely on generated clients, mocks or contract tests for QUERY endpoints, check that each tool in the chain, from linters and code generators to mock servers such as WireMock, supports OpenAPI 3.2 and the new method. Where it doesn’t, keep a small set of hand-written tests for QUERY until support catches up.

Documenting the error responses matters as much as the happy path. The RFC’s status code guidance gives you a ready-made list: 400, 406, 415 and 422 should all appear in the contract if your server can return them.

Infrastructure is the real risk

Most early QUERY bugs will not be in your application code. They will be in the layers between the client and the server:

  • Server frameworks and runtimes that don’t recognise the method and reject it, or pass it through without a usable method name.
  • API gateways and load balancers with method allow-lists.
  • Web application firewalls that block unknown methods by default.
  • Caches and CDNs that either refuse to cache QUERY or, worse, cache it keyed on the URI alone.
  • Observability tools that drop or mislabel unknown methods in dashboards.

Test the full path, not just the service. An end-to-end check through your real gateway and CDN in a staging environment will catch more QUERY problems than unit tests.

Should your team adopt QUERY now?

QUERY is a good fit for search, filtering, reporting and GraphQL-style read operations where the query is large or structured. It is less useful for small queries. The RFC itself notes that “if one needs to send queries that are actually this short, it is likely better to use GET.”

A sensible adoption path:

  1. Start with an internal, read-only search endpoint.
  2. Support both POST and QUERY for a transition period, so older clients keep working.
  3. Add the checklist above to your contract tests.
  4. Verify every hop in production-like infrastructure.
  5. Document Accept-Query and status codes in your API reference.

Update, 30 October 2026: a runtime check

In a quick check on 30 October 2026, the same minimal QUERY handler was run on two Node.js versions. On Node.js 20.19.2, the runtime listed QUERY in http.METHODS, but the request object’s method came through as undefined, so a simple req.method === 'QUERY' check failed and the server returned 405. The same code on Node.js 22.23.4 received QUERY correctly, returned 200 for a valid JSON query and 415 for text/plain. This is a small, informal check, not a compatibility matrix, but it shows why “test the runtime” belongs on the checklist.

What this means for your team

RFC 10008 gives API designers a way to say “this is a read, and it’s safe to retry” while still sending a structured body. For testers, it brings a clear specification with concrete status codes and rules, which makes it unusually easy to turn into test cases.

The hard part will be infrastructure support, not the method itself. Test the whole path, check idempotency explicitly, and treat caching as a correctness concern, not just a performance one.

Frequently asked questions

What is RFC 10008?

RFC 10008 is the IETF Standards Track document, published in June 2026, that defines the HTTP QUERY method and the Accept-Query header field.

Is the QUERY method safe and idempotent?

Yes. IANA registers QUERY as both safe and idempotent, so clients and intermediaries can retry it and cache its responses.

What is the difference between QUERY and POST?

Both carry the query in the request body. POST may change server state, so it can’t be retried or cached in the same way. QUERY is explicitly safe and idempotent.

Does QUERY require a CORS preflight?

Yes. QUERY is not a CORS-safelisted method, so cross-origin browser requests trigger an OPTIONS preflight.

What status code should a server return for an unsupported query format?

415 (Unsupported Media Type), ideally with an Accept-Query header listing the supported formats. A missing Content-Type should get a 4xx such as 400.

Can I describe QUERY endpoints in OpenAPI?

Yes, from OpenAPI 3.2.0, which adds a query operation to the Path Item Object. Earlier versions such as 3.0 and 3.1 have no way to describe it, so check that your linters, generators and mock servers support 3.2 first.

Do I have to replace POST search endpoints with QUERY?

No. QUERY is an option, not a requirement. Many teams will support both for a while, keeping POST for older clients and adding QUERY where retries and caching bring real benefits.

Further reading on CheckOps