What is your developer-dependent CMS actually costing you? Calculate your costs and get a full report
What is your developer-dependent CMS actually costing you? Calculate your costs and get a full report
Architecture
Keep an Agility site serving when the API is slow, limited or down: prerendering, tagged caches cleared by webhooks, Agility's CDN, Content Sync, retries with back-off, and serving stale on error.
A well-built Agility site keeps serving visitors when the content API is slow, rate limited or briefly unavailable. This page explains the limits you design around and the layers that make a site resilient: static generation, your own cache, Agility's CDN, a local copy with Content Sync, and retries with back-off.
For every status code and its meaning, see Fetch API Status Codes and Caching.
| Limit or event | What happens | Source |
|---|---|---|
| More than 10 uncached requests in one second | 429 Too Many Requests | Content Fetch API |
| A request that takes longer than 30 seconds | 408 Request Timeout, cached for 30 seconds | Fetch API Status Codes and Caching |
| A response already on Agility's CDN | Served from the edge, and not counted towards the limit | Content Delivery & CDN Architecture |
| A platform incident | Posted at status.agilitycms.com, where you can subscribe to notifications | SLA & Uptime |
The limit applies to requests that reach the API itself. Most traffic problems come from many different uncached URLs at once: a full static build, a cache that was just cleared, or code that fetches on every page view.
Each layer reduces how often the next one is needed. Use as many as your site allows.
| Layer | What it protects against | How |
|---|---|---|
| 1. Prerendered pages | Visitors never wait on the API | Static generation or incremental regeneration |
| 2. Your data cache | Repeated reads of the same content | Cache every published read, tagged, for a long time |
| 3. Webhook invalidation | Stale content without short cache lifetimes | Clear only the tags that changed on publish |
| 4. Agility's CDN | Load on the API from repeated identical requests | Automatic. Identical URLs are served from the edge |
| 5. A local copy | API dependency at read time | Content Sync into your own store |
| 6. Retries with back-off | Short 429, 408 and network failures | Retry the right errors, with growing, randomized delays |
| 7. Serve stale on error | An API error replacing good content with nothing | Keep the last good copy when a refresh fails |
When pages are rendered at build time or regenerated in the background, a visitor's request doesn't call Agility at all. SLA & Uptime explains why this, together with edge caching, keeps published content available even during an origin event. On Next.js, see Rendering & Data Fetching with Next.js.
Wrap every CMS read in your framework's cache with a tag per item, list, page and sitemap, and give it a long lifetime. When content is published, an Agility webhook calls your endpoint, which clears just those tags. This turns most page renders into cache hits and keeps content fresh within seconds of publishing. Caching with Next.js and Agility has the full pattern.
Avoid short time-based lifetimes (for example 60 seconds) on a busy site: every expiry is another burst of uncached requests.
Identical requests are cheap, because the CDN answers them. Different requests are not. So:
take up to 250 on lists instead of many small pages, and contentLinkDepth (default 1, maximum 5) rather than one request per linked item.If your app must keep working without calling Agility at read time (offline kiosks, very high read volume, or a hard dependency you want to remove), sync content into your own store and read from there. Sync calls are the only API traffic, and a webhook or schedule keeps the copy current. See Content Sync Explained.
| Response | Retry? | How |
|---|---|---|
429 Too Many Requests | Yes | Wait, then retry with an exponential delay and random jitter |
408 Request Timeout | Yes, after at least 30 seconds | The 408 is cached for 30 seconds, so a quicker retry of the same URL gets it again. Ask for less data |
5xx, or a network error | Yes, a few times | Exponential delay with jitter |
400 Bad Request | No | Fix the request, usually the filter |
401 Unauthorized | No | Fix the key or its type |
404 Not Found | No | Treat as "not there" |
Cap the number of attempts, and give up quickly while a visitor is waiting. Retrying hard during a build is fine; retrying hard inside a page request makes the visitor wait.
@agility/content-fetch (2.0.11, checked 2026-10-03) does not retry, and it does not throw on an error response or a network failure. It logs the error and resolves to undefined. If you use it, check every result. An undefined that reaches your cache or your page is cached or rendered as empty content.
This wrapper calls the REST API directly, retries what is safe to retry, and throws when it gives up:
const RETRYABLE = new Set([408, 429, 500, 502, 503, 504])
export async function fetchAgility(url: string, apiKey: string, maxAttempts = 4) {
for (let attempt = 1; ; attempt++) {
let status = 0
try {
const res = await fetch(url, { headers: { APIKey: apiKey } })
if (res.ok) return res.json()
status = res.status
if (!RETRYABLE.has(status)) {
throw new Error(`Agility returned ${status} for ${url}`) // 400, 401, 404: don't retry
}
} catch (err) {
if (status && !RETRYABLE.has(status)) throw err
if (attempt >= maxAttempts) throw err
}
if (attempt >= maxAttempts) throw new Error(`Agility returned ${status} for ${url}`)
const base = status === 408 ? 30_000 : 500 * 2 ** (attempt - 1) // 0.5 s, 1 s, 2 s...
const delay = base + Math.random() * base // jitter spreads retries out
await new Promise((r) => setTimeout(r, delay))
}
}
Whether 429 responses carry a Retry-After header is not documented. If you see one, wait at least that long.
The goal of everything above: an error should never replace good content with nothing.
stale-while-revalidate and stale-if-error in Cache-Control; check its documentation.A static build of a large site is the most common source of 429 responses, because it renders many pages at once and each page makes several requests.
Publishing many items at once sends many webhooks. Acknowledge each one quickly, queue the work, and clear caches in batches. Agility collapses repeated identical events within a 30-second window, but don't rely on it. See Webhook Events and Payload Reference.
429, 408 and 5xx from Agility with the URL, so you can see which code path causes them.429, 408, 5xx and network errors retry with back-off and jitter; 400, 401 and 404 don't