Read-API reference
API đọc bài của adapter citely-api — endpoint, auth, phân trang, mã lỗi.
Read-API phục vụ bài đã publish cho adapter citely-api.
Chỉ trả bài status = published, luôn scoped theo site → không rò bài nháp hay của tenant khác.
Base URL: https://citely-seo.com/connect/v1/sites/{siteToken}
Xác thực
siteTokennằm trong URL — token ngẫu nhiên khó đoán (không phải id tuần tự), công khai.- read-key (
Bearer) — bí mật, đặt server-side làCITELY_READ_KEY. Bắt buộc cholist+export. - Origin allowlist (mềm) + rate-limit phân tầng: request kèm read-key hợp lệ được coi là
build của chủ site (hạn mức lớn); ẩn danh theo
(token, IP)hạn mức chặt hơn.
| Method + path | Auth | Trả về |
|---|---|---|
GET /posts/{slug} | công khai (edge-cache) | 1 bài (Post) |
GET /posts?limit=&cursor= | Bearer <read-key> | { posts: PostListItem[], nextCursor } |
GET /export | Bearer <read-key> | { posts: Post[], count, truncated } |
GET /posts/{slug}
Công khai, cache ở CDN. Tuỳ chọn ?locale=vi để chọn đúng bản dịch khi một slug có nhiều
ngôn ngữ (không có locale → bản mới nhất).
curl https://citely-seo.com/connect/v1/sites/{siteToken}/posts/vi-du-slugResponse 200 — object Post:
{
"slug": "string",
"title": "string",
"seoTitle": "string | null",
"excerpt": "string | null",
"html": "string", // HTML sạch (JSON-LD KHÔNG nằm trong)
"markdown": "string | null",
"jsonLd": [{ "@type": "Article" }],
"cover": "string(url) | null", // 1 ảnh đại diện/OG (KHÔNG phải mảng ảnh)
"author": { "name": "string", "url": "string|null", "avatar": "string|null" } | null,
"tags": ["string"],
"locale": "vi",
"translationKey": "string | null",
"canonicalUrl": "string | null",
"publishedAt": "ISO-8601 | null",
"modifiedAt": "ISO-8601"
}Read-API trả cover (một ảnh) — không có mảng images[]. (Mảng images[] chỉ tồn tại ở envelope push, không phải ở read-API.)
GET /posts — list phân trang
Cần read-key. Phân trang keyset (ổn định, mới nhất trước). limit mặc định 20, tối đa 100.
Lặp bằng cách truyền nextCursor trả về vào ?cursor=.
curl -H "Authorization: Bearer $CITELY_READ_KEY" \
"https://citely-seo.com/connect/v1/sites/{siteToken}/posts?limit=20"Response 200:
{
"posts": [
{
"slug": "string", "title": "string", "excerpt": "string | null",
"cover": "string(url) | null", "tags": ["string"], "locale": "vi",
"translationKey": "string | null",
"publishedAt": "ISO-8601 | null", "modifiedAt": "ISO-8601"
}
],
"nextCursor": "string | null" // null = hết trang
}PostListItem bỏ bớt html/markdown/jsonLd (nhẹ). Cần thân bài → gọi GET /posts/{slug}.
GET /export — xuất toàn bộ (chống lock-in)
Cần read-key. Trả tối đa 1000 bài; vượt → truncated: true (không bao giờ cắt âm thầm).
curl -H "Authorization: Bearer $CITELY_READ_KEY" \
"https://citely-seo.com/connect/v1/sites/{siteToken}/export"{ "posts": [ /* Post đầy đủ */ ], "count": 128, "truncated": false }Mã lỗi (riêng của read-API)
Response lỗi dạng { "error": "<CODE>", "message": "…" }, luôn no-store.
| HTTP | code | Khi nào |
|---|---|---|
| 404 | CPP_NOT_FOUND | siteToken không tồn tại / không phải site citely-api (không lộ phân biệt) |
| 401 | CPP_SIG_INVALID | thiếu/sai read-key ở list/export |
| 403 | CPP_ORIGIN_FORBIDDEN | Origin không nằm trong allowlist của site |
| 429 | CPP_RATE_LIMITED | vượt hạn mức (kèm header Retry-After) |
CPP_NOT_FOUND và CPP_ORIGIN_FORBIDDEN là mã riêng của bề mặt read-API (chưa nằm trong error-catalog chung của Citely). Khác với bộ mã lỗi CPP_* của receiver dùng khi Citely push bài.