Content API
The content API is how you deliver SleekCMS content to your website, app, or frontend. It exposes your entire site's published content — pages, entries, images, option sets, and configuration — as structured JSON through a single authenticated endpoint. You fetch content by environment, and the response includes everything your frontend needs to render the site.
This page covers the API endpoint, authentication, response structure, the official client library and its methods, caching, internationalization, and framework integration patterns.
Endpoint and Authentication
The content API uses a single endpoint per site. You authenticate with a site token passed as the Authorization header and specify which environment to fetch from in the URL path.
GET https://pub.sleekcms.com/{site-id}/{environment}
{site-id} — Your site's unique identifier, assigned when you create the site in SleekCMS.
{environment} — The environment alias to fetch content from. Use latest for the current working state, or a named environment like production or staging for a specific content snapshot.
Authentication
Every request requires a site token in the Authorization header. You can find your site token in the SleekCMS admin interface under site settings.
curl -H "Authorization: YOUR_SITE_TOKEN" \
"https://pub.sleekcms.com/YOUR_SITE_ID/latest"
The site token is a read-only credential — it grants access to published content but cannot modify content, models, or settings. Treat it as a secret in server-side environments but be aware that it is necessarily exposed in client-side applications that call the API directly from the browser.
→ Environments & Content Versions
Response Structure
The API returns your entire site's content as a single JSON payload. This full-payload approach means your frontend receives everything in one request — no pagination, no follow-up queries, no resolving references.
{
"pages": [ ... ],
"entries": { ... },
"images": { ... },
"videos": { ... },
"options": { ... },
"config": { ... },
"_tag": "..."
}
pages
An array of all page records across all page models. Each page object contains its field data plus system properties:
_path — The page's URL path. Static pages have a fixed path like /about. Collection pages have a path that includes the slug, like /blog/hello-world.
_slug — Present on collection pages only. The slug portion of the path, used for route generation.
_meta — Metadata including key (the internal record identifier), created_at, and updated_at timestamps.
All content fields — text, images, references, groups, collections, dynamic blocks — are resolved inline. Referenced entries are embedded as full objects, not IDs. Markdown and rich text fields are delivered as rendered HTML. Image fields are delivered as objects with url, raw, alt, and variant properties.
{
"_slug": "italian",
"_path": "/blog/italian",
"title": "Italian inspiration on a budget",
"published_date": "2025-03-03",
"image": {
"alt": "Fresh Italian ingredients on a rustic table",
"url": "https://img.sleekcms.com/az41/m7urgy6z.webp",
"raw": "https://img.sleekcms.com/az41/m7urgy6z",
"source": null,
"light": null,
"dark": null
},
"category": ["veg", "vegan"],
"content": "<p>Create a blog post subtitle...</p>",
"_meta": {
"key": "cms:r2s2t1",
"updated_at": "2026-01-11T23:32:48.894Z",
"created_at": "2026-01-11T23:32:48.894Z"
}
}
entries
An object keyed by entry handle. Single entries (like a footer or site settings) resolve to an object. Entry collections (like authors or categories) resolve to an array of objects.
{
"entries": {
"footer": {
"copyright_text": "© 2025 Salt & Pepper. All rights reserved.",
"socials": [
{ "icon": "fab fa-facebook-f", "link": "/" },
{ "icon": "fab fa-instagram", "link": "/" }
]
}
}
}
When a page references an entry through a reference field, the entry's full data is embedded inline in the page object. You don't need to look up entries separately — they're already resolved in the page data.
images
Named images registered at the site level, keyed by name. Each image object follows the same structure as image fields on content records: url, raw, alt, source, and optional light/dark theme variants.
options
Option sets with assigned handles, keyed by handle name. Each option set is an array of { label, value } pairs.
config
Site-level configuration:
title — The site title, if set.
origin — The origin URL for the site, used for generating absolute URLs.
subdirectory — The subdirectory path, if the site is deployed under a subpath.
_tag
A version tag for the content snapshot. This value changes whenever content is published to the requested environment. Use it for cache invalidation — if the tag hasn't changed, the content hasn't changed.
Client Library
The official @sleekcms/client library wraps the API with typed methods for querying pages, entries, images, and option sets. It handles authentication, caching, environment resolution, and provides both synchronous and asynchronous client modes.
Installation
npm install @sleekcms/client
Or include directly from a CDN for browser usage:
<script src="https://unpkg.com/@sleekcms/client"></script>
When loaded from a CDN, the library is available globally as SleekCMS.
Creating a Client
The library provides two client constructors. Both accept the same configuration options but differ in how they fetch and return content.
createSyncClient() — Fetches all content upfront during initialization. After initialization, all methods return data synchronously. Best for static site generation where you want instant access to the full content at build time.
import { createSyncClient } from '@sleekcms/client';
const client = await createSyncClient({
siteToken: 'your-site-token',
env: 'latest'
});
// All methods return data immediately — no await needed
const page = client.getPage('/about');
const posts = client.getPages('/blog');
createAsyncClient() — Fetches content on demand. Every method returns a Promise. Best for server-side rendering where you want fresh content per request without loading the entire payload upfront.
import { createAsyncClient } from '@sleekcms/client';
const client = createAsyncClient({
siteToken: 'your-site-token',
resolveEnv: true
});
// All methods are async
const page = await client.getPage('/pricing');
const posts = await client.getPages('/blog');
Configuration Options
| Option | Type | Default | Description |
|---|---|---|---|
siteToken |
string |
required | Your site token from SleekCMS. |
env |
string |
'latest' |
Environment alias to fetch content from. |
resolveEnv |
boolean |
false |
Resolves the environment alias to its underlying version tag. Useful for CDN cache invalidation — produces a different cache key when content changes. Adds some latency to each request. |
lang |
string |
— | Language code for internationalized content. When set, the client returns translations for the specified locale. |
cache |
SyncCacheAdapter | AsyncCacheAdapter |
In-memory | Custom cache adapter for storing fetched content. |
cacheMinutes |
number |
— | Cache expiration time in minutes. If not set, cached content never expires. |
Client Methods
All methods are available on both the sync and async clients. On the sync client, they return values directly. On the async client, they return Promises.
getContent(search?)
Returns the full content payload, or a subset filtered using a JMESPath query. This is the most flexible method — JMESPath expressions can filter, project, and transform the content data.
// Full content payload
const content = client.getContent();
// All pages
const pages = client.getContent('pages');
// A specific entry
const footer = client.getContent('entries.footer');
// Site title from config
const title = client.getContent('config.title');
// Filter pages with JMESPath
const aboutPage = client.getContent('pages[?_path==`/about`] | [0]');
const featuredPosts = client.getContent('pages[?featured==`true`]');
JMESPath is a query language for JSON. It supports filtering ([?field==\value`]`), projections, slicing, and multi-select expressions. For full syntax, see jmespath.org.
getPage(path)
Returns a single page by exact path match. Returns null if no page exists at the given path.
const about = client.getPage('/about');
const post = client.getPage('/blog/hello-world');
const home = client.getPage('/');
The path must match exactly. /about matches the about page but not /about/team. For prefix matching, use getPages().
getPages(path, options?)
Returns all pages whose path starts with the given prefix. Use this to retrieve all pages within a section of your site.
const posts = client.getPages('/blog');
const products = client.getPages('/shop/products');
Options:
| Option | Type | Description |
|---|---|---|
collection |
boolean |
When true, only returns pages that belong to a collection (pages with a _slug). Excludes static index pages. |
// All pages under /blog, including the /blog index page
const allBlogPages = client.getPages('/blog');
// Only blog post collection items (excludes the /blog index)
const posts = client.getPages('/blog', { collection: true });
getEntry(handle)
Returns an entry by its handle. Single entries return an object. Entry collections return an array of objects.
// Single entry (e.g., admin-only site settings)
const header = client.getEntry('header');
// { logo: { url: '...' }, links: [...] }
// Entry collection
const team = client.getEntry('team-members');
// [{ name: 'Jane', role: 'Engineer', ... }, { name: 'Alex', role: 'Designer', ... }]
getSlugs(path)
Extracts slug values from all collection pages under a given path. Returns an array of strings. This is the primary method for generating static routes in frameworks like Next.js, Astro, and 11ty.
const slugs = client.getSlugs('/blog');
// ['hello-world', 'nextjs-tips', 'typescript-guide']
const productSlugs = client.getSlugs('/products');
// ['basic-plan', 'pro-plan', 'enterprise']
getImage(name)
Returns a named image registered at the site level.
const logo = client.getImage('logo');
// { url: 'https://...', alt: 'Company logo', raw: '...', ... }
getOptions(name)
Returns an option set by handle as an array of label-value pairs.
const categories = client.getOptions('categories');
// [{ label: 'Technology', value: 'tech' }, { label: 'Design', value: 'design' }, ...]
This is useful for building filter interfaces, navigation menus, or client-side dropdowns that need the full list of available options rather than just the selected value on a content record.
Caching
The client includes built-in caching to reduce API calls and improve performance. By default, fetched content is stored in an in-memory cache. You can provide your own cache adapter and control expiration.
Default Behavior
With no cache configuration, the client uses an in-memory cache that persists for the lifetime of the process. Content is fetched once and served from memory on subsequent calls.
Custom Cache Adapters
Any object that implements getItem and setItem works as a cache adapter. The browser's localStorage is a natural fit for client-side applications:
const client = await createSyncClient({
siteToken: 'your-site-token',
cache: localStorage,
cacheMinutes: 60 * 24 // Cache expires after 1 day
});
For custom storage backends, implement the adapter interface:
// Synchronous adapter
interface SyncCacheAdapter {
getItem(key: string): string | null;
setItem(key: string, value: string): void;
}
// Asynchronous adapter (for IndexedDB, Redis, remote caches)
interface AsyncCacheAdapter {
getItem(key: string): Promise<string | null>;
setItem(key: string, value: string): Promise<void>;
}
Cache Expiration
The cacheMinutes option controls how long cached content remains valid. When the cache expires, the next request fetches fresh content from the API and repopulates the cache.
If cacheMinutes is not set, cached content never expires — it persists until the cache is manually cleared or the process restarts (for in-memory caches).
Internationalization
The client supports multi-language content through the lang configuration option. When set, the API returns translated content for the specified locale.
const client = await createSyncClient({
siteToken: 'your-site-token',
lang: 'es'
});
const page = client.getPage('/about');
// Returns the Spanish translation of the about page
Language codes correspond to the locales configured in your SleekCMS site settings. If a translation doesn't exist for the requested locale, the API falls back to the default language content.
To serve multiple languages, create a separate client instance per locale:
const clients = {
en: await createSyncClient({ siteToken: 'your-site-token', lang: 'en' }),
es: await createSyncClient({ siteToken: 'your-site-token', lang: 'es' }),
de: await createSyncClient({ siteToken: 'your-site-token', lang: 'de' }),
};
const page = clients[userLocale].getPage('/about');
→ Supporting Multiple Languages
Content Data Shapes
Understanding the shape of content objects in API responses helps you build typed frontends and render content correctly.
Page Objects
Every page object includes its content fields plus system properties:
| Property | Type | Description |
|---|---|---|
_path |
string |
The full URL path for the page. |
_slug |
string |
The slug segment (collection pages only). |
_meta.key |
string |
Internal record identifier. |
_meta.created_at |
string |
ISO 8601 creation timestamp. |
_meta.updated_at |
string |
ISO 8601 last update timestamp. |
All other properties are the page's content fields, named by their handle.
Image Objects
Image fields and named images share the same object structure:
| Property | Type | Description |
|---|---|---|
url |
string |
The optimized image URL (typically WebP format). |
raw |
string |
The base image URL without format conversion. Used for building transformed URLs with query parameters. |
alt |
string | null |
Alt text for the image. |
source |
string | null |
The image source provider (e.g., "unsplash", null for uploaded images). |
light |
object | null |
Light theme variant of the image, if set. |
dark |
object | null |
Dark theme variant of the image, if set. |
Entry Objects
Entry objects contain their content fields plus a _meta block. The shape depends on the entry model's field configuration. When referenced from a page, the full entry object is embedded inline.
Dynamic Block Arrays
Dynamic block fields return an array of block objects. Each block includes a _type property identifying the block model handle, plus the block's content fields:
const page = client.getPage('/');
// page.sections → [
// { _type: 'hero', heading: 'Welcome', image: { url: '...' } },
// { _type: 'features', items: [...] },
// { _type: 'cta', heading: 'Get Started', buttonLabel: 'Sign Up' }
// ]
TypeScript
The client library is fully typed. Import the type definitions to annotate your code:
import type {
SleekClient,
SleekAsyncClient,
Page,
Entry,
Image,
Options
} from '@sleekcms/client';
Framework Integration
The client works with any JavaScript framework. The choice between sync and async clients maps to the rendering strategy your framework uses.
Next.js (Static Site Generation)
Use the sync client in generateStaticParams and page components to fetch content at build time:
// app/blog/[slug]/page.tsx
import { createSyncClient } from '@sleekcms/client';
export async function generateStaticParams() {
const client = await createSyncClient({
siteToken: process.env.SLEEKCMS_SITE_TOKEN!
});
return client.getSlugs('/blog').map((slug) => ({ slug }));
}
export default async function Post({ params }: { params: { slug: string } }) {
const client = await createSyncClient({
siteToken: process.env.SLEEKCMS_SITE_TOKEN!
});
const post = client.getPage(`/blog/${params.slug}`);
return <h1>{post?.title}</h1>;
}
Next.js (Server-Side Rendering)
Use the async client with resolveEnv to ensure fresh content on each request:
// app/blog/page.tsx
import { createAsyncClient } from '@sleekcms/client';
const client = createAsyncClient({
siteToken: process.env.SLEEKCMS_SITE_TOKEN!,
resolveEnv: true
});
export default async function BlogPage() {
const posts = await client.getPages('/blog');
return (
<div>
{posts?.map((post) => (
<article key={post._path}>
<h2>{post.title}</h2>
</article>
))}
</div>
);
}
SvelteKit
// +page.server.ts
import { createAsyncClient } from '@sleekcms/client';
const client = createAsyncClient({
siteToken: process.env.SLEEKCMS_SITE_TOKEN,
resolveEnv: true
});
export async function load() {
const posts = await client.getPages('/blog');
return { posts };
}
Astro
---
// src/pages/blog/index.astro
import { createSyncClient } from '@sleekcms/client';
const client = await createSyncClient({
siteToken: import.meta.env.SLEEKCMS_SITE_TOKEN
});
const posts = client.getPages('/blog');
---
<div>
{posts?.map((post) => (
<article>
<h2>{post.title}</h2>
</article>
))}
</div>
React (Client-Side)
For React SPAs, use the @sleekcms/react package which provides hooks with built-in loading states and error handling:
import { SleekCMSProvider, usePage, usePages, useEntry } from '@sleekcms/react';
function App() {
return (
<SleekCMSProvider siteToken="your-site-token">
<BlogPage />
</SleekCMSProvider>
);
}
function BlogPage() {
const { data: posts, loading } = usePages('/blog');
if (loading) return <p>Loading...</p>;
return (
<div>
{posts?.map((post) => (
<article key={post._path}>
<h2>{post.title}</h2>
</article>
))}
</div>
);
}
What's Next
- @sleekcms/client — Full client library reference including CDN usage and advanced configuration.
- @sleekcms/react — React hooks for client-side content fetching.
- Environments & Content Versions — Managing content snapshots and multi-stage publishing.
- Automating using Triggers — Triggering builds and workflows when content is published.
- Content Modeling — How content models define the shape of API responses.
- Content Field Types — How each field type appears in API responses.