Contentful vs Strapi vs Sanity vs Payload 2026
Headless CMS Matured Beyond "Just an API"
In 2020, picking a headless CMS was about choosing between "hosted with a content API" (Contentful) or "self-hosted REST/GraphQL" (Strapi). By 2026, the category has fragmented into fundamentally different models:
- Contentful: Enterprise SaaS, AI-assisted content modeling, deep localization, reliable APIs at scale — expensive, but the proven enterprise choice
- Sanity: Real-time collaborative editor with GROQ query language and fully customizable studio — ideal for content teams with complex editorial workflows
- Strapi v5: The best self-hosted headless CMS — full REST and GraphQL APIs, a visual content builder, and a plugin ecosystem that's grown significantly
- Payload v3: A TypeScript-native CMS that lives inside your Next.js app — not a standalone service but a code-first content layer with a beautiful admin UI
These serve different personas. Contentful and Sanity are for content teams. Strapi is for developers who want a data API backend. Payload is for full-stack Next.js developers who want CMS as part of their application.
TL;DR
Contentful is the enterprise standard — built for large content operations with strict workflow, localization, and compliance requirements. Sanity is the best choice for content-heavy applications where editors need real-time collaboration and live preview. Strapi v5 is the self-hosted default for developers who want a fully customizable backend without vendor lock-in. Payload v3 is the right call for Next.js teams who want type-safe content management deeply integrated with their application code.
Key Takeaways
- Contentful: Free tier (5 users, 25K records, 2 locales); Starter $300/month; AI features in 2025 (content suggestions, auto-tagging)
- Sanity: Free tier (3 users, 10GB assets, 100K API calls/month); Starter $15/seat/month; GROQ query language; open-source Studio
- Strapi v5: Fully open source (MIT); self-hosted free forever; Strapi Cloud from $29/month; ~70K+ GitHub stars; SQL-only (v5 dropped MongoDB support)
- Payload v3: Open source (MIT); acquired by Figma on June 17, 2025 (MIT license maintained); lives inside Next.js; no per-record pricing; ~33K GitHub stars; Payload Cloud Standard $35/month
- API types: Contentful (REST + GraphQL), Sanity (GROQ + REST + GraphQL), Strapi (REST + GraphQL), Payload (REST + GraphQL + local API)
- TypeScript: Payload is TypeScript-first; Strapi v5 has full TS support; Contentful and Sanity have type generation
Pricing Comparison
| Plan | Contentful | Sanity | Strapi | Payload |
|---|---|---|---|---|
| Free tier | 25 content types, non-commercial | 10K docs, 100K API calls | ✅ (self-host + Cloud Free) | ✅ (self-host) |
| Entry paid | $300/mo (Starter) | $15/seat/mo (Starter) | $29/mo (Cloud) | $35/mo (Cloud Standard) |
| Mid tier | $789/mo (Team) | $25/seat/mo (Plus) | $99/mo (Cloud Pro) | Self-managed |
| Enterprise | Custom | Custom | Custom | Custom |
| Self-hosting | ❌ | ❌ | ✅ Free | ✅ Free |
| API call limits | 2M/month (free) | 100K/month (free) | Unlimited (self-host) | Unlimited (self-host) |
Contentful Pricing Detail
Contentful's pricing is based on records, locales, users, and API calls:
- Free (Community): 5 users; 25 content models (April 2025 restriction); 100K API calls/month; 2 locales; non-commercial use only (terms updated April 2025)
- Starter ($300/month): 25 users; 50,000 records; 10 locales; 10M API calls; Workflows
- Team ($789/month): 50 users; 200,000 records; 25 locales; scheduled publishing; launch darkly
- Enterprise: Custom; HIPAA, SOC 2, custom CDN, SSO, dedicated support
Important: Contentful updated its free tier terms in April 2025 — free accounts are now restricted to non-commercial use only and capped at 25 content types and 100K API calls/month. Commercial projects must move to Starter ($300/month). At that price point, Strapi self-hosted becomes significantly more attractive. 2025 additions include AI content suggestions and automatic tagging.
Sanity Pricing Detail
Sanity uses a seat + usage model:
- Free: 3 users; 10,000 documents (hard cap); 10GB assets; 100K API CDN requests/month (hard cap, no overages — requests blocked at limit); 1 public dataset; 2 projects
- Starter ($15/seat/month): 10GB assets per project; 1M API CDN requests; 5 users max on this tier
- Plus ($25/seat/month): Unlimited API requests; 20GB assets; custom input validation
- Growth: Custom; higher limits, SLA
Sanity's free tier enforces hard caps — when you hit the API limit, requests are blocked (not overaged). The 10K document limit and 1 public dataset restriction mean the free tier suits prototypes and small projects. Sanity's real-time Presentation (live preview) and Sanity Studio (React-based editor) are both open source. Spring 2025 additions: Media Library for asset management, Canvas AI-assisted editor, and App SDK for building custom Sanity integrations.
Strapi Pricing Detail
Strapi is fully open source. You only pay for Strapi Cloud (managed hosting) or build your own infrastructure:
- Community (self-host): Free, unlimited; requires your own server ($5–20/month on Railway, Render, or VPS)
- Strapi Cloud Free: Free managed tier (no credit card required); limited to 1 project
- Strapi Cloud Essential ($29/month): Managed hosting; 50GB transfer; 5 admin users; 2 environments
- Strapi Cloud Plus ($99/month): Custom domains; 10 admin users; 5 environments; advanced RBAC
- Enterprise: Custom; on-prem support; SLA; SSO
Strapi v5 (2024) introduced a revamped admin UI, Content Type Builder improvements, better TypeScript generation, and AI-powered i18n translation for content localization. Important: Strapi v5 dropped MongoDB support entirely — it's SQL-only (Postgres, MySQL, SQLite). If you're on MongoDB, either stay on v4 or plan a migration.
Payload Pricing Detail
Payload v3 is fully open source (MIT). Payload was acquired by Figma on June 17, 2025 — the MIT license remains intact and development continues independently. The acquisition signals long-term investment and stability rather than a pivot to proprietary licensing.
- Self-hosted: Free; runs inside Next.js 15+ App Router; requires your own Postgres or MongoDB
- Payload Cloud Standard ($35/month): Managed hosting; 1 project; Postgres database included
- Payload Cloud Pro ($199/month): Multiple projects; custom domains; priority support
- Community support: GitHub issues + Discord
Payload's Lexical rich text editor is the default in v3 — a significant improvement over the Slate-based editor in v2. The Figma acquisition has accelerated the roadmap significantly.
Developer Experience
Contentful: SDK-First Delivery API
// Contentful Delivery API — TypeScript with codegen
import { createClient } from 'contentful'
import type { TypeBlogPost } from './__generated/contentful-types'
const client = createClient({
space: process.env.CONTENTFUL_SPACE_ID!,
accessToken: process.env.CONTENTFUL_DELIVERY_TOKEN!,
environment: 'master'
})
// Type-safe content fetching
const entries = await client.getEntries<TypeBlogPost>({
content_type: 'blogPost',
select: ['fields.title', 'fields.slug', 'fields.body'],
order: ['-sys.createdAt'],
limit: 10
})
// Contentful Management API — create entries programmatically
import { createClient } from 'contentful-management'
const mgmtClient = createClient({ accessToken: process.env.CONTENTFUL_MANAGEMENT_TOKEN! })
const space = await mgmtClient.getSpace(process.env.CONTENTFUL_SPACE_ID!)
const environment = await space.getEnvironment('master')
const entry = await environment.createEntry('blogPost', {
fields: {
title: { 'en-US': 'My Post' },
slug: { 'en-US': 'my-post' },
body: { 'en-US': richTextContent }
}
})
await entry.publish()
Sanity: GROQ Query Language
// Sanity GROQ — powerful filtered queries with projections
import { createClient } from '@sanity/client'
const client = createClient({
projectId: process.env.SANITY_PROJECT_ID!,
dataset: 'production',
useCdn: true,
apiVersion: '2026-03-16'
})
// GROQ: filter + projection + reference resolution
const posts = await client.fetch(`
*[_type == "post" && publishedAt < now() && !(_id in path("drafts.**"))]
| order(publishedAt desc)
[0...10] {
_id,
title,
slug,
publishedAt,
"author": author->{ name, image },
"categories": categories[]->{ title, slug },
excerpt,
mainImage
}
`)
// Sanity real-time live preview
import { useLiveQuery } from '@sanity/preview-kit'
function BlogPost({ initialData, params }) {
// Data updates in real-time as editor types — no page refresh
const [data] = useLiveQuery(initialData, postQuery, params)
return <Article post={data} />
}
GROQ (Graph-Relational Object Queries) is Sanity's bespoke query language. It's more expressive than REST URL params for complex content relationships and joins, but requires learning a new query syntax.
Strapi: REST and GraphQL Auto-Generated
// Strapi REST API — auto-generated from content types
const response = await fetch(
`${process.env.STRAPI_URL}/api/posts?` +
`populate=author,categories,cover&` +
`filters[publishedAt][$notNull]=true&` +
`sort=publishedAt:desc&` +
`pagination[pageSize]=10`,
{
headers: {
Authorization: `Bearer ${process.env.STRAPI_TOKEN}`
}
}
)
const { data, meta } = await response.json()
# Strapi GraphQL — same data, different interface
query GetPosts($page: Int!) {
posts(
filters: { publishedAt: { notNull: true } }
sort: "publishedAt:desc"
pagination: { page: $page, pageSize: 10 }
) {
data {
id
attributes {
title
slug
publishedAt
author { data { attributes { name } } }
}
}
meta { pagination { total, page, pageSize } }
}
}
Strapi's admin UI auto-generates API endpoints from your content type definitions — add a field, the API updates immediately. No code required.
Payload: Code-First TypeScript
// Payload v3 — collection definition (TypeScript config)
import type { CollectionConfig } from 'payload'
export const Posts: CollectionConfig = {
slug: 'posts',
admin: {
useAsTitle: 'title',
defaultColumns: ['title', 'author', 'status', 'publishedAt'],
},
access: {
read: () => true,
create: isAdmin,
update: isAdminOrAuthor,
},
fields: [
{ name: 'title', type: 'text', required: true },
{ name: 'slug', type: 'text', unique: true },
{ name: 'content', type: 'richText' },
{ name: 'author', type: 'relationship', relationTo: 'users' },
{
name: 'status',
type: 'select',
options: ['draft', 'published'],
defaultValue: 'draft'
}
],
hooks: {
beforeChange: [generateSlug, validateContent],
afterChange: [invalidateCache, notifySlack],
}
}
// Payload Local API — query directly from Next.js Server Components
// No HTTP round-trip — direct database call
import { getPayloadHMR } from '@payloadcms/next/utilities'
import config from '@payload-config'
export async function BlogList() {
const payload = await getPayloadHMR({ config })
const posts = await payload.find({
collection: 'posts',
where: { status: { equals: 'published' } },
sort: '-publishedAt',
limit: 10,
depth: 1 // resolve author relationship
})
return <PostList posts={posts.docs} />
}
Payload's Local API is unique — it queries the database directly from your server code without an HTTP round-trip, which means zero authentication overhead and native TypeScript types throughout.
Content Modeling
| Capability | Contentful | Sanity | Strapi v5 | Payload v3 |
|---|---|---|---|---|
| Rich text | ✅ | ✅ (blocks) | ✅ | ✅ (Lexical) |
| Relationships | ✅ | ✅ (references) | ✅ | ✅ |
| Localization | ✅ (multi-locale) | ✅ | ✅ (i18n plugin) | ✅ |
| Versioning | ✅ | ✅ (revisions) | ✅ | ✅ (drafts) |
| Media management | ✅ | ✅ | ✅ | ✅ |
| Custom fields | ✅ | ✅ (plugins) | ✅ | ✅ (custom fields) |
| Nested content | ✅ | ✅ (portable text) | ✅ | ✅ (blocks) |
| Real-time preview | ✅ | ✅ (live) | ✅ (preview) | ✅ (draft mode) |
| Workflow / approval | ✅ (Starter+) | ❌ | ✅ | ❌ |
When to Choose Each
Choose Contentful if:
- Your content team is non-technical and needs the most polished editorial interface
- You need enterprise workflow (approval chains, scheduled publishing, audit logs)
- Multi-locale with translation management is a core requirement
- You want maximum reliability with no infrastructure management
- Note: Free tier is non-commercial only since April 2025 — commercial projects start at $300/month
Choose Sanity if:
- Real-time collaborative editing is critical (multiple editors in the same document)
- GROQ's query flexibility matters for complex content relationships
- You need highly customized editorial UI (Sanity Studio is fully React-extensible)
- You want live preview that updates as editors type (Presentation layer)
Choose Strapi v5 if:
- You need a self-hosted API backend with full data ownership
- You want REST and GraphQL APIs auto-generated from content types
- You need maximum plugin flexibility (marketplace ecosystem)
- Budget is a primary concern (free self-hosted, or try Strapi Cloud Free with no credit card)
- Note: Strapi v5 is SQL-only — if your data is in MongoDB, plan a migration or stay on v4
Choose Payload v3 if:
- Your project is a Next.js application and you want CMS as code
- You need full TypeScript safety from database to UI
- Direct database queries (Local API) are important for performance
- You want to avoid third-party service dependencies entirely
- The Figma acquisition gives you confidence in long-term investment and roadmap (MIT license unchanged)
Track Contentful, Strapi, Sanity, and Payload API availability on APIScout.
Related: Supabase vs Neon vs PlanetScale 2026 · Vercel AI SDK vs LangChain 2026