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.
Integration method
You connect Replit to Kontent.ai by retrieving your Project ID and API keys from the Kontent.ai dashboard, storing them in Replit Secrets, and calling the Delivery API from your Python or Node.js backend to fetch published content or the Management API to create and update content programmatically. The Delivery API uses API key authentication in an Authorization header and returns content items as structured JSON. The Management API requires a separate Management API key with broader permissions for write operations.
Prerequisites
- 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
Get Your Kontent.ai Project ID and API Keys
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.
Pro tip: Note the difference between the Delivery API (read published content) and the Management API (create/update/publish content). You need a separate key for each. The Management API key grants write access — store it with extra care.
Expected result: You have the Kontent.ai Project ID, Delivery API key, and Management API key copied from the API keys page.
Store Credentials in Replit Secrets
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.
Pro tip: Store the Project ID in Replit Secrets alongside the API keys — it simplifies configuration management and makes it easy to point your Replit app at a different Kontent.ai project by updating a single Secret value.
Expected result: KONTENT_PROJECT_ID, KONTENT_DELIVERY_API_KEY, and KONTENT_MANAGEMENT_API_KEY appear in the Secrets pane.
Fetch Content Using the Delivery API in Python
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.
1import os2import requests34PROJECT_ID = os.environ["KONTENT_PROJECT_ID"]5DELIVERY_KEY = os.environ["KONTENT_DELIVERY_API_KEY"]6MANAGEMENT_KEY = os.environ["KONTENT_MANAGEMENT_API_KEY"]78DELIVERY_BASE = f"https://deliver.kontent.ai/{PROJECT_ID}"9MANAGEMENT_BASE = f"https://manage.kontent.ai/v2/projects/{PROJECT_ID}"1011DELIVERY_HEADERS = {"Authorization": f"Bearer {DELIVERY_KEY}"}12MGMT_HEADERS = {13 "Authorization": f"Bearer {MANAGEMENT_KEY}",14 "Content-Type": "application/json"15}161718def get_content_items(content_type: str = None, limit: int = 10,19 order_by: str = None) -> dict:20 """Fetch content items from the Delivery API."""21 params = {"limit": limit, "depth": 1}22 if content_type:23 params["system.type"] = content_type24 if order_by:25 params["order"] = order_by26 resp = requests.get(27 f"{DELIVERY_BASE}/items",28 params=params,29 headers=DELIVERY_HEADERS30 )31 resp.raise_for_status()32 return resp.json()333435def get_content_item_by_codename(codename: str) -> dict:36 """Fetch a single content item by its codename."""37 resp = requests.get(38 f"{DELIVERY_BASE}/items/{codename}",39 headers=DELIVERY_HEADERS40 )41 resp.raise_for_status()42 return resp.json()434445def create_content_item(type_codename: str, item_name: str) -> dict:46 """Create a new content item of a given type via the Management API."""47 payload = {48 "name": item_name,49 "type": {"codename": type_codename}50 }51 resp = requests.post(52 f"{MANAGEMENT_BASE}/items",53 json=payload,54 headers=MGMT_HEADERS55 )56 resp.raise_for_status()57 return resp.json()585960def upsert_language_variant(item_id: str, language: str, elements: list) -> dict:61 """Upsert a language variant with element values.62 elements: list of dicts like [{"element": {"codename": "title"}, "value": "Hello"}]63 """64 resp = requests.put(65 f"{MANAGEMENT_BASE}/items/{item_id}/variants/{language}",66 json={"elements": elements},67 headers=MGMT_HEADERS68 )69 resp.raise_for_status()70 return resp.json()717273if __name__ == "__main__":74 # Fetch latest articles75 data = get_content_items(content_type="article", limit=5,76 order_by="elements.pub_date[desc]")77 print(f"Found {len(data.get('items', []))} articles:")78 for item in data.get("items", []):79 title = item["elements"].get("title", {}).get("value", "(no title)")80 print(f" {item['system']['codename']}: {title}")Pro tip: Content item codenames in Kontent.ai are auto-generated from names (lowercase, underscores). Use codenames (not IDs) in your Delivery API requests — they are stable and human-readable, unlike UUIDs.
Expected result: Running the script prints a list of published article content items from your Kontent.ai project with their titles.
Build a Node.js Content Delivery Server
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.
1const express = require('express');2const axios = require('axios');34const app = express();5app.use(express.json());67const PROJECT_ID = process.env.KONTENT_PROJECT_ID;8const DELIVERY_KEY = process.env.KONTENT_DELIVERY_API_KEY;9const MANAGEMENT_KEY = process.env.KONTENT_MANAGEMENT_API_KEY;1011const DELIVERY_BASE = `https://deliver.kontent.ai/${PROJECT_ID}`;12const MANAGEMENT_BASE = `https://manage.kontent.ai/v2/projects/${PROJECT_ID}`;1314const deliveryHeaders = { Authorization: `Bearer ${DELIVERY_KEY}` };15const managementHeaders = {16 Authorization: `Bearer ${MANAGEMENT_KEY}`,17 'Content-Type': 'application/json'18};1920// List items by content type21app.get('/content/:type', async (req, res) => {22 const { type } = req.params;23 const { limit = 10, order } = req.query;24 try {25 const params = { 'system.type': type, limit, depth: 1 };26 if (order) params.order = order;27 const { data } = await axios.get(`${DELIVERY_BASE}/items`, {28 params,29 headers: deliveryHeaders30 });31 res.json({32 total: data.pagination?.total_count,33 items: data.items.map(item => ({34 id: item.system.id,35 codename: item.system.codename,36 name: item.system.name,37 last_modified: item.system.last_modified,38 elements: item.elements39 }))40 });41 } catch (err) {42 const status = err.response?.status || 500;43 res.status(status).json({ error: err.response?.data || err.message });44 }45});4647// Get a single item by codename48app.get('/content/item/:codename', async (req, res) => {49 try {50 const { data } = await axios.get(51 `${DELIVERY_BASE}/items/${req.params.codename}`,52 { headers: deliveryHeaders }53 );54 res.json(data.item);55 } catch (err) {56 const status = err.response?.status || 500;57 res.status(status).json({ error: err.response?.data || err.message });58 }59});6061// Create a new content item (Management API)62app.post('/content/create', async (req, res) => {63 const { name, type_codename } = req.body;64 if (!name || !type_codename) {65 return res.status(400).json({ error: 'name and type_codename required' });66 }67 try {68 const { data } = await axios.post(69 `${MANAGEMENT_BASE}/items`,70 { name, type: { codename: type_codename } },71 { headers: managementHeaders }72 );73 res.json(data);74 } catch (err) {75 const status = err.response?.status || 500;76 res.status(status).json({ error: err.response?.data || err.message });77 }78});7980app.listen(3000, '0.0.0.0', () => {81 console.log('Kontent.ai integration server running on port 3000');82});Pro tip: The Kontent.ai Delivery API routes requests through a global CDN. For frequently accessed static content (navigation, global settings), add a short in-memory cache (1-5 minutes) in your Replit backend to reduce round-trip latency.
Expected result: The Node.js server returns content items from GET /content/article and individual items from GET /content/item/{codename}.
Deploy and Configure Webhooks
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.
Pro tip: After deploying, use the Kontent.ai webhook test feature in Project Settings → Webhooks to send a test event to your Replit endpoint. Check your deployment logs to verify the webhook is received and the signature validates correctly.
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.
Common 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.
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.
Copy this prompt to try it in Replit
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.
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.
Copy this prompt to try it in Replit
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.
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.
Copy this prompt to try it in Replit
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.
1# Correct header format2headers = {"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.
1# Use codename, not ID, in Delivery API URLs2resp = 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.
1def get_all_items(content_type):2 all_items = []3 skip = 04 while True:5 data = get_content_items(content_type, limit=100, skip=skip)6 all_items.extend(data['items'])7 if not data.get('pagination', {}).get('next_page'):8 break9 skip += 10010 return all_itemsBest 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.
Alternatives
Ghost is a lightweight headless blog CMS with a simpler API and lower cost, making it better suited for content-focused websites without Kentico's enterprise DXP requirements.
WordPress with the REST API provides a familiar content management interface with a vast plugin ecosystem, better suited for teams with WordPress experience than Kontent.ai's structured content model.
Joomla's built-in multilingual support and web services API make it a strong alternative for multilingual European enterprise sites that need a traditional CMS rather than a headless approach.
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.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation