---
title: Introduction
description: Learn the basics of the Storyblok Content Delivery API, including authentication, caching, CDN, pagination, rate limits, and error handling.
url: https://storyblok.com/docs/api/content-delivery/v2
---

# Introduction

The Storyblok Content Delivery API is organized around REST. It has predictable, resource-oriented URLs and uses HTTP response codes to indicate API errors. The API uses built-in HTTP features, such as HTTP query parameters and HTTP verbs, which are understood by off-the-shelf HTTP clients. Further, it supports cross-origin resource sharing, allowing for secure interaction with the API from a client-side web application. All API responses, including errors, return JSON.

The base URL for the Content Delivery API depends on the server location of the space. Below are the available endpoints for different regions:

-   European Union
    
    ```plaintext
    https://api.storyblok.com/v2/cdn
    ```
    
-   United States
    
    ```plaintext
    https://api-us.storyblok.com/v2/cdn
    ```
    
-   Canada
    
    ```plaintext
    https://api-ca.storyblok.com/v2/cdn
    ```
    
-   Australia
    
    ```plaintext
    https://api-ap.storyblok.com/v2/cdn
    ```
    
-   China
    
    ```plaintext
    https://app.storyblokchina.cn/v2/cdn
    ```

## Authentication

API requests must be authenticated by providing an API access token as a query parameter. Learn more in the [access tokens concept](/docs/concepts/access-tokens).

Consider the following example on how to fetch published stories:

-   cURL
    
    ```shellscript
    curl "https://api.storyblok.com/v2/cdn/stories\
    ?token=wANpEQEsMYGOwLxwXQ76Ggtt\
    &version=published"
    ```
    
-   JS
    
    ```javascript
    // storyblok-js-client@>=7, node@>=18
    import Storyblok from "storyblok-js-client";
    
    const storyblok = new Storyblok({
      accessToken: "krcV6QGxWORpYLUWt12xKQtt",
    });
    
    try {
      const response = await storyblok.get('cdn/stories', {
        "version": "published"
      })
      console.log({ response })
    } catch (error) {
      console.log(error)
    }
    ```
    
-   PHP
    
    ```php
    $client = new \Storyblok\Client('YOUR_STORYBLOK_SPACE_ACCESS_TOKEN');
    
    $client->getStories([
      "version" => "published"
    ])->getBody();
    ```
    
-   Java
    
    ```java
    HttpResponse<String> response = Unirest.get("https://api.storyblok.com/v2/cdn/stories?token=wANpEQEsMYGOwLxwXQ76Ggtt&version=published")
      .asString();
    ```
    
-   C#
    
    ```csharp
    var client = new RestClient("https://api.storyblok.com/v2/cdn/stories?token=wANpEQEsMYGOwLxwXQ76Ggtt&version=published");
    var request = new RestRequest(Method.GET);
    
    IRestResponse response = client.Execute(request);
    ```
    
-   Python
    
    ```python
    import requests
    
    url = "https://api.storyblok.com/v2/cdn/stories"
    
    querystring = {"token":"wANpEQEsMYGOwLxwXQ76Ggtt","version":"published"}
    
    payload = ""
    headers = {}
    
    response = requests.request("GET", url, data=payload, headers=headers, params=querystring)
    
    print(response.text)
    ```
    
-   Ruby
    
    ```ruby
    require 'storyblok'
    client = Storyblok::Client.new(token: 'YOUR_TOKEN')
    
    client.stories({:params => {
      "version" => "published"
    }})
    ```
    
-   Swift
    
    ```swift
    let storyblok = URLSession(storyblok: .cdn(accessToken: "wANpEQEsMYGOwLxwXQ76Ggtt"))
    var request = URLRequest(storyblok: storyblok, path: "stories")
    request.url!.append(queryItems: [
        URLQueryItem(name: "version", value: "published")
    ])
    let (data, _) = try await storyblok.data(for: request)
    print(try JSONSerialization.jsonObject(with: data))
    ```
    
-   Kotlin
    
    ```kotlin
    val client = HttpClient {
        install(Storyblok(CDN)) {
            accessToken = "wANpEQEsMYGOwLxwXQ76Ggtt"
        }
    }
    
    val response = client.get("stories") {
        url {
            parameters.append("version", "published")
        }
    }
    
    println(response.body<JsonElement>())
    ```

## Errors

The Content Delivery API uses HTTP response codes to indicate the success or failure of an API request.

In general, codes in the 2xx range indicate success. Codes in the 4xx range indicate a request that failed given the information provided (for example, a required parameter was omitted, a content entry was not published but the version requested was set to published, etc.). Codes in the 5xx range indicate an error with Storyblok’s servers (these are rare).

Some 4xx errors that could be handled programmatically (for example, if a story was not found) include an error message that briefly explains the error reported.

| Code | Status | Description |
| --- | --- | --- |
| `200` | OK  | Everything worked as expected. |
| `400` | Bad Request | The wrong format was sent (e.g., XML instead of JSON). |
| `401` | Unauthorized | No valid API key was provided. |
| `404` | Not Found | The requested resource doesn’t exist (e.g., due to not yet published content entries). |
| `422` | Unprocessable Entity | The request was unacceptable, often due to missing a required parameter. |
| `429` | Too Many Requests | Too many requests hit the API too quickly. We recommend an exponential backoff of your requests. |
| `500`, `502`, `503`, `504` | Server Errors | Something went wrong on Storyblok’s end (these errors are rare). |

## 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) powered by Amazon CloudFront.

[![AWS CloudFront edge server locations on a world map](https://a.storyblok.com/f/212319/1060x600/56e8a3daf6/cloudfront-network-map.png/m/720x0/)](https://a.storyblok.com/f/212319/1060x600/56e8a3daf6/cloudfront-network-map.png)

AWS CloudFront edge server locations on a world map

> [!TIP]
> Learn all about Storyblok’s caching mechanism and how to optimize your cache strategy in the [caching concept](/docs/concepts/caching).

## Rate limits

The Storyblok Content Delivery API is subject to rate limits to ensure fair usage and optimal performance. The following rate limits apply:

| Type of request | Rate limit |
| --- | --- |
| Cached requests from the CDN | `1000` per second |
| Single content entries | `50` per second |
| Listings with fewer than or equal to `25` entries | `50` per second |
| Listings with `25` to `50` entries | `15` per second |
| Listings with `50` to `75` entries | `10` per second |
| Listings with `75` to `100` entries | `6` per second |

The rate limit per second will be proportionately reduced when opting for more than `25` entries per request via the [pagination parameters](/docs/api/content-delivery/v2/getting-started/pagination).

To ensure compliance with the rate limits, consider the following practices:

-   **Throttling**: If a `429` status code is received, slow down requests by introducing delays to allow graceful recovery.
-   **Caching**: Use cached requests whenever possible, as they have higher rate limits. Learn more in the [caching concept](/docs/concepts/caching).
-   **Retrying**: Implement a retry strategy with exponential backoff for failed requests due to rate limit errors.
-   **Monitoring**: Set up monitoring and alerting to track API usage and detect rate limit breaches early.

## Pagination

To efficiently handle large datasets, the Content Delivery API supports pagination for all the stories, links, and datasource entries endpoints. This allows users to navigate through large datasets by retrieving a specific number of entries per page and requesting subsequent pages as needed.

Paginated endpoints accept two parameters:

-   `page`: An integer value representing the page number to retrieve. Increase this value to receive the next page of entries. Default is `1`.
-   `per_page`: The number of entries per page. Default is `25`. Maximum is `100` (`1000` for datasource entries).

Paginated API requests include two properties in the response headers:

-   `total`: Indicates the total number of items available across all pages.
-   `per_page`: Specifies the number of items per page as per your request.

These values can be leveraged to implement efficient pagination strategies.

> [!NOTE]
> Requesting a page number that is higher than the last available page number results in an empty array in the API response.

## Further resources

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

[Technical Limits](https://www.storyblok.com/pricing/technical-limits)

## Pagination

-   [Next: Retrieve a Single Story](/docs/api/content-delivery/v2/stories/retrieve-a-single-story)
