Citely Docs
Nhà phát triển

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

  • siteToken nằ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 cho list + 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 + pathAuthTrả về
GET /posts/{slug}công khai (edge-cache)1 bài (Post)
GET /posts?limit=&cursor=Bearer <read-key>{ posts: PostListItem[], nextCursor }
GET /exportBearer <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-slug

Response 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.

HTTPcodeKhi nào
404CPP_NOT_FOUNDsiteToken không tồn tại / không phải site citely-api (không lộ phân biệt)
401CPP_SIG_INVALIDthiếu/sai read-key ở list/export
403CPP_ORIGIN_FORBIDDENOrigin không nằm trong allowlist của site
429CPP_RATE_LIMITEDvượt hạn mức (kèm header Retry-After)

CPP_NOT_FOUNDCPP_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.

On this page