페이지네이션
Sursa API의 목록은 커서로 페이지를 나눠요. 최대 limit개의 항목을 요청하면 응답이 다음 페이지를 받는 방법을 알려
줘요. 오프셋을 계산할 필요가 없고, 그 사이에 데이터가 바뀌어도 페이지 사이에서 항목이 중복되거나 빠지지 않아요.
| 파라미터 | 값 |
|---|---|
limit | 페이지당 항목 수. 보통 1~200이에요. 새 목록의 기본값은 50이며, 목록별 범위와 기본값은 레퍼런스에 나와 있어요. |
cursor | 이전 페이지에서 받은 커서. 첫 페이지에서는 생략하세요. |
목록은 응답 형태에 따라 두 가지 방식 중 하나로 다음 커서를 반환해요. 어느 방식을 쓰는지는 API 레퍼런스의 각 엔드포인트 페이지에 나와 있어요.
data로 감싼 목록
이벤트 피드처럼 공개 API를 위해 추가된 목록은 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
}
has_more가 true이면 cursor에 next_cursor 값을 넣고 같은 필터로 다음 페이지를 요청하세요.
curl "https://api.sursa.com/v1/webhook-events?limit=2&cursor=eyJ2IjoxLCJrIjpbMTA0Ml19" \
-H "Authorization: Bearer $SURSA_API_KEY"커서가 도입되기 전부터 있던 목록 중 객체를 반환하는 목록은 고유한 래퍼를 그대로 써요(예:
{"items": […], "total": 120}). 이런 목록은 같은 객체 안에 next_cursor와 has_more가 추가되고, 항목은 원래 자리에 그대로 있어요.
배열을 반환하는 목록
몇몇 목록은 GET /v1/collections처럼 순수 JSON 배열을 반환해요. 응답
형태를 바꿀 수 없으므로 다음 페이지 정보는 헤더로 와요.
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", …}, …]
rel="next"가 있는Link헤더는 다음 페이지의 전체 주소예요.X-Next-Cursor헤더에는 같은 커서만 따로 담겨요.rel="next"가 있는Link헤더도,X-Next-Cursor헤더도 없으면 마지막 페이지예요.limit없이 호출하면 이 목록은 예전처럼 모든 항목을 한 번의 응답으로 반환해요. 페이지로 나누려면limit값을 보내세요. 커서가 페이지 크기를 기억하므로 다음 요청에서는limit값을 생략해도 돼요.GET /v1/cards는 예외예요. 이 목록은 항상 페이지로 나누어 반환하며, 기본값은 카드 200장이고limit으로 최대 1000장까지 늘릴 수 있어요. 그러니limit없이도X-Next-Cursor헤더를 따라가세요.
// 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;
}
}이벤트 피드
이벤트 피드 GET /v1/webhook-events는 로그이므로 페이지 방식이 조금
달라요.
- 이벤트는 오래된 것부터 오름차순으로 정렬돼요.
next_cursor는has_more가false일 때도 모든 페이지에서 반환돼요. 이 값을 저장해 두었다가 나중에 같은 커서로 다시 요청하면 그 뒤에 일어난 이벤트를 받을 수 있어요. 피드는 원하는 속도로 읽을 수 있는 대기열이에요.- 이벤트는 해당 변경이 일어난 뒤 약 1초 안에 피드에 나타나며, 이미 읽은 이벤트보다 앞에 나타나는 일은 없어요.
- 피드에는 조직 전체의 이벤트가 담기므로, 피드를 읽으려면
admin역할과webhooks:manage스코프가 있는 키가 필요해요.
완전한 폴링 루프는 데이터 웨어하우스로 내보내기에서 볼 수 있어요.
커서 규칙
- 커서는 최대 512자의 불투명한 문자열이에요. 형식이 바뀔 수 있으니 파싱하거나 직접 만들지 마세요.
- 커서는 하나의 목록과 하나의 필터 조합에 속해요. 손상되었거나, 다른 목록에서 왔거나, 다른 필터와 함께 보낸 커서는
400상태와api.cursor_invalid코드를 반환해요. - 각 목록에는 고유한 키로 끝나는 고정된 정렬 순서가 있어서, 모든 항목은 정확히 한 페이지에만 나타나요.
- 커서와 함께 추가된 목록은 전체 개수를 반환하지 않아요. 전부터
total값이 있던 목록은 그 값을 계속 반환해요. 전체 개수가 필요하면 읽으면서 항목을 세세요.
오프셋 페이지네이션
일부 목록은 커서가 도입되기 전에 offset을 받았고, 기존 연동이 계속 동작하도록 지금도 받아요. 새 코드에서는
cursor를 쓰세요. offset과 cursor는 함께 쓸 수 없으며, 둘 다 보내면 422 상태와
validation.failed 코드를 반환해요.