---
title: "Kentico to Contentful Migration: A Technical Guide"
slug: kentico-to-contentful-migration-a-technical-guide
date: 2026-10-12
author: Abdul Wahab
categories: [Migration Guide, Kentico, Contentful]
excerpt: "How to migrate content from Kentico Xperience 13 to Contentful: SQL extraction, HTML-to-Rich-Text conversion, CMA rate limits, content model mapping, and validation."
tldr: "Kentico to Contentful migration requires SQL extraction, HTML-to-Rich-Text AST conversion, and CMA ingestion at 7–10 req/s — there is no connector, and the rich text transform is the hardest part."
canonical: https://clonepartner.com/blog/kentico-to-contentful-migration-a-technical-guide
---

# Kentico to Contentful Migration: A Technical Guide


# Kentico to Contentful Migration: A Technical Guide

> [!NOTE]
> **TL;DR:** Kentico to Contentful migration requires flattening a hierarchical SQL-backed page tree into flat, API-delivered entries and converting raw HTML into Contentful's strict JSON Rich Text AST. There is no connector — you need a custom ETL pipeline, and the Rich Text conversion is the hardest part.

Migrating from [Kentico](https://www.kentico.com/) (Xperience 13 or earlier) to [Contentful](https://www.contentful.com/) means moving from a monolithic .NET CMS with a relational SQL Server database, page-tree hierarchy, and coupled data tables — to a headless, API-first platform where everything is a typed entry in a JSON content model and rich text is a structured AST, not HTML.

There is no off-the-shelf migration connector between these two platforms. You need a custom ETL pipeline: extract from Kentico's SQL database or REST API, transform page content and HTML into Contentful's Rich Text JSON document format, and load entries through the Content Management API (CMA) while respecting rate limits and dependency ordering. The hardest part is not moving the data — it is reshaping a tree-structured, server-rendered content model into flat, API-delivered structured content.

With [Kentico Xperience 13 reaching end of support on December 31, 2026](https://clonepartner.com/blog/blog/kentico-xperience-13-end-of-support-2026-what-actually-changes), teams running KX13 have a hard deadline. If your plan is to leave Kentico entirely — not upgrade to Xperience by Kentico — Contentful is a common target, especially for teams that want to decouple content from presentation and adopt a JavaScript or multi-channel frontend.

> [!WARNING]
> **Important clock distinction:** The timelines in this guide describe **the data migration itself** — extracting, transforming, and loading content from Kentico into Contentful. They do not describe the broader replatform effort (rebuilding frontend templates, integrations, editorial workflows), which is a separate project measured in months. ClonePartner moves the data; we do not rebuild the application.

## How Kentico and Contentful Store Content Differently

Before writing extraction queries, you need to understand how fundamentally these two systems differ in their data models.

**Kentico** uses a relational SQL Server database with a three-table structure for every page. The `CMS_Tree` table stores tree position and node-level properties shared across language versions. `CMS_Document` stores culture-specific page properties, widget data, and editable region content — with multiple records per page for multilingual sites. Coupled data tables (e.g., `DancingGoat_Article`) store the custom fields defined on each page type. URL routing is inherently tied to this physical tree structure.

**Contentful** stores everything as typed entries and assets inside spaces and environments. Each content type can have up to **50 fields**. Rich text is a JSON Abstract Syntax Tree (AST) with a `document` root node containing typed child nodes — not HTML strings, not Markdown. References between entries use `Link` objects with `sys.id` identifiers, not relational foreign keys. There is no inherent hierarchy or page tree.

The mapping table below covers the core concepts:

| Kentico Concept | Contentful Equivalent | Migration Notes |
|---|---|---|
| Page type | Content type | 1:1 mapping, but Contentful limits to 25 types (Starter) or 50 (Lite) per space |
| Page (CMS_Tree + CMS_Document + coupled) | Entry | Flatten the three-table join into a single entry |
| Coupled data fields | Content type fields | Max 50 fields per content type |
| Page attachment | Asset | Upload via CMA, link by `sys.id` |
| Media library file | Asset | Separate extraction from filesystem or Azure Blob Storage |
| Editable region (HTML) | Rich Text field (JSON AST) | Requires HTML → Rich Text AST conversion |
| Page relationship | Entry link (reference field) | Must resolve to Contentful `sys.id` |
| Content tree hierarchy | No native tree | Model via parent reference fields or slug conventions |
| Culture versions | Locales | Contentful uses field-level localization |

The architectural mismatch that trips up most teams: **Kentico's tree hierarchy has no direct equivalent in Contentful.** Contentful entries are flat. If your Kentico site relies on tree position for navigation, breadcrumbs, or URL generation, you need to model that explicitly — for example, with a `parent` reference field or a `slug` field that encodes the full path. You must extract Kentico's `NodeAliasPath` and `NodeParentID` values and use them to construct reference links between your new Contentful entries during the import phase.

## Extraction Method Decision Tree

Choosing the wrong extraction method wastes significant time. Use this decision tree to select your approach before writing any code:

```
Do you have direct SQL Server access to the Kentico database?
├── YES → Use SQL extraction (preferred for all KX12/KX13 installations)
│   ├── On-premises or self-hosted? → Direct connection string
│   └── Azure SQL? → Connect via Azure portal credentials or managed identity
└── NO
    ├── Is Kentico cloud-hosted with no DB access granted?
    │   ├── Volume < 2,000 pages? → Use REST API with pagination loop
    │   └── Volume > 2,000 pages? → Negotiate DB access or request a DB backup
    └── Do you only have the Kentico Export Wizard ZIP?
        └── Do NOT use for Contentful migration — proprietary format,
            no useful mapping to Contentful's data model
```

## How to Extract Content from Kentico

### SQL extraction (preferred)

Direct SQL extraction from Kentico's database is the most reliable path for cross-platform migrations. Join `CMS_Tree`, `CMS_Document`, and the relevant coupled data table for each page type. The `View_CMS_Tree_Joined` view simplifies this, but writing explicit joins gives you more control over which columns you extract.

```sql
SELECT 
  t.NodeAliasPath,
  t.NodeGUID,
  t.NodeParentID,
  d.DocumentCulture,
  d.DocumentName,
  d.DocumentModifiedWhen,
  a.ArticleTitle,
  a.ArticleSummary,
  a.ArticleText,
  a.PublishDate
FROM CMS_Tree t
JOIN CMS_Document d ON d.DocumentNodeID = t.NodeID
JOIN DancingGoat_Article a ON a.ArticleID = d.DocumentForeignKeyValue
WHERE t.NodeClassID = (
    SELECT ClassID FROM CMS_Class WHERE ClassName = 'DancingGoat.Article'
  )
  AND d.DocumentCulture = 'en-US'
  AND d.DocumentIsArchived = 0
ORDER BY t.NodeAliasPath
```

Note that `DocumentModifiedWhen` is included here — this field is the basis for delta sync (covered below). Export results as JSON, which maps cleanly to Contentful's ingestion scripts.

Repeat this per page type. Each page type has its own coupled table, so there is no single query that extracts all content — you need one query per type.

### REST API (limited use cases)

Kentico Xperience 13 exposes a REST service at `<admin-domain>/rest`. You can retrieve pages at paths like `/rest/content/currentsite/en-us/document/<alias-path>`. The REST API returns XML or JSON and supports pagination via `?skip=` and `?take=` parameters, but maximum page size is constrained to 1,000 records per call. For sites with tens of thousands of pages, looping through all pages at 1,000 records per call introduces significant latency — typically 2–5x slower than equivalent SQL extraction. Use REST only when direct database access is unavailable and content volume is below approximately 2,000 pages.

### The Kentico Export Wizard: why it does not apply here

The [Kentico export wizard](https://clonepartner.com/blog/blog/how-to-export-data-from-kentico-extraction-methods-api-limits-portability) produces a proprietary ZIP format containing XML data packages designed for [Kentico-to-Kentico transfers](https://clonepartner.com/blog/blog/what-kenticos-migration-tool-doesnt-move-kx13-xbyk). The format encodes Kentico-internal object references, widget configurations, and tree positions that have no equivalent in Contentful. Parsing this format to produce Contentful-compatible JSON requires more effort than SQL extraction with none of the benefits.

### Media and attachments

Kentico stores media in two places: **page attachments** (binary data in `CMS_Attachment` or on the filesystem) and **media library files** (in `~/[SiteName]/media/` or Azure Blob Storage). Both need separate extraction workflows. URLs for media library files typically follow the pattern `/getmedia/12345-guid/filename.jpg`. For Contentful, every media file becomes an Asset uploaded through the CMA's asset processing pipeline.

### Delta sync mechanics

The bulk migration captures a point-in-time snapshot of Kentico content. If editors continue publishing during migration validation, you need a final delta sync before cutover.

Delta sync uses `DocumentModifiedWhen` as the change-detection field. The process:

1. Record the timestamp when the bulk migration extraction began (`T_bulk`).
2. After bulk import and validation complete, query Kentico for all documents where `DocumentModifiedWhen > T_bulk`.
3. Also query for net-new records: `DocumentID` values not present in the original extraction result set.
4. Process only this changed set through the full ETL pipeline.
5. For updated records that already exist in Contentful, use the CMA's `PUT /entries/{entryId}` endpoint with the current `sys.version` to avoid conflict errors.

For detecting deletions (pages removed in Kentico after the bulk extraction), compare the full set of `NodeGUID` values from Kentico against the external ID index you built during the bulk import, then unpublish or archive the delta in Contentful.

## Content Model Design: What to Decide Before You Import

Do not start importing until your Contentful content model is finalized. Every content type change after import requires re-migration or manual cleanup.

### Content type budget

Contentful imposes hard limits on content types per space: **25 content types** on a Starter Space, **50** on a Lite Space, with more available on Enterprise plans. A typical Kentico site with 10–15 page types, plus custom module classes and custom tables, can approach the Starter limit fast — especially once you add component-level types for reusable content blocks.

### Worked example: consolidating 18 Kentico page types into 12 Contentful content types

A real Kentico site might have these page types that appear distinct but share significant structural overlap:

| Kentico Page Types (Before) | Contentful Content Types (After) | Consolidation Logic |
|---|---|---|
| Article, BlogPost, PressRelease | `Article` | Add `articleType` Symbol field (enum: article/blog/press-release); all three share title, body, author, publishDate, tags |
| ProductPage, ProductVariant | `Product` + `ProductVariant` | Keep separate — variant references parent product via Link field |
| TeamMember, Author | `Person` | Both have name, bio, image, role; merge with `personType` field |
| LandingPage, CampaignPage | `LandingPage` | Both are single-use, non-hierarchical; distinguish via `campaignTag` |
| FAQ, FAQCategory | `FAQ` + `FAQCategory` | Retain hierarchy via reference field |
| NewsItem | merged into `Article` | Identical field structure |
| OfficeLocation, StoreLocator | `Location` | Merge geographic content types; add `locationType` field |
| EventPage | `Event` | Retain — unique fields (startDate, endDate, venue, registrationUrl) |
| DocumentPage | Merge into `Resource` | PDF/download pages with no body content |
| ResourceHub, DownloadCenter | `Resource` | Both are asset-delivery wrappers |

Result: 18 Kentico page types → 12 Contentful content types. The consolidation preserves all semantic distinctions through taxonomy fields while staying within Starter plan limits.

### Field mapping rules

- **Short text fields** → `Symbol` (max 256 characters)
- **Long text / HTML body** → `RichText` (JSON AST) or `Text` (Markdown/plain)
- **Date fields** → `Date` (ISO 8601)
- **Boolean fields** → `Boolean`
- **Image/file fields** → `Link` to Asset
- **Related pages** → `Array` of `Link` to Entry
- **Categories/tags** → Contentful tags (up to 100 per entry) or a dedicated taxonomy content type with references

Each Contentful content type supports a maximum of **50 fields**. If a Kentico page type has more than 50 coupled data columns (rare but possible in legacy installations), you will need to split it into multiple linked content types or drop unused fields.

## How to Convert Kentico HTML to Contentful Rich Text

This is the hardest transformation in a [Kentico to Contentful migration](https://clonepartner.com/blog/migrate/kentico-to-contentful). **Contentful Rich Text is a JSON AST** — a tree of typed nodes (`paragraph`, `heading-1`, `embedded-entry-block`, etc.) with `content` arrays and `data` objects. Kentico stores body content as raw HTML strings, often littered with inline macros (e.g., `{% CurrentDocument.DocumentName %}`), shortcodes, and hardcoded relative links to internal pages or media files.

You cannot paste HTML into a Rich Text field via the CMA. You must convert it to the Rich Text document structure. A simple paragraph in Contentful looks like this:

```json
{
  "nodeType": "document",
  "data": {},
  "content": [
    {
      "nodeType": "paragraph",
      "content": [
        {
          "nodeType": "text",
          "value": "This is extracted from Kentico.",
          "marks": [],
          "data": {}
        }
      ],
      "data": {}
    }
  ]
}
```

Failing to strictly adhere to Contentful's Rich Text schema results in validation errors from the CMA, causing the entire entry creation to fail.

### Tooling comparison

| Tool | Type | Supported Input | Key Limitations | Maintenance Status |
|---|---|---|---|---|
| `@contentful/rich-text-from-markdown` | Official npm package | Markdown | No HTML support; requires pre-conversion of HTML to Markdown | Actively maintained by Contentful |
| `contentful-html-rich-text-converter` | Community npm package | HTML | Limited support for tables, nested lists; no inline style handling; does not validate against Contentful schema | Low activity (last commit 2022+) |
| `contentful-import` CLI | Official CLI tool | Contentful JSON export format | Requires pre-transformed JSON; no HTML parsing; ideal for the load phase after transformation | Actively maintained by Contentful |
| Custom HTML → AST parser (`cheerio`/`jsdom`) | Custom build | Any HTML | Build time required; full control over edge cases | N/A — you own it |

**Recommendation:** Use `contentful-import` for the load phase (it accepts a well-structured JSON file and handles rate limiting internally). Build a custom parser for the HTML → Rich Text AST transformation phase. The community converter is not recommended for production migrations due to schema validation gaps.

### Conversion options

**`@contentful/rich-text-from-markdown`** — Handles paragraphs, headings, emphasis, strong, links, lists, blockquotes, tables, and code blocks. Images require a custom callback. If your Kentico content is Markdown-based (unlikely for most KX13 sites), this is the cleanest path. For HTML content, you would first need to convert HTML to Markdown using a tool like `turndown`, which introduces a second transformation layer and additional error surface.

**Custom HTML → AST parser** — The most reliable approach for complex Kentico content. Parse HTML with a library like `cheerio` or `jsdom`, walk the DOM tree, and emit Contentful Rich Text nodes. This gives you full control over how Kentico-specific patterns (widget markup, inline macros, custom elements) get mapped.

Your migration middleware must:
1. Load the Kentico HTML string into an HTML parser.
2. Strip out unsupported Kentico inline macros or replace them with static text.
3. Identify `<img>` tags and `<a>` tags pointing to internal Kentico resources.
4. Walk the DOM tree and map standard HTML tags (`<h1>`, `<p>`, `<strong>`) to their corresponding Contentful `nodeType` objects.
5. Rebuild internal links using Contentful's `entry-hyperlink` or `asset-hyperlink` node types, which requires knowing the Contentful Entry ID of the linked target.

### Edge cases that break conversions

- **Inline images in HTML** — Contentful Rich Text supports `embedded-asset-block` but not inline images within a paragraph. Images must be block-level. Any `<img>` inside a `<p>` needs to be extracted and placed as a sibling block.
- **Tables** — Contentful Rich Text supports tables natively, but nested tables or tables with `colspan`/`rowspan` are not supported. Complex Kentico tables may need restructuring.
- **Widget markup** — KX13 page builder widget data is stored as JSON in `CMS_Document`. These are layout/presentation constructs with no content equivalent in Contentful. Extract the content from widget data; discard the layout metadata.
- **Embedded media with inline styles** — Contentful strips inline styles. If your Kentico content uses inline `style` attributes for layout (float, width), those are lost.
- **Rich Text size limit** — Contentful enforces a ~1 MB limit on Rich Text fields. Pages with extremely long body content may need to be split across multiple entries or fields.

### CMA error taxonomy for Rich Text failures

When the CMA rejects an entry, it returns a `422 Unprocessable Entity` with a structured error body. The following are the most common errors in migration context:

| CMA Error | Root Cause | Resolution |
|---|---|---|
| `invalid_entry` / `INVALID_RICH_TEXT` | Rich Text AST node has an unrecognized `nodeType` | Check for unsupported node types; Kentico widget markup or custom HTML elements often produce these |
| `INVALID_RICH_TEXT_MARKS` | Mark type not in allowed set (`bold`, `italic`, `underline`, `code`, `superscript`, `subscript`) | Strip or remap custom mark types; inline `<span style="color:red">` has no valid mark |
| `INVALID_RICH_TEXT_EMBEDDED_ENTRY` | Referenced entry `sys.id` does not exist or is not published | Import dependency ordering violated; ensure referenced entries exist before the linking entry |
| `RICH_TEXT_TOO_LARGE` | Serialized Rich Text field exceeds ~1 MB | Split content across multiple entries or multiple Rich Text fields |
| `LINKED_ENTRY_NOT_FOUND` | A `Link` field references a `sys.id` that was not created | Dependency ordering failure; rebuild import sequence or pre-validate ID map |
| `VERSION_MISMATCH` | `PUT` request on update specifies wrong `sys.version` | Re-fetch the entry to get current version before updating (critical in delta sync) |
| `TOO_MANY_REQUESTS` (429) | Rate limit exceeded | Read `X-Contentful-RateLimit-Reset` header; pause for specified seconds before retry |

## How to Handle Assets and Media Library Files

Contentful treats every image or document as an **Asset**. Migrating media from Kentico requires a multi-step API sequence:

1. **Extract the binary** — Download the file from the live Kentico environment, the underlying filesystem, or Azure Blob Storage.
2. **Create the Asset** — Send a POST request to the Contentful CMA with the file's metadata (title, description) and either a publicly accessible upload URL or direct binary data.
3. **Process the Asset** — Trigger the `/process` endpoint so Contentful downloads the file, generates thumbnails, and distributes it to the CDN.
4. **Publish the Asset** — Send a final request to publish the Asset so it becomes accessible via the delivery API.

Each step is a separate API call. At 7–10 requests per second (depending on plan tier), uploading 1,000 media files requires a minimum of 4,000 API calls — approximately 7–10 minutes of pure API time at maximum throughput, before accounting for asset processing latency, which is asynchronous. You must poll the asset status endpoint until `sys.status` returns `published` before referencing that asset in an entry.

For Kentico media library files stored on the filesystem, the simplest approach is to host them temporarily on an accessible URL (an S3 bucket or Azure Blob container) and pass the URL to Contentful's asset creation endpoint. For page attachments stored in `CMS_Attachment`, extract the binary data, write it to a temporary location, and follow the same process.

> [!TIP]
> **Asset ID mapping is critical.** Build and persist a lookup table mapping Kentico attachment GUIDs and media file paths to Contentful asset `sys.id` values. You will need this to resolve image references inside Rich Text fields and reference fields during entry creation.

Because content entries depend on asset IDs for Rich Text embedded images and reference fields, **media migration must always happen before content migration**.

## How to Load Entries into Contentful via the CMA

### Official tooling: `contentful-import`

Contentful's official `contentful-import` CLI tool accepts a JSON file in Contentful's export format and handles the full create-and-publish sequence, including basic retry logic. It is the recommended load mechanism when your transformation output can be structured as Contentful JSON.

```bash
contentful-import \
  --space-id YOUR_SPACE_ID \
  --environment-id sandbox \
  --management-token YOUR_CMA_TOKEN \
  --content-file ./transformed-output.json
```

The tool respects dependency ordering within a single import file if entries are sorted correctly. For large datasets, split the import file by content type and run assets first, then independent types, then dependent types. The tool does not handle Kentico-specific extraction or HTML transformation — it is load-only.

### Rate limits

The Contentful Content Management API enforces a default rate limit of **7 requests per second** on free plans and **10 requests per second** on paid plans (verified against Contentful's published API documentation). This is a per-space limit shared by all clients — including the Contentful web app itself. Running a migration script while editors are working in the same space will degrade their experience.

Your migration script must implement:

- **Throttling:** Use a task queue or a library like `p-limit` in Node.js to cap concurrent requests below the rate limit.
- **Retry logic:** When a 429 error occurs, read the `X-Contentful-RateLimit-Reset` header. Pause the script for the exact number of seconds specified before retrying.
- **Failure logging:** Log every failure with the source record ID for re-processing.

**Best practice:** Run migrations against a **sandbox environment**, not `master`. Contentful environments let you create isolated copies for testing. Migrate into a sandbox, validate, then promote using environment aliasing. Environment aliasing works by pointing a named alias (e.g., `master`) to a specific environment ID — you promote by updating the alias target, making the switch atomic from the delivery API's perspective with no downtime.

### Dependency ordering

Contentful entries cannot reference other entries that do not yet exist. If a `BlogPost` entry links to an `Author` entry, the author must be created and published first.

The correct import order is:

1. **Assets** — Upload, process, and publish all media first.
2. **Independent content types** — Authors, categories, tags — entries with no outbound references.
3. **Dependent content types** — Articles, pages — entries that reference other entries or assets.
4. **Publish** — Entries must be explicitly published after creation. A created-but-unpublished entry exists in draft state and will not appear in delivery API responses.

Skipping this ordering produces `LINKED_ENTRY_NOT_FOUND` or `UnresolvedLinks` errors on publish.

### Batch timing benchmarks

At 10 requests/second on a paid plan, with 2 API calls per entry (create + publish):

| Content Volume | Minimum API Time | Realistic Time (with retries + processing) |
|---|---|---|
| 500 entries | ~1.7 minutes | ~5–10 minutes |
| 5,000 entries | ~17 minutes | ~45–90 minutes |
| 20,000 entries | ~67 minutes | ~3–5 hours |
| 1,000 assets | ~7 minutes API time | ~20–40 minutes (async processing adds latency) |

Real-world throughput is lower than theoretical maximums because 429 retries add wait time (typically 1–10 seconds per retry), asset processing is asynchronous and variable, and validation errors on individual entries require logging, skipping, and re-queuing.

## What Does Not Migrate Cleanly

Some Kentico features have no Contentful equivalent. Understand what you will lose before you start:

| Kentico Feature | Contentful Status | What to Do |
|---|---|---|
| Page templates / layouts | No equivalent | Rebuild in your frontend framework |
| Page builder widgets | No equivalent | Extract content from widget JSON; discard layout |
| Workflows (approval chains) | Basic workflows on paid plans | Recreate manually; Contentful workflows are simpler |
| Custom modules | No equivalent | Evaluate if data belongs in Contentful or an external system |
| Form data (BizForms) | No equivalent | Migrate to a forms service (Typeform, HubSpot, etc.) |
| E-commerce (SKUs, orders) | No equivalent | Migrate to a commerce platform (Shopify, Commerce Layer, etc.) |
| User accounts / membership | No equivalent | External identity provider |
| URL aliases / routing rules | No built-in routing | Implement in frontend or CDN layer |
| Scheduled publishing | Supported (paid plans) | Available on Lite and above |
| A/B testing / personalization | Contentful Personalization (paid) | Separate product, requires Enterprise |

## Multilingual Content: How Locales Map

Kentico stores each language version as a separate `CMS_Document` record linked to the same `CMS_Tree` node via `DocumentNodeID`. Contentful uses **field-level localization** — a single entry contains values for all locales in each field.

Your import script must group all culture versions of a Kentico page into a single Contentful entry creation call with locale-specific field values:

```javascript
const entry = {
  fields: {
    title: {
      'en-US': 'Article Title',
      'de-DE': 'Artikeltitel'
    },
    body: {
      'en-US': richTextDocEN,
      'de-DE': richTextDocDE
    }
  }
};
```

Contentful requires locales to be configured in the space before import. The free plan supports **2 locales**, Lite supports **3**, and Enterprise supports more (verified against Contentful's published plan limits). If your Kentico site has more culture versions than your Contentful plan allows, you will need to upgrade or split content across spaces.

## Validation and Cutover Strategy

Never trust a migration until you have validated it programmatically.

**Record-count parity:** Compare entry counts per content type in Contentful against page counts per page type in Kentico. Mismatches indicate dropped records — usually caused by validation errors, malformed HTML, or unresolved references.

**Field-level validation:** Spot-check a sample of entries across every content type. Compare source field values against Contentful entry fields. Pay particular attention to Rich Text fields where conversion may have dropped elements.

**Referential integrity:** Verify that all entry references resolve. A broken link in Contentful is an entry reference pointing to a `sys.id` that does not exist — this will cause frontend errors if published. Query the Contentful CMA for entries in draft state; an entry that failed validation during publish will remain in draft.

**Asset verification:** Confirm all assets are in `published` state and their URLs resolve. Contentful's asset processing is async — an asset can exist in the space without being processed or published. Filter assets by `sys.publishedAt` is null to find unprocessed assets.

**Redirect map:** Export `NodeAliasPath` from Kentico and map it to the URL structure in your new frontend. Publish 301 redirects at cutover. Kentico URLs often follow the tree path (`/Products/Widget-A`); Contentful has no URL concept — your frontend defines routing.

A background sync approach keeps the Kentico source system operational during validation. Run the bulk migration, validate in a sandbox environment, fix issues, perform a delta sync (using `DocumentModifiedWhen` as the change-detection field), then promote the environment alias from the sandbox to `master` with no downtime.

## How Long Does a Kentico to Contentful Data Migration Take?

The data migration itself — extracting from Kentico, transforming content, and loading into Contentful — typically falls within **1–5 business days** for most sites. This depends on data volume, content complexity, the number of page types, and Contentful's CMA rate limits.

Small datasets under 10,000 records can finish in under 24 hours. A content-heavy Kentico site with 5,000+ pages, complex rich text, and thousands of media files is a custom engagement — typically an initial bulk migration followed by delta syncs, running 7–13+ business days depending on API rate limits, data complexity, and the customer's own availability for validation.

These numbers describe **the data migration only**. They do not describe the time to rebuild your frontend, configure editorial workflows in Contentful, or reimplement integrations. We recommend starting the scoping conversation **at least 3–4 months before your internal deadline**, especially if you are working against the KX13 end-of-support date.

## Kentico Xperience 13 End-of-Support Context

Kentico Xperience 13 reaches **end of support on December 31, 2026**. From January 1, 2027, Kentico provides no security patches, hotfixes, or technical support. KX13 has already been in security-only support since January 2026 — no new features, no functional bug fixes.

The platform continues to run after end of support; it is not switched off. But running unsupported software exposes your organization to unpatched vulnerabilities and compliance risk, particularly if you handle customer data or payments.

Kentico's recommended path is to upgrade to Xperience by Kentico. Migrating to Contentful instead is a fundamentally different decision — it is a replatform, not an upgrade. For teams evaluating that option, see [What Kentico's Migration Tool Doesn't Move (KX13 → XbyK)](https://clonepartner.com/blog/blog/what-kenticos-migration-tool-doesnt-move-kx13-xbyk) and [How to Export Data from Kentico](https://clonepartner.com/blog/blog/how-to-export-data-from-kentico-extraction-methods-api-limits-portability) for extraction specifics. If you are still evaluating target platforms, we have a detailed comparison of [Contentful alternatives](https://clonepartner.com/blog/blog/contentful-alternatives-2026-features-migration-tco), as well as dedicated migration guides for [Sitecore](https://clonepartner.com/blog/blog/kentico-to-sitecore-migration-a-technical-guide), [Optimizely](https://clonepartner.com/blog/blog/kentico-to-optimizely-migration-a-technical-guide), [Umbraco](https://clonepartner.com/blog/blog/kentico-to-umbraco-migration-a-technical-guide), and [WordPress](https://clonepartner.com/blog/blog/kentico-to-wordpress-migration-a-technical-guide).

## When to Use ClonePartner for This Migration

A Kentico-to-Contentful migration is a pipeline engineering problem: SQL extraction, HTML-to-Rich-Text-AST conversion, asset processing, rate limit management, dependency ordering, and multi-pass validation.

Our process:
1. Connect to your source and target accounts.
2. Build a custom extraction and transformation pipeline mapping Kentico page types to Contentful content types.
3. Provide unlimited sample migrations until the data structure is right.
4. Execute the full migration, followed by delta syncs to catch last-minute content changes.

One fixed price per project starting at $500 — no hourly billing, no per-record metering. A dedicated migration engineer owns your project from start to finish. Our transfer preference is **API > JSON > CSV**. For Kentico, that typically means direct SQL or API extraction on the source side and CMA ingestion on the Contentful side. We handle the Rich Text conversion, asset mapping, locale merging, and validation — the parts where migrations break.

> Book a free 30-minute scoping call to get a fixed-price proposal for your Kentico to Contentful migration. We'll review your page types, content volume, and media library to scope the work accurately.
>
> [Talk to us](https://clonepartner.com/talk-to-us?duration=30&utm_source=blog&utm_medium=button&utm_campaign=demo_bookings&utm_content=cta_click&utm_term=demo_button_click)

## Frequently asked questions

### Can I migrate directly from Kentico to Contentful?

There is no off-the-shelf migration connector between Kentico and Contentful. You need a custom ETL pipeline that extracts content from Kentico's SQL Server database (or REST API), converts HTML body content to Contentful's Rich Text JSON AST format, uploads media as Contentful Assets, and creates entries via the Content Management API.

### How do I convert Kentico HTML to Contentful Rich Text?

Contentful Rich Text is a JSON AST, not HTML. You can use the @contentful/rich-text-from-markdown npm package for Markdown content, or parse HTML with a library like cheerio or jsdom and emit Rich Text nodes programmatically. A custom parser gives the most control over edge cases like inline images, widget markup, and nested tables.

### What are Contentful's CMA rate limits for migration?

The Contentful Content Management API enforces a rate limit of 7 requests per second on the free plan and 10 requests per second on paid plans. This is a per-space limit shared across all clients. Creating and publishing 5,000 entries takes roughly 8–9 minutes of pure API time at the paid plan rate, before accounting for retries and asset processing.

### How long does a Kentico to Contentful data migration take?

The data migration itself typically takes 1–5 business days for most sites, depending on data volume, content complexity, and API rate limits. Small datasets under 10,000 records can finish in under 24 hours. This timeframe covers the data migration only — not rebuilding frontend templates, integrations, or editorial workflows, which is a separate project.

### Does Kentico Xperience 13 still work after end of support?

Yes. Kentico Xperience 13 reaches end of support on December 31, 2026 — not end of life. The platform continues to run, but Kentico will no longer provide security patches, hotfixes, or technical support from January 1, 2027. Running unsupported software carries increasing security and compliance risk.
