Pages are the workhorse of the API. One request gives you a whole page — every section, every block, every translation — ready to be poured into your templates. No N+1, no waterfall of fetches, no “why is the hero loading after the footer”.
There are two flavours:
GET /v1/pages— the table of contents: every page of the site, without content. Great for menus and sitemaps.GET /v1/pages/:marker— one page with everything inside. This is the one you will call 95% of the time.
List all pages
GET
/v1/pageshttps://back.sitecog.com/content/v1/pages
Returns every page of the site as an object keyed by page marker. Sections and blocks are not included — think of it as the building directory in the lobby, not the building itself.
Query parameters
langquerystringoptionalDefault: all active languagesWhich translations to include in page params (title, description, keywords). One code, a comma list (
ru,en) or a repeated parameter. See Languages.Example
curl https://back.sitecog.com/content/v1/pages \
-H "x-crm-key: pk_3f9c2a7e1b4d4c0e8a6f5b2d9c1e7a40"const res = await fetch('https://back.sitecog.com/content/v1/pages', {
headers: { 'x-crm-key': 'pk_3f9c2a7e1b4d4c0e8a6f5b2d9c1e7a40' },
});
const pages = await res.json();
Object.values(pages).forEach((page) => console.log(page.href, page.name));{
"home": {
"id": 26,
"marker": "home",
"name": "Home",
"href": "/",
"index": 0,
"params": {
"title": { "en": "VERTEX Air 3 — wireless earbuds" },
"description": { "en": "Hybrid noise cancelling, 42 hours of battery." }
}
},
"contacts": {
"id": 29,
"marker": "contacts",
"name": "Contacts",
"href": "/contacts",
"index": 3,
"params": {}
}
}Get one page with content
GET
/v1/pages/:markerhttps://back.sitecog.com/content/v1/pages/:marker
The whole page in one go: the page itself, its sections in
content, and the blocks of each section in the section's own content.Parameters
markerpathstringrequiredThe page marker you set in the CRM, e.g.
home or pricing. Latin letters, digits and underscores, 2–40 characters. Case matters: Home is not home.langquerystringoptionalDefault: all active languagesLimit translations to these languages.
?lang=en, ?lang=en,de or ?lang=en&lang=de. Every code must be an active language of the site, otherwise you get 400 unknown_lang.emptyqueryflagoptionalDefault: offReturn the page without its
content. Present = on: ?empty, ?empty=1, ?empty=true. Turn it off with 0 or false. Handy for SEO metadata when the body comes from somewhere else.x-crm-keyheaderstringrequiredYour site key. Can also be passed as
?key= if headers are not an option. See Site keys.Example request
curl "https://back.sitecog.com/content/v1/pages/home?lang=en" \
-H "x-crm-key: pk_3f9c2a7e1b4d4c0e8a6f5b2d9c1e7a40"const res = await fetch('https://back.sitecog.com/content/v1/pages/home?lang=en', {
headers: { 'x-crm-key': 'pk_3f9c2a7e1b4d4c0e8a6f5b2d9c1e7a40' },
});
if (!res.ok) throw new Error(`Content API: ${res.status}`);
const page = await res.json();
const hero = page.content.hero.content;
console.log(hero.hero_title.content.en); // "Earbuds that mute the city"// app/page.tsx — a Server Component, the key never reaches the browser
export default async function Home() {
const res = await fetch('https://back.sitecog.com/content/v1/pages/home?lang=en', {
headers: { 'x-crm-key': process.env.CRM_KEY! },
next: { revalidate: 60 },
});
const page = await res.json();
const hero = page.content.hero.content;
return <h1>{hero.hero_title.content.en}</h1>;
}Example response
{
"id": 26,
"marker": "home",
"name": "Home",
"href": "/",
"index": 0,
"params": {
"title": { "en": "VERTEX Air 3 — wireless earbuds" }
},
"content": {
"hero": {
"id": 41,
"marker": "hero",
"name": "Hero",
"index": 0,
"show": true,
"content": {
"hero_title": {
"id": 95,
"marker": "hero_title",
"name": "Hero title",
"type": "text",
"multilang": true,
"updatedAt": "2026-09-20T16:33:23.000Z",
"content": { "en": "Earbuds that mute the city" }
},
"hero_image": {
"id": 96,
"marker": "hero_image",
"name": "Hero image",
"type": "image",
"multilang": false,
"updatedAt": "2026-09-18T09:12:40.000Z",
"content": { "file": "https://cdn.example.com/storage/your-site/hero.jpg" }
}
}
},
"faq": {
"id": 44,
"marker": "faq",
"name": "FAQ",
"index": 5,
"show": false,
"content": { "…": "…" }
}
}
}Response fields
Three levels, one shape per level. Once you have seen one page you have seen them all.
idnumberInternal page id. Stable, but prefer the marker in your code — ids differ between environments.
markerstringThe page marker, the same one you put in the URL.
namestringHuman name from the CRM (“Home”). It is for editors, not for visitors — don't print it on the page.
hrefstring | nullThe path this page lives at on your site, if the editor filled it in.
null for service pages like common (header and footer).indexnumberPosition in the CRM menu, starting at 0. Sort by it to rebuild the order editors see.
paramsobjectPage settings.
title, description and keywords are language maps and respect lang; anything else (Open Graph tags, scripts) comes through exactly as saved.params →
title{ [lang]: string }The
<title> of the page.description{ [lang]: string }Meta description.
keywords{ [lang]: string }Meta keywords, if anyone still uses them. We don't judge.
content{ [sectionMarker]: Section }Sections of the page keyed by marker. Missing when you pass
?empty.content →
idnumberSection id.
markerstringSection marker, e.g.
hero.namestringEditor-facing name.
indexnumberOrder on the page. Object keys keep insertion order, but sorting by index is the honest way.
showbooleanWhether the editor wants this section visible. Hidden sections are still returned — hiding them is your template's job (
{section.show && <Faq />}).content{ [blockMarker]: Block }Blocks of the section keyed by marker.
content →
idnumberBlock id.
markerstringBlock marker, e.g.
hero_title.namestringEditor-facing name.
typestringOne of
text, html, image, video, link, number, color, date, date_range, boolean, object, array. It decides the shape of content.multilangbooleanFor images and videos: true if every language has its own file.
updatedAtstring (ISO 8601)When the block was last edited. Nice for “updated 2 hours ago” badges and cache keys.
contentdepends on typeThe actual value. A language map for text,
{ file } for a single image, and so on — every shape is on the Block types page.Errors
| Status | Body | What happened |
|---|---|---|
| 400 | {"message":"invalid_marker"} | The marker has characters outside A–Z a–z 0–9 _ or the wrong length. |
| 400 | {"message":"unknown_lang", …} | A language in lang is not active on the site. The body lists the available ones. |
| 401 | {"message":"invalid_key"} | Missing, malformed or revoked key. |
| 404 | {"message":"page_not_found"} | No page with this marker. Typo? Different site key? |
| 429 | {"message":"rate_limit_exceeded"} | Too many requests this minute. See Rate limits. |
The full list with fixes lives on the Errors page.