---
title: storyblok-js-client
description: storyblok-js-client is the official JavaScript client for Storyblok’s Content Delivery API and Management API.
url: https://www.storyblok.com/docs/libraries/js/universal-api-client
---

# storyblok-js-client

[storyblok-js-client](https://github.com/storyblok/monoblok/tree/main/packages/js-client) is a universal JavaScript client for both of Storyblok’s APIs. A single `StoryblokClient` instance targets the [Content Delivery API](/docs/api/content-delivery/v2) when initialized with an access token, or the [Management API](/docs/api/management) when initialized with a personal access token. The client offers in-memory caching, automatic rate limit, and retry handling.

> [!TIP]
> For new projects, we recommend the dedicated modern clients instead of `storyblok-js-client`: [@storyblok/api-client](/docs/libraries/js/content-delivery-api-client) for the [Content Delivery API](/docs/api/content-delivery/v2) and [@storyblok/management-api-client](/docs/libraries/js/management-api-client) for the [Management API](/docs/api/management).

## Requirements

-   Node.js 18 or higher
-   The latest versions of Chrome, Edge, Firefox, and Safari

The client calls the native `fetch` API. In environments that lack it, import [isomorphic-fetch](https://github.com/matthew-andrews/isomorphic-fetch) at the entry point of the application to provide it:

```javascript
import "isomorphic-fetch";
```

Internet Explorer support is available in [version 4](https://github.com/storyblok/storyblok-js-client/tree/v4.5.8) of the package.

## Installation

```bash
npm install storyblok-js-client
```

## Usage

To read content, initialize the client with a preview or public access token from **Settings** → **Access Tokens**:

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

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

const { data } = await Storyblok.get("cdn/stories/home", {
  version: "draft",
});
```

To manage content, initialize the client with a [personal access token](/docs/concepts/access-tokens#personal-access-token) instead:

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

const Storyblok = new StoryblokClient({
  oauthToken: "YOUR_PERSONAL_ACCESS_TOKEN",
});

await Storyblok.post(`spaces/${spaceId}/stories`, {
  story: { name: "About", slug: "about" },
});
```

## API

The following sections document the `StoryblokClient` constructor and each method it exposes.

### `StoryblokClient()`

Creates a client instance.

```javascript
const Storyblok = new StoryblokClient(CONFIG, ENDPOINT);
```

`CONFIG` accepts the following options:

| Option | Description |
| --- | --- |
| `accessToken` | A preview or public access token. Required for Content Delivery API requests. |
| `oauthToken` | A [personal access token](/docs/concepts/access-tokens#personal-access-token). Required for Management API requests. |
| `region` | The space’s region: `eu` (default), `us`, `ap`, `ca`, or `cn`. Required for spaces outside the EU. |
| `cache` | Cache configuration object. Refer to [Caching](#caching). |
| `rateLimit` | A fixed rate limit in requests per second that overrides the automatic limits. Refer to [Rate limit](#rate-limit). |
| `maxRetries` | The number of retries after a `429` response. Defaults to `10`. |
| `retriesDelay` | The delay in milliseconds between retries. Defaults to `300`. |
| `timeout` | Request timeout in milliseconds. |
| `responseInterceptor` | A function that receives and returns the response. Refer to [Response interceptors](#response-interceptors). |
| `resolveNestedRelations` | Whether to resolve nested relations. Defaults to `true`. |
| `https` | Whether to call the API over HTTPS. |

`ENDPOINT` optionally overrides the API base URL.

### `get()`

Fetches a single entry or one page of entries. Listings return 25 entries per page by default. The [maximum](/docs/api/content-delivery/v2#pagination) is 100 entries per page for stories, and 1000 for the other endpoints.

```javascript
Storyblok.get(SLUG, PARAMS, FETCH_OPTIONS);
```

| Parameter | Description |
| --- | --- |
| `SLUG` | **Required.** The path to request, such as `cdn/stories`, `cdn/links`, `cdn/tags`, or `cdn/datasources`. |
| `PARAMS` | Query parameters. Refer to the [Content Delivery API](/docs/api/content-delivery/v2). |
| `FETCH_OPTIONS` | Fetch options. Refer to [Custom fetch options](#custom-fetch-options). |

```javascript
const { data } = await Storyblok.get("cdn/stories/home", {
  version: "draft",
});
```

### `getAll()`

Fetches every entry across all pages and returns them as a single array. The client pages through the endpoint internally, so a listing larger than the [page maximum](/docs/api/content-delivery/v2#pagination) needs no pagination code.

```javascript
Storyblok.getAll(SLUG, PARAMS, ENTITY, FETCH_OPTIONS);
```

| Parameter | Description |
| --- | --- |
| `SLUG` | **Required.** The path to request, such as `cdn/stories`, `cdn/links`, `cdn/tags`, or `cdn/datasources`. |
| `PARAMS` | **Required.** Query parameters. Refer to the [Content Delivery API](/docs/api/content-delivery/v2). |
| `ENTITY` | The entity to read from the response, such as `stories`, `links`, or `datasource_entries`. |
| `FETCH_OPTIONS` | Fetch options. Refer to [Custom fetch options](#custom-fetch-options). |

Unlike `get()`, this method resolves to the array of entries rather than to a response object:

```javascript
const stories = await Storyblok.getAll("cdn/stories", {
  version: "draft",
  starts_with: "articles/",
});
```

> [!WARNING]
> Each page is a separate API request, so fetching a large space consumes both [rate limit](#rate-limit) budget and [traffic](/docs/concepts/traffic). Narrow the result set with `starts_with`, `content_type`, or [filter queries](/docs/api/content-delivery/v2/filter-queries) where possible.

### `post()`, `put()`, `patch()`, and `delete()`

Write to the Management API. Each method requires a client initialized with an `oauthToken`.

```javascript
Storyblok.post(SLUG, PARAMS, FETCH_OPTIONS);
Storyblok.put(SLUG, PARAMS, FETCH_OPTIONS);
Storyblok.patch(SLUG, PARAMS, FETCH_OPTIONS);
Storyblok.delete(SLUG, PARAMS, FETCH_OPTIONS);
```

`SLUG` is a [Management API](/docs/api/management) path and `PARAMS` is the request body.

```javascript
await Storyblok.post(`spaces/${spaceId}/stories`, {
  story: { name: "About", slug: "about" },
});

await Storyblok.put(`spaces/${spaceId}/stories/${storyId}`, {
  story: { name: "About us", slug: "about-us" },
});

await Storyblok.delete(`spaces/${spaceId}/stories/${storyId}`);
```

### `flushCache()`

Empties the client’s cache and returns the client.

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

### `ejectInterceptor()`

Removes a previously configured response interceptor.

```javascript
Storyblok.ejectInterceptor();
```

## Caching

The client can cache responses in memory. Without a `clear` strategy, the cache persists until `flushCache()` runs.

```javascript
const Storyblok = new StoryblokClient({
  accessToken: "YOUR_SPACE_ACCESS_TOKEN",
  cache: {
    clear: "auto",
    type: "memory",
  },
});
```

| Option | Values | Description |
| --- | --- | --- |
| `type` | `none`, `memory`, `custom` | Where to cache responses. |
| `clear` | `manual`, `auto`, `onpreview` | When the cache is empty. Defaults to `manual`. `onpreview` clears only on draft requests. |
| `cv` | `auto`, `manual` | Whether the client tracks the content version. Defaults to `auto`. |

With `cv: 'auto'`, the client stores the `cv` value the Content Delivery API returns and appends it to subsequent published requests, which keeps responses served from the CDN cache. Set `cv: 'manual'` where an external system handles invalidation, or in server-side and edge caching setups where a tracked `cv` would prevent cache reuse. Learn more in the [caching concept](/docs/concepts/caching).

## Custom cache provider

To store the cache elsewhere (such as Redis), set `type: 'custom'` with four methods:

```javascript
new StoryblokClient({
  accessToken: "YOUR_SPACE_ACCESS_TOKEN",
  cache: {
    clear: "manual",
    type: "custom",
    custom: {
      get() {
        return Promise.resolve(0);
      },
      getAll() {
        return Promise.resolve(0);
      },
      set() {
        return Promise.resolve(0);
      },
      flush() {
        return Promise.resolve(0);
      },
    },
  },
});
```

## Rate limit

The client queues requests to stay within the rate limits of the [Content Delivery API](/docs/api/content-delivery/v2#rate-limits) and the [Management API](/docs/api/management#rate-limits), with no configuration required. It derives the rate from `per_page` and adjusts it to the rate limit headers the API returns.

Set `rateLimit` to apply one fixed rate to every request instead, up to a maximum of 1000 per second:

```javascript
const Storyblok = new StoryblokClient({
  accessToken: "YOUR_SPACE_ACCESS_TOKEN",
  rateLimit: 25,
});
```

A request answered from the in-memory cache skips the queue, since it never reaches the API.

## Retry

A `429` response triggers an automatic retry:

```javascript
const Storyblok = new StoryblokClient({
  accessToken: "YOUR_SPACE_ACCESS_TOKEN",
  maxRetries: 10,
  retriesDelay: 300,
});
```

## Custom fetch options

`get()`, `getAll()`, `post()`, `put()`, `patch()`, `delete()`, `getStory()`, and `getStories()` accept a final argument matching the Fetch API’s [RequestInit](https://developer.mozilla.org/en-US/docs/Web/API/RequestInit) object, minus `method`, which the client sets itself.

```javascript
const { data } = await Storyblok.get("cdn/stories/home", { version: "draft" }, { mode: "cors", cache: "no-cache" });
```

## Response interceptors

A response interceptor receives every response before the caller does. It must return the response.

```javascript
const Storyblok = new StoryblokClient({
  accessToken: "YOUR_SPACE_ACCESS_TOKEN",
  responseInterceptor: (response) => {
    if (response.status === 200) {
      // Handle the response
    }

    return response;
  },
});
```

Call [`ejectInterceptor()`](#ejectinterceptor) to remove it.

## Error handling

A failed request throws an object rather than a plain `Error`:

```typescript
interface ISbError {
  message: Error;
  status: number;
  response: ISbResponse;
}

interface ISbResponse {
  data: any;
  status: number;
  statusText: string;
  headers: any;
  config: any;
  request: any;
}
```

```javascript
try {
  const { data } = await Storyblok.get("cdn/stories/home", { version: "draft" });
} catch (error) {
  console.log(error.status, error.message);
}
```

## Relation resolving in the Bridge

`resolveRelations()` resolves referenced stories in the Storyblok Bridge’s `input` event, so live editing reflects referenced content. It resolves entries up to two levels deep.

```javascript
window.storyblok.on("input", (event) => {
  window.storyblok.addComments(event.story.content, event.story.id);
  window.storyblok.resolveRelations(event.story, ["post.author", "post.categories"], () => {});
});
```

> [!NOTE]
> The client and Storyblok’s framework SDKs inject resolved relations into the properties that reference them. Access a resolved reference, such as `post.author`, instead of reading the response’s `rels` array.

## Further resources

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

[Management API](/docs/api/management)

[Developer Concept: Caching](/docs/concepts/caching)

## Pagination

-   [Next: Content Delivery API Client](/docs/libraries/js/content-delivery-api-client)
