Rate Limits
10 min
tl;dr every client ip address may send up to 1,000 requests per minute to the graphql endpoints above that, requests are rejected with http 429 and a graphql style json error until the rate drops below the limit again do not retry rejected requests immediately back off, spread out your traffic, and cache what you can the catalog api and the purple publish api are shared by all purple customers a rate limit protects the platform from runaway integrations, misconfigured retry loops and abusive traffic, so that one client cannot degrade the service for everyone else the limit is generous for normal app, website and integration traffic well behaved clients will never see it what is limited limit 1,000 requests per minute counted per client ip address (the public ip the request arrives from) window rolling 1 minute window applies to all http requests to a /graphql path on the hosts below on exceed http 429 too many requests with a json error body affected endpoints https //catalog purplemanager com/graphql https //api purplepublish com/graphql what counts as a request every http request that reaches one of the endpoints above counts towards the limit, independent of the query it carries graphql requests, including introspection and queries sent from the graphiql editor requests that are answered from the cdn cache cors preflight (options) requests sent by browsers requests that fail, for example with a validation error or an invalid api key the number of fields or operations inside a request does not matter one request containing three queries counts once three separate requests count three times what is not limited downloading content resources such as images, pdfs or issue packages from the cdn graphql subscriptions over websocket (/subscriptions) the 429 response when a client ip exceeds the limit, further requests from that ip are rejected the response uses http status 429 too many requests and carries a body in the same shape as a graphql error response, so existing graphql error handling can pick it up { "errors" \[ { "message" "rate limit exceeded please retry later ", "extensions" { "code" "rate limited" } } ] } rejections continue until the request rate of that ip falls below 1,000 requests per minute again, which usually takes well under a minute once the client slows down the response currently does not include a retry after header production rollout enforcement on production begins on \[date – to be announced] until then, requests above the limit are counted but not rejected how your client should react check for http status 429 or for an error with extensions code equal to rate limited, then stop sending immediately do not fire the same request again in a tight loop every retry counts and prolongs the block back off exponentially with jitter wait about 1 second before the first retry, then 2, 4, 8 seconds and so on, each with a random offset stop after a small number of attempts and surface the error treat it as a signal to slow down globally when one request is rejected, all concurrent requests from the same ip are affected pause your request queue instead of letting every worker retry on its own handle other failures the same way apply the same backoff to timeouts and 5xx responses aggressive retries during an incident make the incident worse for everyone, including you a minimal example in javascript async function catalogquery(body, attempt = 0) { const response = await fetch("https //catalog purplemanager com/graphql", { method "post", headers { "content type" "application/json", "user agent" "acme website/2 3 1" }, body json stringify(body) }); if (response status === 429 || response status >= 500) { if (attempt >= 4) throw new error(`catalog api unavailable (${response status})`); const delay = 1000 2 attempt + math random() 500; await new promise(resolve => settimeout(resolve, delay)); return catalogquery(body, attempt + 1); } return response json(); } staying well below the limit 1,000 requests per minute is roughly 16 requests per second from a single ip typical apps and websites stay far below that server side integrations are the ones that need attention combine queries graphql lets you request several connections in one request a page that needs a post, its related posts and the menu should send one request, not three paginate deliberately pages are limited to 200 entries fetching what you need with first 200 uses far fewer requests than walking through the result set 20 entries at a time cache on your side server side rendering is the most common source of excessive traffic cache rendered pages or the underlying query results, and use the content's lastmodified timestamps to decide when to refresh a cache miss should trigger one catalog api request, not one per component watch for crawlers on your own site bots and crawlers visiting your website trigger your server side rendering, which in turn calls the catalog api a crawler working through thousands of long tail urls can multiply your request rate overnight serve them from your cache, and make sure redirect chains and link loops on your side cannot cause the same page to be rendered repeatedly spread out bulk work full re indexing, sitemap generation and similar jobs should run at a steady, throttled pace rather than as a burst if a job needs 50,000 requests, running it over an hour instead of five minutes keeps it well under the limit mind shared egress ips the limit is per public ip if your website, your backend services and your import jobs all leave your network through the same nat gateway or proxy, they share one budget of 1,000 requests per minute send a descriptive user agent include your product and version, for example acme website/2 3 1 it lets us attribute traffic to your integration and contact you about unusual patterns before they become a problem need a higher limit? talk to us first if your integration legitimately needs more than 1,000 requests per minute from a single ip, contact purple support before you go live please include the affected endpoint, the public ip addresses you send from, your user agent, and the peak request rate you expect we will review the pattern with you and, where appropriate, look for ways to reduce the request volume or adjust the limit