# How to Integrate Replit with Kentico

- Tool: Replit
- Difficulty: Intermediate
- Time required: 35 minutes
- Last updated: March 2026

## TL;DR

To integrate Replit with Kentico (Kontent.ai), obtain your Delivery API key and Management API key from the Kontent.ai dashboard, store them in Replit Secrets (lock icon 🔒), and use your Python or Node.js backend to fetch content items via the Delivery API or create/update content via the Management API. Deploy on Autoscale for on-demand content delivery.

## Why Connect Replit to Kontent.ai?

Kontent.ai (formerly Kentico Kontent) is the enterprise headless CMS arm of the Kentico ecosystem, used by large organizations to manage structured content across multiple channels — web, mobile apps, digital displays, and third-party platforms. Unlike traditional CMS platforms, Kontent.ai stores content as structured data (content types with defined elements) and delivers it as JSON via REST APIs, making it ideal for headless architectures where content is rendered by custom frontends.

The Kontent.ai API has two primary surfaces: the Delivery API for reading published content (fast, cached, optimized for high-traffic reads), and the Management API for creating, updating, and publishing content programmatically. A Replit backend can serve as a custom frontend that renders Kontent.ai content, a content migration tool that programmatically imports content from other systems, or a workflow automation layer that triggers content updates based on external events.

Replit's Secrets system (lock icon 🔒 in the sidebar) is where you store your Project ID and API keys. The Management API key in particular has write access to all content in your Kontent.ai project — treat it as a sensitive credential. The Delivery API key is slightly less sensitive (read-only), but should still be stored in Secrets to prevent unauthorized access to private content in secured environments.

## Before you start

- A Replit account with a Python or Node.js project created
- A Kontent.ai account (free trial available at https://kontent.ai)
- A Kontent.ai project with at least one content type defined
- Basic understanding of headless CMS concepts and REST API JSON responses
- Python 3.10+ or Node.js 18+ (both available on Replit by default)

## Step-by-step guide

### 1. Get Your Kontent.ai Project ID and API Keys

Log in to https://app.kontent.ai and navigate to your project. In the left sidebar, go to Project Settings → API keys. On this page you will find:

1. Project ID — a UUID identifying your project, required in every API URL
2. Delivery API key — for reading published content via the Delivery API (can be empty for public projects with no preview access control)
3. Management API key — for creating, updating, and publishing content via the Management API
4. Preview API key — for fetching draft/unpublished content (requires enabling Preview API in project settings)

Copy the Project ID and whichever API keys your integration requires. The Management API key has full write access to your project content — copy it carefully and do not share it. If your Kontent.ai project is publicly accessible (no Delivery API protection), the Delivery API key may not be required for read operations, but it is still good practice to use authenticated requests.

For webhook configuration, you will also need to create a webhook in Project Settings → Webhooks later in this guide. Each webhook has a secret that you can use to verify incoming requests.

**Expected result:** You have the Kontent.ai Project ID, Delivery API key, and Management API key copied from the API keys page.

### 2. Store Credentials in Replit Secrets

Open your Replit project and click the lock icon 🔒 in the left sidebar to open the Secrets pane. Add the following secrets:

- Key: KONTENT_PROJECT_ID — Value: your Kontent.ai Project ID
- Key: KONTENT_DELIVERY_API_KEY — Value: your Delivery API key
- Key: KONTENT_MANAGEMENT_API_KEY — Value: your Management API key

Click 'Add Secret' after each entry. The Project ID is technically not sensitive (it appears in URLs), but storing it as a Secret alongside the API keys keeps all configuration in one place and makes it easy to switch between projects by updating a single Secret.

If you use the Preview API for fetching draft content, also add:
- Key: KONTENT_PREVIEW_API_KEY — Value: your Preview API key

All Kontent.ai API calls use these values in Bearer token Authorization headers — they are never embedded in query parameters or URLs where they could appear in logs.

**Expected result:** KONTENT_PROJECT_ID, KONTENT_DELIVERY_API_KEY, and KONTENT_MANAGEMENT_API_KEY appear in the Secrets pane.

### 3. Fetch Content Using the Delivery API in Python

The Kontent.ai Delivery API is a RESTful API accessed at https://deliver.kontent.ai/{project_id}/items. Requests use Bearer token authentication with your Delivery API key. You can filter, sort, and paginate using URL query parameters.

Key query parameters for the Delivery API:
- system.type — filter by content type (e.g., ?system.type=article)
- elements.{element_name} — filter by element value
- order — sort order (e.g., ?order=elements.pub_date[desc])
- limit and skip — pagination
- depth — control how deeply linked items are resolved (0 = no resolution)

The response includes an 'items' array of content items, each with a 'system' object (id, name, type, language, last_modified) and an 'elements' object containing the content type's fields with their values. Linked content (referenced items) are either inline in the response or in a separate 'modular_content' object depending on the depth parameter.

```
import os
import requests

PROJECT_ID = os.environ["KONTENT_PROJECT_ID"]
DELIVERY_KEY = os.environ["KONTENT_DELIVERY_API_KEY"]
MANAGEMENT_KEY = os.environ["KONTENT_MANAGEMENT_API_KEY"]

DELIVERY_BASE = f"https://deliver.kontent.ai/{PROJECT_ID}"
MANAGEMENT_BASE = f"https://manage.kontent.ai/v2/projects/{PROJECT_ID}"

DELIVERY_HEADERS = {"Authorization": f"Bearer {DELIVERY_KEY}"}
MGMT_HEADERS = {
    "Authorization": f"Bearer {MANAGEMENT_KEY}",
    "Content-Type": "application/json"
}


def get_content_items(content_type: str = None, limit: int = 10,
                      order_by: str = None) -> dict:
    """Fetch content items from the Delivery API."""
    params = {"limit": limit, "depth": 1}
    if content_type:
        params["system.type"] = content_type
    if order_by:
        params["order"] = order_by
    resp = requests.get(
        f"{DELIVERY_BASE}/items",
        params=params,
        headers=DELIVERY_HEADERS
    )
    resp.raise_for_status()
    return resp.json()


def get_content_item_by_codename(codename: str) -> dict:
    """Fetch a single content item by its codename."""
    resp = requests.get(
        f"{DELIVERY_BASE}/items/{codename}",
        headers=DELIVERY_HEADERS
    )
    resp.raise_for_status()
    return resp.json()


def create_content_item(type_codename: str, item_name: str) -> dict:
    """Create a new content item of a given type via the Management API."""
    payload = {
        "name": item_name,
        "type": {"codename": type_codename}
    }
    resp = requests.post(
        f"{MANAGEMENT_BASE}/items",
        json=payload,
        headers=MGMT_HEADERS
    )
    resp.raise_for_status()
    return resp.json()


def upsert_language_variant(item_id: str, language: str, elements: list) -> dict:
    """Upsert a language variant with element values.
    elements: list of dicts like [{"element": {"codename": "title"}, "value": "Hello"}]
    """
    resp = requests.put(
        f"{MANAGEMENT_BASE}/items/{item_id}/variants/{language}",
        json={"elements": elements},
        headers=MGMT_HEADERS
    )
    resp.raise_for_status()
    return resp.json()


if __name__ == "__main__":
    # Fetch latest articles
    data = get_content_items(content_type="article", limit=5,
                             order_by="elements.pub_date[desc]")
    print(f"Found {len(data.get('items', []))} articles:")
    for item in data.get("items", []):
        title = item["elements"].get("title", {}).get("value", "(no title)")
        print(f"  {item['system']['codename']}: {title}")
```

**Expected result:** Running the script prints a list of published article content items from your Kontent.ai project with their titles.

### 4. Build a Node.js Content Delivery Server

The Node.js implementation uses axios for HTTP requests. Install it with 'npm install axios'. The Express server below provides endpoints for listing items by type and fetching individual items by codename.

For content-heavy applications, implement response caching to avoid hitting the Kontent.ai Delivery API on every request. The Delivery API has a built-in CDN layer, but caching common responses in your Replit backend (using a simple Map or Redis) reduces latency and API call volume for frequently accessed content like navigation menus and global settings.

```
const express = require('express');
const axios = require('axios');

const app = express();
app.use(express.json());

const PROJECT_ID = process.env.KONTENT_PROJECT_ID;
const DELIVERY_KEY = process.env.KONTENT_DELIVERY_API_KEY;
const MANAGEMENT_KEY = process.env.KONTENT_MANAGEMENT_API_KEY;

const DELIVERY_BASE = `https://deliver.kontent.ai/${PROJECT_ID}`;
const MANAGEMENT_BASE = `https://manage.kontent.ai/v2/projects/${PROJECT_ID}`;

const deliveryHeaders = { Authorization: `Bearer ${DELIVERY_KEY}` };
const managementHeaders = {
  Authorization: `Bearer ${MANAGEMENT_KEY}`,
  'Content-Type': 'application/json'
};

// List items by content type
app.get('/content/:type', async (req, res) => {
  const { type } = req.params;
  const { limit = 10, order } = req.query;
  try {
    const params = { 'system.type': type, limit, depth: 1 };
    if (order) params.order = order;
    const { data } = await axios.get(`${DELIVERY_BASE}/items`, {
      params,
      headers: deliveryHeaders
    });
    res.json({
      total: data.pagination?.total_count,
      items: data.items.map(item => ({
        id: item.system.id,
        codename: item.system.codename,
        name: item.system.name,
        last_modified: item.system.last_modified,
        elements: item.elements
      }))
    });
  } catch (err) {
    const status = err.response?.status || 500;
    res.status(status).json({ error: err.response?.data || err.message });
  }
});

// Get a single item by codename
app.get('/content/item/:codename', async (req, res) => {
  try {
    const { data } = await axios.get(
      `${DELIVERY_BASE}/items/${req.params.codename}`,
      { headers: deliveryHeaders }
    );
    res.json(data.item);
  } catch (err) {
    const status = err.response?.status || 500;
    res.status(status).json({ error: err.response?.data || err.message });
  }
});

// Create a new content item (Management API)
app.post('/content/create', async (req, res) => {
  const { name, type_codename } = req.body;
  if (!name || !type_codename) {
    return res.status(400).json({ error: 'name and type_codename required' });
  }
  try {
    const { data } = await axios.post(
      `${MANAGEMENT_BASE}/items`,
      { name, type: { codename: type_codename } },
      { headers: managementHeaders }
    );
    res.json(data);
  } catch (err) {
    const status = err.response?.status || 500;
    res.status(status).json({ error: err.response?.data || err.message });
  }
});

app.listen(3000, '0.0.0.0', () => {
  console.log('Kontent.ai integration server running on port 3000');
});
```

**Expected result:** The Node.js server returns content items from GET /content/article and individual items from GET /content/item/{codename}.

### 5. Deploy and Configure Webhooks

Deploy your Replit app by clicking the Deploy button. For a content delivery API, Autoscale deployment works well since request volume is predictable and the service can scale down during off-hours. After deploying, note your stable deployment URL (https://your-app.replit.app).

To receive Kontent.ai content change notifications, go to your Kontent.ai project → Project Settings → Webhooks → Add webhook. Enter your deployment URL (e.g., https://your-app.replit.app/webhook/kontent) and choose which workflow events should trigger notifications (item published, unpublished, etc.). Kontent.ai generates a webhook secret — store it as KONTENT_WEBHOOK_SECRET in Replit Secrets and use it to verify the X-KC-Signature header on incoming webhook requests.

Webhook verification uses HMAC-SHA256 with the raw request body and your webhook secret. Always verify signatures before processing webhook events to prevent spoofed cache invalidation or build triggers.

**Expected result:** Your Replit app is deployed with a stable URL, serves Kontent.ai content via the Delivery API, and receives webhook events for content changes.

## Best practices

- Store KONTENT_PROJECT_ID, KONTENT_DELIVERY_API_KEY, and KONTENT_MANAGEMENT_API_KEY in Replit Secrets — never hardcode them, especially the Management API key which has write access.
- Use content item codenames (not UUIDs) in Delivery API requests — codenames are stable identifiers that don't change and are human-readable.
- Enable only the Delivery API features you need (limit depth parameter) to keep response payloads small and API calls fast.
- Implement response caching for frequently accessed content like navigation menus and global settings — Kontent.ai content changes infrequently and caching reduces API call volume.
- Use webhook notifications for cache invalidation rather than polling — configure Kontent.ai webhooks to notify your Replit backend when content is published or updated.
- Verify Kontent.ai webhook signatures using HMAC-SHA256 with the webhook secret before processing any webhook-triggered actions.
- Use the Management API for content migrations and automation, but avoid using it in high-traffic request paths — it has lower rate limits than the Delivery API.
- Test with the Kontent.ai preview API during development to see draft content before publishing.

## Use cases

### Headless Frontend Content Fetching

A Replit Node.js server fetches articles, product listings, or landing page content from Kontent.ai and renders them as HTML or returns them as JSON for a frontend SPA. The backend uses Kontent.ai's Delivery API filtering to return only published items of a specific content type, sorted by publication date.

Prompt example:

```
Build an Express server that fetches all published blog articles from Kontent.ai, filters by a 'category' taxonomy element, sorts by publication date descending, and returns the articles as JSON with title, date, summary, and slug fields.
```

### Automated Content Import Pipeline

A Replit script reads structured data from an external source (CSV file, database, or third-party API) and creates corresponding content items in Kontent.ai using the Management API. This enables bulk content migrations or automated syndication of external data into the CMS.

Prompt example:

```
Write a Python script that reads product data from a CSV file, creates a Kontent.ai content item of type 'product' for each row using the Management API, and publishes each item automatically.
```

### Content Webhook Processor

A Replit backend receives Kontent.ai webhook events when content is published or updated, and triggers downstream actions such as cache invalidation, static site regeneration, or notifications to a Slack channel. The webhook handler verifies the request signature before processing.

Prompt example:

```
Create a Flask server that receives Kontent.ai workflow webhooks, logs the content item ID and transition details, and sends a Slack notification when any content item moves to the 'Published' workflow step.
```

## Troubleshooting

### 401 Unauthorized when calling the Delivery or Management API

Cause: The API key in the Authorization header is missing, malformed, or incorrect. Delivery and Management APIs use different keys — using the Management key for a Delivery request (or vice versa) will fail.

Solution: Verify the correct key is stored in the corresponding Replit Secret. Ensure the Authorization header is formatted as 'Bearer {key}' with no extra whitespace. Confirm KONTENT_DELIVERY_API_KEY is used for Delivery API calls and KONTENT_MANAGEMENT_API_KEY for Management API calls.

```
# Correct header format
headers = {"Authorization": f"Bearer {os.environ['KONTENT_DELIVERY_API_KEY']}"}
```

### 404 Not Found for content items that exist in the Kontent.ai dashboard

Cause: The Delivery API only returns published content items by default. Items in Draft or Archived workflow states are not returned. Also, using the item ID in the URL instead of the codename will cause 404 errors.

Solution: Ensure content items are published in Kontent.ai before expecting them to appear via the Delivery API. For draft preview, use the Preview API endpoint (preview-deliver.kontent.ai) with your Preview API key. Use item codenames (not UUIDs) in Delivery API URLs.

```
# Use codename, not ID, in Delivery API URLs
resp = requests.get(f"{DELIVERY_BASE}/items/my_article_codename", headers=DELIVERY_HEADERS)
```

### Management API returns 400 Bad Request when creating content items

Cause: The content type codename does not exist in the project, or the language variant elements don't match the required elements defined in the content type schema.

Solution: Check the exact codenames of content types and elements in Kontent.ai → Content models. Codenames are different from display names — they are lowercase with underscores. Also verify that all required elements are provided when upserting a language variant.

### Delivery API returns paginated results but you only see the first page

Cause: The Delivery API returns a maximum of 100 items per request by default. The response includes a 'pagination' object with 'next_page' and 'total_count' fields that must be used to fetch subsequent pages.

Solution: Check the 'pagination.next_page' field in the response and make additional requests until it is empty. Use the 'skip' and 'limit' parameters to paginate through large content sets.

```
def get_all_items(content_type):
    all_items = []
    skip = 0
    while True:
        data = get_content_items(content_type, limit=100, skip=skip)
        all_items.extend(data['items'])
        if not data.get('pagination', {}).get('next_page'):
            break
        skip += 100
    return all_items
```

## Frequently asked questions

### How do I store Kontent.ai credentials in Replit?

Click the lock icon 🔒 in the left sidebar to open the Secrets pane. Add KONTENT_PROJECT_ID, KONTENT_DELIVERY_API_KEY, and KONTENT_MANAGEMENT_API_KEY as separate secrets. Access them with os.environ['KONTENT_PROJECT_ID'] in Python or process.env.KONTENT_PROJECT_ID in Node.js.

### What is the difference between the Kontent.ai Delivery API and Management API?

The Delivery API is a read-only, CDN-backed API for fetching published content items — it is optimized for high-traffic production use. The Management API is a read-write API for creating, updating, and publishing content programmatically — it has lower rate limits and is intended for content migrations and automation workflows, not high-traffic request paths.

### Can I use Kontent.ai on Replit for free?

Yes. Kontent.ai offers a free Developer plan with limited content items and API calls, which is sufficient for development and small projects. Replit's free tier supports outbound API calls. For production applications with higher content volumes, Kontent.ai's paid plans provide increased limits and additional features.

### Why are my content items not appearing in Delivery API responses?

The Delivery API returns only published content items by default. Items in Draft, In Review, or Archived workflow states are not included. To see draft content, use the Preview API endpoint (preview-deliver.kontent.ai) with your Preview API key. Make sure to publish your content items in the Kontent.ai dashboard before expecting them in Delivery API responses.

### How do I filter Kontent.ai content items by a specific field?

Use query parameters in the format elements.{element_codename}={value} in your Delivery API request. For example, to filter articles by category: /items?system.type=article&elements.category=technology. Date filtering uses operators like [gt] and [lt]: ?elements.pub_date[gt]=2026-01-01.

---

Source: https://www.rapidevelopers.com/replit-integration/kentico
© RapidDev — https://www.rapidevelopers.com/replit-integration/kentico
