Chuyển tới nội dung
Sursa Developers

Phân trang

Danh sách của Sursa API được phân trang bằng con trỏ: bạn yêu cầu tối đa limit phần tử, và phản hồi cho bạn biết cách lấy trang tiếp theo. Bạn không bao giờ phải tính offset, và phần tử không bị lặp hay bị sót giữa các trang khi dữ liệu thay đổi trong lúc đó.

Tham sốGiá trị
limitSố phần tử mỗi trang, thường từ 1 đến 200. Các danh sách mới mặc định là 50; tài liệu tham chiếu ghi khoảng giá trị và giá trị mặc định của từng danh sách.
cursorCon trỏ từ trang trước. Bỏ qua tham số này cho trang đầu tiên.

Danh sách trả về con trỏ tiếp theo theo một trong hai cách, tùy theo dạng phản hồi của nó. Trang của mỗi endpoint trong tài liệu tham chiếu API cho biết nó dùng cách nào.

Danh sách có lớp bọc data

Các danh sách được thêm cho API công khai, chẳng hạn nguồn cấp sự kiện, trả về một đối tượng với các phần tử trong data:

curl "https://api.sursa.com/v1/webhook-events?limit=2" \
  -H "Authorization: Bearer $SURSA_API_KEY"
{
  "data": [
    {"id": "evt_8a1f…", "type": "card.published", "created_at": "2026-10-24T10:00:00Z"},
    {"id": "evt_8a20…", "type": "card.updated", "created_at": "2026-10-24T10:00:03Z"}
  ],
  "next_cursor": "eyJ2IjoxLCJrIjpbMTA0Ml19",
  "has_more": true
}

Khi has_more là true, hãy yêu cầu trang tiếp theo với cursor đặt bằng next_cursor và cùng các bộ lọc:

curl "https://api.sursa.com/v1/webhook-events?limit=2&cursor=eyJ2IjoxLCJrIjpbMTA0Ml19" \
  -H "Authorization: Bearer $SURSA_API_KEY"

Các danh sách có từ trước khi có con trỏ và trả về một đối tượng giữ lớp bọc riêng của chúng (ví dụ {"items": […], "total": 120}). Chúng nhận next_cursor và has_more trong cùng đối tượng; các phần tử vẫn ở chỗ cũ.

Danh sách trả về mảng

Một vài danh sách trả về mảng JSON thuần, ví dụ GET /v1/collections. Dạng phản hồi của chúng không thể thay đổi, nên trang tiếp theo được trả về trong header:

curl -i "https://api.sursa.com/v1/collections?limit=50" \
  -H "Authorization: Bearer $SURSA_API_KEY"
HTTP/1.1 200 OK
Link: <https://api.example.com/v1/collections?limit=50&cursor=eyJ2IjoxLCJrIjpbNTBdfQ>; rel="next"
X-Next-Cursor: eyJ2IjoxLCJrIjpbNTBdfQ

[{"id": 1, "name": "support", …}, …]
  • Link với rel="next" là địa chỉ đầy đủ của trang tiếp theo; X-Next-Cursor là chính con trỏ đó.
  • Không có Link với rel="next" và không có X-Next-Cursor nghĩa là đây là trang cuối cùng.
  • Khi không có limit, các danh sách này trả về mọi phần tử trong một phản hồi, như trước nay. Hãy truyền limit để phân trang chúng; con trỏ ghi nhớ kích thước trang, nên yêu cầu tiếp theo có thể bỏ qua limit.
  • GET /v1/cards là ngoại lệ: danh sách này luôn trả về theo trang, mặc định 200 thẻ và tối đa 1000 thẻ với limit, nên hãy theo X-Next-Cursor ngay cả khi không có limit.
// Every item of a list that returns an array.
async function* listArray(path) {
  let next = `https://api.sursa.com${path}?limit=200`;
  while (next) {
    const response = await fetch(next, { headers: { Authorization: `Bearer ${process.env.SURSA_API_KEY}` } });
    if (!response.ok) throw new Error(`HTTP ${response.status}`);
    yield* await response.json();
    const cursor = response.headers.get("X-Next-Cursor");
    next = cursor ? `https://api.sursa.com${path}?cursor=${encodeURIComponent(cursor)}` : null;
  }
}

Nguồn cấp sự kiện

Nguồn cấp sự kiện GET /v1/webhook-events là một nhật ký, nên nó phân trang hơi khác:

  • Sự kiện được sắp xếp tăng dần, cũ nhất trước.
  • next_cursor được trả về trên mọi trang, ngay cả khi has_more là false. Hãy lưu nó, và hỏi lại sau với cùng con trỏ để nhận các sự kiện đã xảy ra kể từ đó. Nguồn cấp là một hàng đợi mà bạn có thể đọc theo nhịp độ của mình.
  • Sự kiện xuất hiện trong nguồn cấp trong khoảng một giây sau thay đổi mà nó mô tả, và không bao giờ xuất hiện trước một sự kiện đã được đọc.
  • Nguồn cấp chứa sự kiện của toàn bộ tổ chức, nên để đọc nó bạn cần một khóa có vai trò admin và phạm vi webhooks:manage.

Xuất sang kho dữ liệu trình bày một vòng lặp thăm dò hoàn chỉnh.

Quy tắc về con trỏ

  • Con trỏ là một chuỗi không trong suốt dài tối đa 512 ký tự. Đừng phân tích hay tự tạo nó: định dạng của nó có thể thay đổi.
  • Con trỏ thuộc về một danh sách và một tập bộ lọc. Con trỏ bị hỏng, đến từ danh sách khác hoặc được gửi kèm bộ lọc khác trả về 400 với mã api.cursor_invalid.
  • Mỗi danh sách có thứ tự sắp xếp cố định kết thúc bằng một khóa duy nhất, nên mỗi phần tử xuất hiện trên đúng một trang.
  • Danh sách được thêm cùng với con trỏ không trả về tổng số; danh sách đã có total từ trước vẫn giữ nó. Hãy tự đếm phần tử trong quá trình đọc nếu bạn cần tổng số.

Phân trang theo offset

Một số danh sách đã chấp nhận offset từ trước khi có con trỏ và vẫn chấp nhận, để các tích hợp hiện có tiếp tục hoạt động. Trong mã mới, hãy ưu tiên cursor. Không thể kết hợp offset và cursor: gửi cả hai trả về 422 với mã validation.failed.