Страницы — рабочая лошадка API. Один запрос — и у вас вся страница целиком: все секции, все блоки, все переводы, бери и раскладывай по шаблонам. Никаких N+1, никакого водопада запросов и никаких «почему шапка грузится позже подвала».
Запрос бывает двух видов:
GET /v1/pages— оглавление: все страницы сайта без содержимого. То, что нужно для меню и sitemap.GET /v1/pages/:marker— одна страница со всем, что внутри. Именно его вы будете вызывать в 95% случаев.
Список всех страниц
GET
/v1/pageshttps://back.sitecog.com/content/v1/pages
Возвращает все страницы сайта — объектом, где ключ это маркер страницы. Секций и блоков здесь нет: это табличка с номерами кабинетов у входа, а не само здание.
Параметры запроса
langquerystringнеобязательноПо умолчанию: все активные языкиКакие переводы положить в параметры страницы (title, description, keywords). Один код, список через запятую (
ru,en) или повторённый параметр. Подробно — в разделе Языки.Пример
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
/v1/pages/:markerhttps://back.sitecog.com/content/v1/pages/:marker
Вся страница за раз: сама страница, её секции в
content и блоки каждой секции — в собственном content секции.Параметры
markerpathstringобязательноМаркер страницы, который вы задали в CRM, например
home или pricing. Латинские буквы, цифры и подчёркивание, от 2 до 40 символов. Регистр важен: Home и home — разные страницы.langquerystringнеобязательноПо умолчанию: все активные языкиОставить переводы только на этих языках:
?lang=en, ?lang=en,de или ?lang=en&lang=de. Каждый код должен быть активным языком сайта, иначе получите 400 unknown_lang.emptyqueryflagнеобязательноПо умолчанию: выключенВернуть страницу без
content. Достаточно упомянуть параметр: ?empty, ?empty=1, ?empty=true. Выключается значениями 0 или false. Удобно для SEO-метаданных, когда тело страницы берётся откуда-то ещё.x-crm-keyheaderstringобязательноКлюч вашего сайта. Если заголовок передать никак, подойдёт и
?key=. Подробнее — в разделе Ключ сайта.Пример запроса
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 — серверный компонент, ключ до браузера не доходит
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>;
}Пример ответа
{
"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": { "…": "…" }
}
}
}Поля ответа
Три уровня, у каждого одна и та же форма. Разобрались с одной страницей — разобрались со всеми.
idnumberВнутренний номер страницы. Он не меняется, но в коде лучше опираться на маркер: номера в разных окружениях разные.
markerstringМаркер страницы — тот самый, что вы подставили в адрес.
namestringЧеловеческое название из CRM («Главная»). Оно для редакторов, а не для посетителей — выводить его на страницу не нужно.
hrefstring | nullПуть, по которому страница живёт на вашем сайте, если редактор его заполнил. У служебных страниц вроде
common (шапка и подвал) — null.indexnumberМесто в меню CRM, считается с 0. Отсортируйте по нему — и получите тот же порядок, что видят редакторы.
paramsobjectНастройки страницы.
title, description и keywords — карты переводов, они слушаются lang; всё остальное (теги Open Graph, скрипты) приходит ровно в том виде, в каком сохранено.params →
title{ [lang]: string }<title> страницы.description{ [lang]: string }Meta description.
keywords{ [lang]: string }Meta keywords — если ими кто-то ещё пользуется. Мы не осуждаем.
content{ [sectionMarker]: Section }Секции страницы, ключ — маркер. С
?empty этого поля нет.content →
idnumberНомер секции.
markerstringМаркер секции, например
hero.namestringНазвание для редакторов.
indexnumberПорядок на странице. Ключи объекта и так идут по порядку, но сортировать по index — честнее.
showbooleanХочет ли редактор, чтобы секцию было видно. Скрытые секции всё равно приходят в ответе — прятать их должен ваш шаблон (
{section.show && <Faq />}).content{ [blockMarker]: Block }Блоки секции, ключ — маркер.
content →
idnumberНомер блока.
markerstringМаркер блока, например
hero_title.namestringНазвание для редакторов.
typestringОдин из
text, html, image, video, link, number, color, date, date_range, boolean, object, array. От него зависит форма content.multilangbooleanДля картинок и видео: true, если у каждого языка свой файл.
updatedAtstring (ISO 8601)Когда блок правили последний раз. Пригодится для плашек «обновлено 2 часа назад» и ключей кэша.
contentзависит от typeСамо значение. Карта переводов для текста,
{ file } для одной картинки и так далее — все варианты собраны на странице Типы блоков.Ошибки
| Статус | Тело ответа | Что случилось |
|---|---|---|
| 400 | {"message":"invalid_marker"} | В маркере есть символы кроме A–Z a–z 0–9 _ или не та длина. |
| 400 | {"message":"unknown_lang", …} | Языка из lang нет среди активных языков сайта. Доступные перечислены прямо в ответе. |
| 401 | {"message":"invalid_key"} | Ключа нет, он кривой или отозван. |
| 404 | {"message":"page_not_found"} | Страницы с таким маркером нет. Опечатка? Ключ от другого сайта? |
| 429 | {"message":"rate_limit_exceeded"} | Слишком много запросов за эту минуту. Смотрите Лимиты. |
Полный список с рецептами лечения — на странице Ошибки.