Кожен запит до Content API несе ключ сайту. Він підказує нам, з якого сайту ви читаєте, — і це, власне, все, що він робить. Жодних танців з OAuth, жодного оновлення токенів, жодного підписування запитів опівночі. Один рядок в одному заголовку — і ви всередині.
Що таке ключ сайту
Ключ сайту виглядає так:
pk_3f9c2a7e1b4d4c0e8a6f5b2d9c1e7a40- Префікс
pk_, за яким рівно 32 малі шістнадцяткові символи:^pk_[a-f0-9]{32}$. - Він належить одному сайту. Лише ключ визначає, чий вміст ви отримаєте, — параметра на кшталт «site id» в API немає ніде.
- Він лише для читання і бачить тільки опублікований вміст.
- У сайту може бути кілька ключів одночасно, тож ротація минає безболісно (докладніше — нижче).
pk_ — це кивок у бік «publishable keys», які ви, можливо, знаєте з платіжних сервісів: ключ, створений для того, щоб жити в публічному коді. Чому це нормально, розповімо за хвилину.
Де взяти ключ
Відкрийте свій сайт у CRM
Увійдіть у Diil і оберіть сайт, вміст якого хочете читати.Перейдіть у Налаштування → Ключі Content API
Тут живуть усі ключі сайту.Створіть ключ
Ви отримаєте новенький рядокpk_…. Скопіюйте його.Покладіть його у змінну оточення
Не просто в код — ви з майбутнього, що ротуватимете ключі, будете вдячні. Шаблони для популярних фреймворків — нижче.
Як передати ключ
Покладіть ключ у заголовок запиту x-crm-key. Це стандартний спосіб, і він працює однаково з серверів і з браузерів — CORS дозволяє цей заголовок із будь-якого джерела.
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' },
});
const page = await res.json();GET /content/v1/pages/home?lang=en HTTP/1.1
Host: back.sitecog.com
x-crm-key: pk_3f9c2a7e1b4d4c0e8a6f5b2d9c1e7a40Запасний варіант ?key=
Якщо задати заголовок ну ніяк не виходить, передайте ключ як query-параметр:
curl "https://back.sitecog.com/content/v1/pages/home?lang=en&key=pk_3f9c2a7e1b4d4c0e8a6f5b2d9c1e7a40"Коли це нормально?
- Швидкі перевірки — вставити URL в адресний рядок браузера й подивитися, що повертає endpoint.
- Інструменти, що приймають лише URL — no-code інтеграція, імпортер фідів, плагін генератора статичних сайтів без налаштування заголовків.
В усіх інших випадках краще заголовок. URL мають звичку осідати в логах сервера, історії браузера й аналітиці, і хоча ключ не секрет, розкидати його всюди немає сенсу. До того ж так URL коротші, а код охайніший.
Чому ключ безпечно тримати в браузері
Коротко: бо він не вміє нічого такого, чого ваші відвідувачі не можуть зробити, просто відкривши ваш сайт. Ключ сайту публічний за задумом. Ось що він може, а що ні:
| Ключ сайту… | |
|---|---|
| читає опубліковані сторінки, секції, блоки й дописи блогу свого сайту | так |
| змінює, створює чи видаляє щось | ні — API лише для читання |
| бачить чернетки чи неопубліковані дописи блогу | ні — лише опублікований вміст |
| читає вміст інших ваших сайтів | ні — один ключ, один сайт |
Та сама ідея, що й у publishable key платіжного сервісу: він визначає, чиї дані показувати, але не дає над ними влади. Усе, що він може прочитати, і так опиниться на вашому публічному сайті.
Єдине, що скопійований ключ може, — витрачати вашу квоту запитів: у кожного ключа власний ліміт 600 запитів на хвилину (див. Ліміти запитів). Якщо хтось почне це робити, проведіть ротацію ключа — це займе кілька хвилин.
Ключ у змінних оточення
Ключ публічний — навіщо тоді змінні оточення? Бо ключі змінюються, і ротація ключа має бути зміною конфігурації, а не коду. До того ж більшість фреймворків вимагають префікс, перш ніж пустити змінну в код для браузера:
# Next.js — Server Components, Route Handlers (у браузер не потрапляє)
CRM_KEY=pk_3f9c2a7e1b4d4c0e8a6f5b2d9c1e7a40
# Next.js — Client Components (вбудовується в бандл під час збирання)
NEXT_PUBLIC_CRM_KEY=pk_3f9c2a7e1b4d4c0e8a6f5b2d9c1e7a40
# Vite (React, Vue, Svelte…) — вбудовується під час збирання
VITE_CRM_KEY=pk_3f9c2a7e1b4d4c0e8a6f5b2d9c1e7a40
# Nuxt — перевизначає runtimeConfig.public.crmKey
NUXT_PUBLIC_CRM_KEY=pk_3f9c2a7e1b4d4c0e8a6f5b2d9c1e7a40// app/page.tsx — Server Component
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();
return <h1>{page.content.hero.content.hero_title.content.en}</h1>;
}'use client';
import { useEffect, useState } from 'react';
export function HeroTitle() {
const [title, setTitle] = useState('');
useEffect(() => {
fetch('https://back.sitecog.com/content/v1/blocks/hero_title?section=hero&lang=en', {
headers: { 'x-crm-key': process.env.NEXT_PUBLIC_CRM_KEY! },
})
.then((r) => r.json())
.then((block) => setTitle(block.content.en ?? ''));
}, []);
return <h1>{title}</h1>;
}// src/content.ts
export async function getPage(marker: string, lang = 'en') {
const res = await fetch(`https://back.sitecog.com/content/v1/pages/${marker}?lang=${lang}`, {
headers: { 'x-crm-key': import.meta.env.VITE_CRM_KEY },
});
if (!res.ok) throw new Error(`Content API: ${res.status}`);
return res.json();
}export default defineNuxtConfig({
runtimeConfig: {
public: {
crmKey: '', // заповнюється з NUXT_PUBLIC_CRM_KEY
},
},
});<script setup>
const { public: { crmKey } } = useRuntimeConfig();
const { data: page } = await useFetch('https://back.sitecog.com/content/v1/pages/home', {
query: { lang: 'en' },
headers: { 'x-crm-key': crmKey },
});
</script>
<template>
<h1>{{ page.content.hero.content.hero_title.content.en }}</h1>
</template>Відкликання та ротація ключів
Будь-який ключ можна відкликати в CRM у тому ж списку Налаштування → Ключі Content API. Відкликаний ключ перестає працювати: кожен запит із ним отримує 401 invalid_key. Щоб замінити ключі без жодного невдалого запиту, використовуйте їх деякий час паралельно:
Створіть новий ключ
Старий поки не чіпайте — обидва працюють пліч-о-пліч.Задеплойте з новим ключем
Оновіть змінну оточення всюди, де використовувався старий ключ, перезберіть, якщо ваш фреймворк його вбудовує, і задеплойте.Перевірте, що сайт і далі отримує вміст
Відкрийте кілька сторінок, загляньте в логи. Жодних 401? Чудово.Відкличте старий ключ
Тепер можна сміливо висмикувати штепсель.
Помилки ключа
| Статус | Тіло | Що сталося |
|---|---|---|
| 401 | {"message":"invalid_key"} | Ключа немає, він некоректний (не pk_ + 32 hex) або відкликаний. |
| 429 | {"message":"rate_limit_exceeded"} | Забагато запитів — або забагато неправильних ключів з вашої IP за цю хвилину (див. нижче). |
Блокування за неправильні ключі
Щоб підбирати ключі було безглуздо, ми рахуємо запити з неправильними ключами для кожної IP. Понад 20 запитів із неправильним ключем за хвилину з однієї IP — і ця IP отримує 429 до кінця хвилини — навіть із правильним ключем. Заголовків Retry-After немає; вікно скидається на початку наступної хвилини.
Класичний спосіб випадково на це натрапити: ви відкликали старий ключ, а якийсь сервер чи забутий cron досі ним користується. Серверний рендеринг строчить 401, і за хвилину заблоковано весь сервер — разом із новим ключем. Тож:
const res = await fetch(url, { headers: { 'x-crm-key': process.env.CRM_KEY } });
if (res.status === 401) {
// Неправильний чи відкликаний ключ сам не виправиться. Повтори лише
// спалюють ліміт на неправильні ключі й призводять до блокування цієї IP.
throw new Error('Content API: неправильний ключ сайту, перевірте CRM_KEY');
}
if (res.status === 429) {
// Зачекайте до наступної хвилини (близько 60 с плюс невеликий випадковий зсув).
}Усі інші коди — на сторінці Помилки.