Seiten sind das Arbeitspferd der API. Ein Request liefert dir eine ganze Seite — jede Section, jeden Block, jede Übersetzung —, fertig zum Eingießen in deine Templates. Kein N+1, kein Wasserfall aus Fetches, kein „Warum lädt der Hero erst nach dem Footer?“.
Es gibt zwei Varianten:
GET /v1/pages— das Inhaltsverzeichnis: alle Seiten der Website, ohne Content. Super für Menüs und Sitemaps.GET /v1/pages/:marker— eine Seite mit allem drin. Die rufst du in 95 % der Fälle auf.
Alle Seiten auflisten
GET
/v1/pageshttps://back.sitecog.com/content/v1/pages
Liefert alle Seiten der Website als Objekt, nach Seiten-Marker geschlüsselt. Sections und Blöcke sind nicht dabei — stell es dir als Wegweiser in der Eingangshalle vor, nicht als das Gebäude selbst.
Query-Parameter
langquerystringoptionalStandard: alle aktiven SprachenWelche Übersetzungen in den Seitenparametern landen (title, description, keywords). Ein Code, eine kommagetrennte Liste (
ru,en) oder ein wiederholter Parameter. Siehe Sprachen.Beispiel
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": {}
}
}Eine Seite mit Content abrufen
GET
/v1/pages/:markerhttps://back.sitecog.com/content/v1/pages/:marker
Die ganze Seite auf einen Rutsch: die Seite selbst, ihre Sections in
content und die Blöcke jeder Section im content der jeweiligen Section.Parameter
markerpathstringPflichtDer Seiten-Marker, den du im CRM festgelegt hast, z. B.
home oder pricing. Lateinische Buchstaben, Ziffern und Unterstriche, 2–40 Zeichen. Groß- und Kleinschreibung zählt: Home ist nicht home.langquerystringoptionalStandard: alle aktiven SprachenÜbersetzungen auf diese Sprachen beschränken.
?lang=en, ?lang=en,de oder ?lang=en&lang=de. Jeder Code muss eine aktive Sprache der Website sein, sonst bekommst du 400 unknown_lang.emptyqueryflagoptionalStandard: ausLiefert die Seite ohne ihren
content. Vorhanden = an: ?empty, ?empty=1, ?empty=true. Ausschalten mit 0 oder false. Praktisch für SEO-Metadaten, wenn der Inhalt von woanders kommt.x-crm-keyheaderstringPflichtDein Site-Key. Geht auch als
?key=, falls Header keine Option sind. Siehe Site-Keys.Beispiel-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 — eine Server Component, der Key erreicht nie den 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>;
}Beispiel-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": { "…": "…" }
}
}
}Felder der Response
Drei Ebenen, eine Form pro Ebene. Wer eine Seite gesehen hat, hat alle gesehen.
idnumberInterne Seiten-ID. Stabil, aber nimm in deinem Code lieber den Marker — IDs unterscheiden sich zwischen Umgebungen.
markerstringDer Seiten-Marker, derselbe, den du in die URL schreibst.
namestringMenschenlesbarer Name aus dem CRM („Home“). Der ist für die Redaktion, nicht für Besucher — gib ihn nicht auf der Seite aus.
hrefstring | nullDer Pfad, unter dem diese Seite auf deiner Website liegt, falls die Redaktion ihn ausgefüllt hat.
null bei Service-Seiten wie common (Header und Footer).indexnumberPosition im CRM-Menü, beginnend bei 0. Sortiere danach, um die Reihenfolge nachzubauen, die die Redaktion sieht.
paramsobjectSeiteneinstellungen.
title, description und keywords sind Sprach-Maps und beachten lang; alles andere (Open-Graph-Tags, Skripte) kommt exakt so durch, wie es gespeichert wurde.params →
title{ [lang]: string }Der
<title> der Seite.description{ [lang]: string }Meta-Description.
keywords{ [lang]: string }Meta-Keywords, falls die noch jemand benutzt. Wir urteilen nicht.
content{ [sectionMarker]: Section }Sections der Seite, nach Marker geschlüsselt. Fehlt, wenn du
?empty mitschickst.content →
idnumberID der Section.
markerstringMarker der Section, z. B.
hero.namestringName für die Redaktion.
indexnumberReihenfolge auf der Seite. Objekt-Keys behalten zwar die Einfügereihenfolge, aber nach index zu sortieren ist der ehrliche Weg.
showbooleanOb die Redaktion diese Section sichtbar haben will. Versteckte Sections werden trotzdem ausgeliefert — sie auszublenden ist Job deines Templates (
{section.show && <Faq />}).content{ [blockMarker]: Block }Blöcke der Section, nach Marker geschlüsselt.
content →
idnumberID des Blocks.
markerstringMarker des Blocks, z. B.
hero_title.namestringName für die Redaktion.
typestringEiner von
text, html, image, video, link, number, color, date, date_range, boolean, object, array. Er bestimmt die Form von content.multilangbooleanFür Bilder und Videos: true, wenn jede Sprache ihre eigene Datei hat.
updatedAtstring (ISO 8601)Wann der Block zuletzt bearbeitet wurde. Schön für „vor 2 Stunden aktualisiert“-Badges und Cache-Keys.
contentdepends on typeDer eigentliche Wert. Eine Sprach-Map bei Text,
{ file } bei einem einzelnen Bild und so weiter — jede Form steht auf der Seite Blocktypen.Fehler
| Status | Body | Was passiert ist |
|---|---|---|
| 400 | {"message":"invalid_marker"} | Der Marker enthält Zeichen außerhalb von A–Z a–z 0–9 _ oder hat die falsche Länge. |
| 400 | {"message":"unknown_lang", …} | Eine Sprache in lang ist auf der Website nicht aktiv. Der Body listet die verfügbaren auf. |
| 401 | {"message":"invalid_key"} | Key fehlt, ist fehlerhaft oder wurde widerrufen. |
| 404 | {"message":"page_not_found"} | Keine Seite mit diesem Marker. Tippfehler? Anderer Site-Key? |
| 429 | {"message":"rate_limit_exceeded"} | Zu viele Requests in dieser Minute. Siehe Rate Limits. |
Die vollständige Liste mit Lösungen findest du auf der Seite Fehler.