- GraphPilot
- Automatic: a mutation purges the cached responses that contained the objects it returned. A create reaches a cached list through a key the list carries and the create's response names.
- Automatic invalidation ↗
- Stellate
- Automatic, with three policies: Entity, List and Type.
- Stellate docs, invalidation methods ↗
GraphPilot and Stellate compared
Both are managed edge caches for GraphQL APIs on Fastly's network. This page sets out what each one states about itself, with a source for every statement, and where they differ.
- Stellate facts read on 2026-11-06
- A source for every statement
- Out of date? Tell us
GraphPilot
GraphPilot is a managed edge cache for GraphQL and plain HTTP APIs that purges cached responses automatically when a GraphQL mutation returns an object they contain.
Source: GraphPilot documentationStellate
Stellate describes itself as a platform to "Secure, Scale, and Optimize Your GraphQL APIs at the Edge", with GraphQL edge caching, metrics and security. Its acquisition post of 2024-09-10 states that The Guild is acquiring the product, will continue running Stellate's GraphQL CDN for its customers, and will gradually integrate it into the GraphQL Hive platform.
Source: Stellate blog, "Stellate has been acquired!" (2024-09-10), read 2026-11-06At a glance
The differences people ask about first. The numbers link to each statement's source; every card also links to its full row.
When a query doesn't ask for id · 0:40 A query that does not select id
Caching that reads your schema · 1:12 Where the keys live
The empty list · 1:02 An empty list
Know which app is calling · 0:39 Identifying client apps
GraphQL and REST, one cache · 0:51 Plain HTTP next to GraphQL
Rules in one line · 0:45 Splitting the cache by caller
Purge speed
- GraphPilot
- Fastly's purge network: 150 ms on average, as Fastly states. [14]
- Stellate
- At most 150 ms, as Stellate states. [15]
Security attestation
Details and sources ↓Each problem in a short clip, with a transcript: all caching problems.
In detail
Every difference with both sides' sources. Stellate statements as read on 2026-11-06.
Invalidation and keys
How a change in your data reaches the cached responses that contain it.
- GraphPilot
- In the schema, with @surrogateKey. Fields of a key scalar (ID) and fields named id, _id or key count by default; both rules are configurable.
- Directives ↗
- Stellate
- "By default, Stellate looks for id, _id and key fields on your types." Further key fields are set with keyFields in the Stellate configuration file.
- Stellate docs, key fields ↗
- GraphPilot
- GraphPilot adds the missing key fields to the request it sends to the origin, tags the cached response with them, and removes them before the client sees the response.
- Key fields the query did not select ↗
- Stellate
- Its docs describe tagging a response with the key fields the query requests ("Any query that fetches an entity of type Launch and also requests the launch_name key field is then also tagged") and adding __typename to the query sent to the origin. The gcdn-missing-id-paths header lists "Entities included in the response that are missing a way to identify them uniquely, e.g. because the query doesn't ask for id or another configured key field." Adding missing key fields is not described in the docs as read on 2026-11-06.
- Stellate docs, Stellate headers ↗ Stellate docs, response tagging ↗ Stellate docs, switching to the new infrastructure ↗
- GraphPilot
- Cached, and tagged with the list's type, so a mutation that returns an object of that type, such as creating the first item, clears the cached empty answer.
- Why the empty-list key exists ↗
- Stellate
- "We currently do not cache empty responses." The stated reason: responses are tagged with __typename, which an empty response does not have.
- Stellate docs, limitations ↗
- GraphPilot
- Not cached (CACHE_SKIPPED_SURROGATE_KEYS_TOO_LARGE). The keys of one response may take 16,000 bytes, about 1,300 to 2,000 keys.
- How many keys fit on one response ↗
- Stellate
- The gcdn-surrogate-skipped-keys header lists "Surrogate keys applied to the cached document which did not fit within the gcdn-surrogate-key header (because of header length restrictions)."
- Stellate docs, Stellate headers ↗
- GraphPilot
- A purge API and gpilot purge: by surrogate key, cache key, request id, query hash, or everything; hard or soft.
- The purge API ↗
- Stellate
- A purging API generated as a GraphQL API for each service: by type (optionally narrowed by key fields), operation name, query, query field, scope, or everything; soft with soft: true.
- Stellate docs, purging API ↗
- GraphPilot
- A mutation purges straight from the edge service, over Fastly's purge network, which Fastly states clears content globally in 150 ms on average. A purge through the API first passes the GraphPilot API. Allow a few seconds before relying on every location.
- Purging is global ↗
Caching
What is cached, who shares an entry, and how you see why something was not cached.
- GraphPilot
- GraphQL APIs and plain HTTP APIs, with a feature matrix of what applies to which.
- Features and limitations ↗
- Stellate
- GraphQL APIs. Its cache statuses page states that "non-GraphQL requests can never be cached by Stellate."
- Stellate docs, cache statuses ↗
- GraphPilot
- The @cacheControl directive in the schema, or a Cache-Control header from the origin. The schema and gpilot.toml are deployed together with gpilot deploy.
- How long an entry lives ↗
- Stellate
- Cache rules, and the stellate_cache directive in the GraphQL schema.
- Stellate docs, cache directives ↗
- GraphPilot
- A private policy that names what identifies a caller, named scopes such as ORGANIZATION on schema types, and vary rules on verified claims, headers, cookies or URL parameters.
- Who shares an entry ↗
- Stellate
- Scopes that split the cache on headers, cookies, or both, or on a claim of a verified JWT.
- Stellate docs, scopes ↗
- GraphPilot
- Policy expressions (Radiogram), an expression language that reads verified JWT claims next to headers, cookies and URL parameters, joins them, and normalizes them with functions (lower, upper, split, membership, version comparisons, pseudonymize); the deployment warns about a negation a request could leave undecided. On every plan, Glider (Free) included.
- Policy expressions ↗
- Stellate
- A JWT scope names the claim to scope by (claim: 'sub'). Dynamic scopes run a function from the configuration file over the request's headers and cookies; a JWT read there is checked in the function: "you need to validate it manually inside your function". "This feature is not available on the free plan."
- Stellate docs, scopes ↗
- Stellate
- Partial Query Caching splits a query into separate entries based on the cache rules for its types and fields, so that "only the non-cacheable parts will be sent to your origin server" on later requests.
- Stellate docs, partial query caching ↗
- GraphPilot
- Every response carries gp-cache, and every PASS a gp-cache-reason code; the Cockpit shows the same reason on each request.
- Why a response was not cached ↗
- Stellate
- Cache statuses with PASS reasons such as HAS_SET_COOKIE and NO_CACHEABLE_DATA.
- Stellate docs, cache statuses ↗
- GraphPilot
- Every request, unless telemetry.record, a policy expression, narrows it (leaving out health probes, for example) or turns it off; a request left out is still served and billed. The client IP address is kept in full, truncated or not at all.
- Recording only some requests ↗
- Stellate
- Query metadata is anonymized: "we do not store variables", and hard-coded arguments are turned into variables before a query is stored.
- Stellate docs, query anonymization ↗
Security
Verifying callers and protecting the origin.
- Stellate
- For JWT scopes: "We do not support JSON Web Key Sets (JWKS) at this time. Your configuration will need to refer to the actual public key and not a JWKS URL".
- Stellate docs, scopes ↗
- GraphPilot
- Client profiles: a policy expression on a header, a verified claim or the User-Agent names the client and reads its version. Each client can get its own limits, a minimum version below which it is refused at the edge, or no caching.
- Client profiles ↗
- Stellate
- Stellate documents this for metrics with two headers: "Stellate infers the client from headers that you should include in every request: x-graphql-client-name, x-graphql-client-version." For rate limiting, its product page describes "Different limits for different consumers", with consumers identified "by any part of the request".
- Stellate docs, client tracking ↗ Stellate, GraphQL Rate Limiting ↗
- GraphPilot
- Automatic persisted queries, a registry pushed from a client manifest, and a mode that refuses any operation the registry does not hold.
- Persisted queries ↗
- Stellate
- Persisted operations, saved on the server and sent by reference, and automatic persisted queries.
- Stellate docs, persisted operations ↗
- GraphPilot
- Not yet. There is no limit on requests in flight or requests per second; the edge limits query depth, selections, document size and body size instead.
- Limits ↗
- Stellate
- Stellate's docs and product page describe Rate Limiting (request-based, and complexity-based "Currently in Alpha"), and its current pricing page still lists it ("Rate Limiting + $5" per 1M requests on the Business plan), as read on 2026-11-06. Earlier, the acquisition post of 2024-09-10 announced: "The Developer Portal and Rate Limiting products will be sunset in two months." The pages read do not say whether that plan was carried out.
- Stellate docs, Rate Limiting quickstart ↗ Stellate pricing ↗ Stellate, GraphQL Rate Limiting ↗ Stellate blog, "Stellate has been acquired!" (2024-09-10) ↗
Network, plans and company
Where it runs, what it costs to start, and who runs it.
- Stellate
- Fastly's network, "roughly 100 (and increasing) worldwide edge locations".
- Stellate docs, edge locations ↗
- GraphPilot
- Glider (Free): 100,000 request units a month. A request unit is counted per started 10 KiB of response body at each point of presence, so it is not the same unit as a request.
- GraphPilot pricing ↗
Plan scope and billing models differ (GraphPilot: request units and recorded requests, in EUR; Stellate: requests per product, in US dollars), so these prices are not a like-for-like comparison.
- GraphPilot
- Jet (Starter): 149 EUR a month, net, billed monthly, with 500,000 request units and 10M recorded requests (Insights) a month included; above that, 6 EUR per started 1M request units and 12 EUR per started 1M recorded requests.
- GraphPilot pricing ↗
- Stellate
- Business: "Starts at $249 / month", "25M metrics requests included / mo", "Additional requests: $10 / mo per 1M requests"; edge caching and rate limiting are billed per 1M requests on top ("+ $5" each).
- Stellate pricing ↗
Plan scope and billing models differ (GraphPilot: request units and recorded requests, in EUR; Stellate: requests per product, in US dollars), so these prices are not a like-for-like comparison.
- Stellate
- The Guild. The acquisition post of 2024-09-10 set the handover for 2024-09-24; the stellate.co footer reads "© 2026 The Guild".
- Stellate blog, "Stellate has been acquired!" (2024-09-10) ↗
What GraphPilot does not do
Stated up front, so you don't have to find out later.
- No partial query caching yet: a response is cached, and purged, as a whole. Source: Features and limitations
- No rate limiting and no query cost scoring yet. Source: Limits
- No conditional revalidation: no-cache is refused rather than stored and revalidated, and a background refresh is a full request. Source: Features and limitations
- No SOC 2 or comparable report is published. Source: Data processing agreement
- Query text is hashed as sent, not normalized, so the same operation written two ways is cached twice. Source: Coming from another CDN
Moving from Stellate to GraphPilot
The concepts map as follows. The full checklist for moving traffic is in Coming from another CDN.
Frequently asked questions
- Do my queries have to select id for invalidation to work?
- No. When a query does not select the key fields of an object, GraphPilot adds them to the request it sends to the origin, tags the cached response with them and removes them before the client sees the response.
- Is GraphPilot a drop-in replacement for Stellate?
- No configuration carries over as is. Cache lifetimes move to @cacheControl or Cache-Control headers, scopes to GraphPilot's private policy, named scopes or vary rules, and clients point at the GraphPilot service URL. Partial query caching has no equivalent yet.
- Does GraphPilot purge cached responses after a mutation?
- Yes. A mutation purges the cached responses that contained the objects it returned. To make a created item appear in a cached list, the list carries a key that the create's response names.
- Do GraphPilot and Stellate run on the same edge network?
- Both state that they run on Fastly's edge network.
- Can GraphPilot be tried without paying?
- Yes. Glider (Free) needs no credit card. It is meant for trying GraphPilot out, development, testing and small projects, not for production.
- Does GraphPilot support rate limiting?
- Not yet. Today it refuses GraphQL operations over its depth, selection, document size and body size limits before they reach the origin, but it does not limit how many requests a client sends.
Sources
Stellate statements come from Stellate's own pages, read on 2026-11-06. GraphPilot statements come from the GraphPilot documentation and this site.
- stellate.co
- Stellate pricing
- Stellate blog, "Stellate has been acquired!" (2024-09-10)
- Stellate blog, SOC 2 Type 2 (2024-04-04)
- Stellate, GraphQL Rate Limiting
- Stellate docs, Rate Limiting quickstart
- Stellate docs, edge locations
- Stellate docs, cache directives
- Stellate docs, invalidation methods
- Stellate docs, purging API
- Stellate docs, scopes
- Stellate docs, key fields
- Stellate docs, response tagging
- Stellate docs, switching to the new infrastructure
- Stellate docs, Stellate headers
- Stellate docs, limitations
- Stellate docs, client tracking
- Stellate docs, query anonymization
- Stellate docs, cache statuses
- Stellate docs, partial query caching
- Stellate docs, persisted operations
Cleared for takeoff
Start on Glider (Free).
No credit card, no commitment.
Prefer to talk first? Book a call