This is a preview of the Storyblok Website with Draft Content

JoyConf 2026 is back. Content Confidence. Human Connection. Save your spot!

The Latest API Clients Deliver More Speed with Fewer Errors

Storyblok is the first headless CMS that works for developers & marketers alike.

In version 0.9.1, both @storyblok/api-client and @storyblok/management-api-client deliver up to three times faster story fetches (up to 20 times faster in extreme cases) and make aborting on rate limit errors a thing of the past.

The Content Delivery and Management API clients offer improved rate limit handling natively and feature a new API for custom rate-limiting logic. The updates that unlock this performance boost are entirely under the hood with no breaking changes.

Update your dependencies now and keep reading to learn how we did it.

In numbers

The new client version can be up to three times faster, depending on circumstances. The data below represents fetching 3,818 stories from Storyblok’s website (warm cache):


Total

Single-story rate

Before

84.3s

50 requests/s

After

27.3s

338 requests/s peak

To maximize the rate limit, the new client can adjust the request rate rather than setting a fixed 50 requests per second.

Similar improvements are available when running whole fleets of clients, with better rate-limiting logic that results in far fewer responses with status code 429 (Too Many Requests):

Workers

Rate limiter

Progress

429s

Time

4

Old

56.4%

9,928

48.1s

4

New default

100%

272

92.6s

The above table illustrates how four parallel API clients perform with the new default rate-limiting logic. The old clients aborted after receiving too many 429 errors (over 9,000), while the fleet of new clients kept working, causing far fewer errors.

Hint:

Only a large volume of HTTP responses with status 429 causes an API client to give up and raise a rate limit error. Occasional 429s are gracefully handled under the hood.

Using custom rate-limiting logic can lead to even greater gains:

Workers

Rate limiter

Progress

429s

Time

4

New default

100%

272

92.6s

4

Custom, Redis-based

100%

71

80.2s

These improvements result from the clients’ new cache-handling logic while offering better coordination of request rates when part of a fleet.

Dynamic, cache-aware request ceilings

The Content Delivery API has tiered rate limits:

  • Single content entries: 50 per second
  • Listing requests with fewer than or equal to 25 entries: 50 per second
  • Listings with 25-50 entries: 15 per second
  • Listings with 50-75 entries: 10 per second
  • Listings with 75-100 entries: 6 per second

The limits only apply to requests that reach the origin. Any request that hits the CDN cache doesn’t count toward the tiered rate limits, but @storyblok/api-client previously didn’t account for this fact. It instead paced itself against the tiered limits to avoid overrunning them, treating all requests as counting towards the limits. This approach established a safe upper bound for the request rate but left a lot on the table.

Now, the API client accounts for the cache status of requested resources. It keeps a rolling window of the last 50 responses with an identifiable cache status via X-Cache header and uses a dynamic ceiling of tierLimit / (1 - hitShare). This yields a theoretical rate of up to 1,000 requests per second, which is the general rate limit for cached CDN responses.

As a result, a warm cache can handle much higher throughput, while cold and mixed workloads remain the same.

This improvement only applies to the Content Delivery API—the Management API is uncached and therefore subject to the same rate limits and behaviors as before.

Adaptive custom rate limit logic

The rate limit applies per API access token, which therefore acts as a shared resource if you run multiple clients in parallel. A whole fleet of clients can quickly overrun the API’s rate limit, even if every instance tries to pace itself.

There are two ways to handle this use case. The default approach works like TCP congestion control. When a client receives a response with a status code 429, it divides the request rate and then generally increases it. This forces multiple clients to share the rate limit.

To operate multiple clients consistently at the edge of the shared token’s rate limits, set a fleet-wide dynamic ceiling with custom rate-limiting logic. The following snippet is a minimal example that uses rateLimit.limiter to configure Redis as an external, shared limiting mechanism:

import { createApiClient } from "@storyblok/api-client";
import { Redis } from "ioredis";

const redis = new Redis(process.env.REDIS_URL);
redis.defineCommand("reserveSlot", {
  /* ... */
});

const fleetClient = createApiClient({
  accessToken,
  rateLimit: {
    limiter: {
      // bucket = rate-limit tier, limit = that tier's req/s
      async acquire({ bucket, limit }) {
        const waitMs = await redis.reserveSlot(
          `storyblok-ratelimit:${bucket}`,
          1000 / limit,
        );
        if (waitMs > 0) {
          await new Promise((r) => setTimeout(r, waitMs));
        }
      },
    },
  },
});

This setup ensures that every client is aware of the fleet’s overall limit and can pace themselves without first having to encounter a 429.

Upgrade now

The new clients introduce no breaking changes—upgrade now and get a risk-free speedup.

If you come across any bugs or have ideas for new features, open an issue on GitHub and let us know. Join our Discord server to tell us what you think about the new API client and to explore other Storyblok topics with members of our community.