How do bots abuse GraphQL endpoints that REST rate limits never see?

Short answer: A REST rate limit counts requests per route. A GraphQL bot does not care. It sends one request that asks for a thousand objects, batches fifty operations into a single call, or walks the schema with introspection until it knows every field worth scraping. Counting requests against an endpoint designed to make each request elastic is measuring the wrong unit. Cost is the unit: how much data, how much nesting, how much work per call.

Introspection is a map handed to strangers

Introspection lets a client ask the schema what it can do, which is wonderful in development and generous in production. A scraper with introspection does not probe or guess. It reads the catalogue: every type, every field, every argument, including the ones the storefront interface never uses, the wholesale price field, the inventory by location query, the customer lookup nobody linked from the site.

Turn introspection off for unauthenticated production traffic, or gate it behind the same credential a partner integration carries. Legitimate first party clients ship with their queries known. They do not need to discover the schema at runtime, and a client that does is either a developer tool you recognise or a visitor you do not.

Batching and depth multiply a single request

Two features turn one allowed request into fifty: batching, where an array of operations rides in one call, and depth, where nested fields expand one query into a tree of resolver work. A bot that batches a product query fifty ways has made fifty requests by any honest accounting while your limiter recorded one.

Set limits in the units that hurt: maximum query depth, a complexity score per operation with fields weighted by cost, and a per request cap on batched operations. Complexity scoring is the control that survives contact with real traffic because it prices the expensive query and the cheap one differently, instead of pretending all requests are equal.

Persisted queries end the argument

The strongest control for a first party storefront is an allowlist of persisted queries: the server only runs operations it has seen before, identified by hash, registered when the site was built. A scraper can still call those queries, at the volumes and shapes the site itself uses, but it cannot invent a new one that joins customer data to order data in a way the interface never offered.

Persisted queries do not stop credential stuffing or checkout abuse, and they should not be sold as a bot solution. They close one specific door, schema exploration and arbitrary data extraction, and they close it completely. Pair them with per client cost budgets and the GraphQL endpoint stops being the soft route around every control you put on the HTML site.

If your logs say POST to the GraphQL endpoint ten thousand times, you have no evidence. Log the operation name, the complexity score, the fields touched, and the client identity for every call. Scraping has a shape in that data: one client enumerating a collection in ID order, touching price and stock fields and nothing else, at a steadiness no browsing human produces.

Log the operation, not just the endpoint

That log is also how you tune without breaking integrations. Before enforcing a complexity cap, run it in report only mode against a week of traffic and read the top operations by cost. Half the time the worst offender is a legitimate partner with a lazy query, and a conversation fixes what a block would have turned into an outage.

Keep reading: How do residential proxy networks make bot traffic look like neighbourhood traffic?, How do bots exploit mobile app APIs that the website never exposes?.

Frequently asked questions

Why do REST rate limits fail on GraphQL?

Because one GraphQL request can contain many operations and return very different amounts of data. Counting requests treats a trivial lookup and a full catalogue extraction as identical. Cost based limits, depth caps, and batching caps measure what the server actually spends.

Why do REST rate limits fail on GraphQL?

For unauthenticated traffic, yes, or gated behind a credential. First party clients already know their queries. Leaving introspection open hands any visitor a complete map of fields the public interface never exposes.

Why do REST rate limits fail on GraphQL?

An allowlist where the server executes only pre registered operations, identified by hash. Clients cannot invent new queries, which blocks arbitrary data extraction through the schema while leaving normal storefront traffic untouched.

Why do REST rate limits fail on GraphQL?

They can call the allowed queries, including product and price lookups the site itself uses. That is why cost budgets, per client limits, and operation level logging still matter. Persisted queries remove invention, not volume.

See your own numbers.

A free bot-traffic audit shows the human-automated split in your live traffic - no code changes, no commitment.

Get a free bot-traffic audit