---
title: Caching
description: Storyblok caches API responses and assets through a global CDN. Learn how to use the cv parameter, TTL settings, and cache invalidation to optimize your project.
url: https://storyblok.com/docs/concepts/caching
---

# Caching

Caching is the process of storing data so that future requests for that data can be served faster. Instead of generating a response from scratch for every request, a cached copy is served, reducing the load on the server and speeding up response times. To deliver content and assets as fast as possible, Storyblok leverages a content delivery network ([CDN](/docs/api/content-delivery/v2#caching)) powered by Amazon CloudFront.

Storyblok's CDN caches two types of data at endpoints around the world, each for up to one year:

-   **Content**: Content Delivery API responses for requests of published content.
-   **Assets**: Images, videos, documents, and other static files, including image variations generated by the Image Service.

## Content caching

The CDN caches Content Delivery API responses, reducing latency and offloading traffic from the backend. Understanding how this cache works, and how to use it effectively, is essential for building performant websites and applications that stay within the API's rate limits.

### Rate limits

The Content Delivery API enforces rate limits on uncached requests. For example, the limit for single stories is 50 per second, whereas the limit for requests containing 75 to 100 stories is 6 per second. In contrast, cached requests from the CDN allow over 1000 requests per second. Find the [complete rate limit table in the Content Delivery API reference](/docs/api/content-delivery/v2#rate-limits). Maximizing cache usage is the most effective way to stay within these limits.

### Cache version

To ensure a high cache hit rate for published content, Storyblok uses the `cv` parameter. Every response from the Content Delivery API contains such a `cv` property. The `cv` is a space-specific numeric counter (a Unix timestamp) that increments with each content change within the space. It serves as a cache key: requests that include a `cv` parameter are served directly from the CDN.

Under certain conditions, the API automatically redirects requests without a `cv` to the stories endpoint with the latest `cv` value:

-   When the request is made without a `cv` parameter
-   When the request is made with an outdated `cv` value and that version has never been requested before (i.e., it has never been cached in the CDN)
-   When the request is made with an invalid `cv` value

```bash
GET https://api.storyblok.com/v2/cdn/stories?token=[PUBLIC_ACCESS_TOKEN]
# 301 Redirect
GET https://api.storyblok.com/v2/cdn/stories?cv=1735645795&token=[PUBLIC_ACCESS_TOKEN]
```

If this exact request is made for the first time, the response is then cached in the CDN. Any subsequent request with the same `cv` value is served directly from the cache without hitting the backend.

### Retrieve the latest cache version

While the stories, datasources, and datasource entries endpoints of the Content Delivery API return the current `cv` value, the most efficient way to retrieve it is via the spaces endpoint:

```bash
GET https://api.storyblok.com/v2/cdn/spaces/me?token=[PUBLIC_ACCESS_TOKEN]
```

```json
{
  "space": {
    "id": 123456,
    "name": "Storyblok",
    "domain": "https://www.storyblok.com/",
    "version": 1544117388
  }
}
```

The `version` property in the response is the latest `cv` value. Use it as the `cv` query parameter in all subsequent API calls to maximize cache usage.

### Cache invalidation

When new content is published, the `version` value updates, but previous responses remain cached under the old `cv`. To retrieve up-to-date content, fetch the updated `version` and use it as the new `cv` parameter.

Fetching, storing, and invalidating the `cv` parameter should be part of your application architecture. Use cron jobs, custom events, or Storyblok's webhooks to detect when to retrieve a new `cv`.

## Asset caching

The CDN caches assets uploaded to Storyblok, as well as optimized image variations generated by the Image Service. This caching mechanism ensures that assets are delivered quickly to users. Assets are first served from the origin, and subsequent requests are cached on the CDN to minimize latency. Refer to the [assets concept](/docs/concepts/assets) for further information.

### Image Service cache

The CDN also caches image variations created on demand using the Image Service. Once a particular variation has been requested for the first time, it is cached. Refer to the [Image Service documentation](/docs/api/image-service) for further information.

> [!TIP]
> The cache mechanism takes into account the `Accept` header of the request to automatically determine WebP support, unless a specific format is requested using the [format filter](/docs/api/image-service/operations/format). The Image Service always serves the cached version in the correct format if it already exists. Otherwise, it is generated immediately.

### Cache invalidation

An asset's cache does not need to be cleared unless it is replaced or changed (for example, using Storyblok's [Image Editor](/docs/manuals/image-editor) or the [Replace Assets app](https://www.storyblok.com/apps/replace_asset)). In these scenarios, the cache is automatically invalidated. The new version is served on the next request, and cached hereafter.

## Optimization strategies

The following strategies help optimize API usage and cache performance. Each strategy addresses a different aspect of caching, and combining several of them yields the best results.

### Essentials

#### Always specify the cache version

First and foremost, always include the `cv` in API requests. This is the single most effective optimization.

### Content freshness

#### Webhook-based cache invalidation

Use Storyblok's webhooks to detect content changes and trigger cache invalidation in your application. For example, trigger a webhook whenever a story is published, updated, moved, or deleted to update the `cv` to the most up-to-date value. Refer to the [webhooks developer concept](/docs/concepts/webhooks) for further information.

#### Time to live (TTL) for the cache version

Setting a time to live (TTL) is most effective for high-traffic projects that send many requests to the Content Delivery API and update content frequently. It reduces uncached API calls and lowers response times. However, it introduces a trade-off: content updates are not reflected until the TTL expires and a new change occurs.

By default, the `cv` value changes with every content update, which means previously cached responses become stale immediately. The TTL setting for public access tokens overrides this behavior by making the `cv` persist for a defined duration. Refer to the [access tokens developer concept](/docs/concepts/access-tokens) for further information.

Setting a TTL value (in seconds) on a public access token freezes the `cv` for that duration. During the TTL window, all API requests using that token receive the same `cv`. Therefore, all responses are served from the cache.

After the TTL window has expired, the `cv` is not updated automatically. The next content change triggers a new `cv`. The first API request after that change retrieves fresh content and creates a new cache entry.

For example, a TTL of 300 seconds (five minutes) means:

1.  The first request retrieves content and caches it with the current `cv`.
2.  For the next five minutes, all requests receive the cached response, with the exact same content — even if content changes in the meantime.
3.  After five minutes, the next content change generates a new `cv`, and the first subsequent request creates a fresh cache.

> [!TIP]
> If both server-side and client-side API calls are made in your project, a shared cache, using the same token, can be beneficial. To achieve that, retrieve the `cv` server-side and expose it to the client via a dedicated endpoint.

### Architecture considerations

#### Framework rendering mode

Consider that the rendering mode of your application determines how and when API requests are made, which directly affects caching behavior:

-   **Client-side rendering (CSR) and server-side rendering (SSR)**: The application fetches data for every user request. Implement a caching strategy that reduces the number of requests the backend processes. Consider how often your content changes to determine cache invalidation frequency.
-   **Static site generation (SSG)**: During the build process, all content is fetched at once, which can be aggressive on rate limits. To avoid rate limit issues, using the [multiple stories endpoint](/docs/api/content-delivery/v2/stories/retrieve-multiple-stories) rather than the [single story endpoint](/docs/api/content-delivery/v2/stories/retrieve-a-single-story) is preferable. Consider that some static builds are executed with multiple workers, which may impact the benefits of the `cv`.

#### Story version caching

As explained further under [cache version](/docs/concepts/caching#cache-version), the `cv` is space-specific rather than story-specific.

[Developer Tutorial: Story Version Caching](/tp/story-version-caching) Learn how to leverage the cache version to implement a robust, story-specific caching strategy.

### Monitoring and error handling

#### Monitor API usage

Track request volume and rate limit status to detect potential issues before they affect your application. Use the space dashboard to monitor consumption metrics. Refer to the [space dashboard user manual](/docs/manuals/space-dashboard#consumption-metrics) for further information. Additionally, in your application, log rate limit errors.

#### Handle rate limit errors

With a solid caching strategy in place, operating within Storyblok's rate limits should be the norm. Nevertheless, it is advisable to implement retry logic with exponential backoff to handle HTTP `429` responses gracefully.

### Infrastructure

#### Custom assets domain

For greater control over asset caching, consider setting up a custom assets domain. This allows to defining custom cache headers and TTL policies, and leveraging any existing CDN infrastructure. Refer to the [custom assets domain documentation](/docs/concepts/assets#custom-assets-domain) for setup guides.

#### Additional cache layers

Beyond Storyblok's CDN, consider implementing cache layers in your own application. Browser caching reduces redundant network requests for returning visitors. Application-level caching — for example, storing API responses in memory or in a key-value store — reduces the number of requests to Storyblok's API during server-side rendering or build processes. Set cache durations based on how often your content changes and use webhooks to invalidate stale entries.

## Storyblok SDKs

### JavaScript client

Storyblok's JavaScript-based framework SDKs use the JavaScript client, which handles the `cv` parameter automatically. When the application makes its first API call to the `/stories/` endpoint, the client stores the `version` number from the response in memory and reuses it for all subsequent calls.

Override the cached `cv` by passing a custom value in an API request:

```javascript
import StoryblokClient from "storyblok-js-client";

const Storyblok = new StoryblokClient({
  accessToken: "PUBLIC_ACCESS_TOKEN",
});

const response = await Storyblok.getStory("home", {
  version: "published",
  cv: "1735815318",
});
```

The client caches this value. Subsequent calls without a custom `cv` parameter reuse it:

```javascript
// Results in GET /stories/home?cv=1735815318&version=published&token=[YOUR_TOKEN]
const response = await Storyblok.getStory("home", {
  version: "published",
});
```

Call the `flushCache()` method to clear the client's in-memory cache:

```javascript
await Storyblok.flushCache();
```

Passing a `cv` parameter equal to the current timestamp bypasses the CDN cache entirely, because the requested version is always newer than the latest cached version. Note that this forces every request to hit the backend and count against the rate limit.

```javascript
const response = await Storyblok.getStory("home", {
  version: "published",
  cv: Date.now(),
});
```

Alternatively, calling the `/stories/` endpoint with `version: "draft"` flushes the client cache. Use this approach only in preview or development environments.

> [!NOTE]
> In some server-rendered applications (such as Next.js), the client instance may persist until the server restarts. Use webhooks or custom logic to revalidate the cache as needed.

## Further resources

[Developer Tutorial: Story Version Caching](/tp/story-version-caching)

[Content Delivery API Reference: Introduction](/docs/api/content-delivery/v2)

[Developer Concept: Assets](/docs/concepts/assets)

## Pagination

-   [Previous: Blueprints](/docs/concepts/blueprints)
-   [Next: CMS Migration](/docs/concepts/cms-migration)
