후원 내역 조회
현재 방송에서 받은 후원 스티커 내역을 조회해요. 이벤트 스트림의 donation 은 실시간 한 번뿐이라, 봇이 꺼져 있던 동안의 후원을 복구할 때 이 API 를 써요.
| 메서드 · 경로 | GET /v1/live/donations |
| 필요한 권한 | donations.read |
| 성공 응답 | 200 |
| 방송 중이 아닐 때 | 404 (OAPI_MNGR_0301) |
후원이 많은 방송은 한 번에 다 주지 않고 커서로 이어 받아요.
export SPOON_BASE_URL="https://kr-openapi.spooncast.net"
export SPOON_ACCESS_TOKEN="{발급받은 access token}"
일본 지역이면 https://jp-openapi.spooncast.net 이에요 — 지역별 주소.
요청
GET /v1/live/donations?cursor={cursor}
헤더
| 이름 | 필수 | 설명 |
|---|---|---|
Authorization | ✔ | Bearer {access_token} 형식이에요. |
쿼리 파라미터
| 이름 | 타입 | 필수 | 설명 |
|---|---|---|---|
cursor | string | 이전 응답의 nextCursor 를 그대로 넣어요. 첫 장은 생략해요. |
요청 예시
# 첫 장
curl -H "Authorization: Bearer $SPOON_ACCESS_TOKEN" "$SPOON_BASE_URL/v1/live/donations"
# 다음 장 — 앞 응답의 nextCursor 를 그대로 넣어요
curl -H "Authorization: Bearer $SPOON_ACCESS_TOKEN" \
"$SPOON_BASE_URL/v1/live/donations?cursor=1024307"
응답
{
"donations": [
{
"user": {
"id": "Ab1Cd2Ef3Gh4Ij5Kl6Mn7O",
"nickname": "스푼청취자"
},
"stickerName": "하트",
"amount": 300,
"time": "2026-10-19T05:47:50.512Z"
}
],
"nextCursor": "1024307"
}
| 필드 | 타입 | null | 설명 |
|---|---|---|---|
donations | object[] | 아니오 | 현재 방송에서 받은 후원이에요. |
donations[].user | object | 아니오 | 후원한 사람이에요. |
donations[].user.id | string | 아니오 | 후원한 사람을 가리키는 값이에요. 암호화한 값이에요. |
donations[].user.nickname | string | 아니오 | 닉네임이에요. |
donations[].stickerName | string | 아니오 | 스티커의 표시 이름이에요. 앱 화면에 보이는 이름 그대로예요. |
donations[].amount | number | 아니오 | 이번 후원의 총 스푼이에요. 이벤트 스트림 donation 의 amount 와 같은 값이에요. |
donations[].time | string | 아니오 | 후원 시각이에요. ISO-8601 UTC(Z) 예요. |
nextCursor | string | 예 | 다음 장의 커서예요. null 이면 마지막 장이에요. |
user.id 로 사람을 식별해요user.id 는 이 사람의 고유 식별자예요. 같은 사람이면 항상 같은 값이라 출석 · 누적 · 랭킹의
키로 쓰면 돼요. user.nickname 은 바뀌고 중복될 수 있으니 식별에 쓰지 마세요.
값의 범위는 내 봇과 그 DJ 조합이에요. DJ 가 연동을 끊었다 다시 동의해도 유지돼요.
이 user 는 이벤트 스트림의 user 와 같은 모양, 같은 id 예요. 실시간으로 받아 둔
후원과 이 내역을 같은 키로 합칠 수 있어요.
코드
// donations.ts — node --experimental-strip-types donations.ts
interface Donation {
user: { id: string; nickname: string };
stickerName: string;
amount: number;
time: string;
}
interface DonationsPage {
donations: Donation[];
nextCursor: string | null;
}
const BASE = process.env.SPOON_BASE_URL;
const TOKEN = process.env.SPOON_ACCESS_TOKEN;
const all = [];
let cursor;
do {
const url = new URL(`${BASE}/v1/live/donations`);
if (cursor) url.searchParams.set('cursor', cursor);
const res = await fetch(url, { headers: { Authorization: `Bearer ${TOKEN}` } });
if (!res.ok) throw new Error(`후원 내역 조회 실패: ${res.status}`);
const page = (await res.json()) as DonationsPage;
all.push(...page.donations);
cursor = page.nextCursor; // null 이면 반복이 끝나요
} while (cursor);
const total = all.reduce((sum, d) => sum + d.amount, 0);
console.log(`이번 방송 후원 ${all.length}건, 총 ${total} 스푼이에요.`);
// 재시작 복구 — 봇이 저장해 둔 마지막 처리 시각 이후만 다시 처리해요
const lastProcessedTime = '2026-10-19T05:00:00.000Z';
const missed = all.filter((d) => d.time > lastProcessedTime);
console.log(`놓친 후원 ${missed.length}건을 다시 처리해요.`);
node --experimental-strip-types donations.ts
주의
현재 방송 범위예요. 방송이 끝나면 그 방송의 내역은 조회할 수 없고, 다음 방송에서는 다음 방송 것만 와요. 방송을 넘어 쌓으려면 봇이 직접 저장해요.
한마디는 없어요. 후원과 함께 온 한마디(message)는
이벤트 스트림 donation 에만 실려요. 한마디가 필요한
기능은 실시간으로 받아 저장해요.
평소에는 이벤트 스트림으로 받아요. 이 API 는 복구 용도예요 — 재접속했을 때 그 사이
내역을 당겨와 메우고, 같은 후원이 양쪽에서 겹치면 user.id 와 time 조합으로 걸러요.
nextCursor 가 null 일 때까지 반복해요. 첫 장만 받고 끝내면 후원이 많은 방송에서
일부만 보게 돼요. 반대로 null 을 그대로 ?cursor= 에 넣으면 안 돼요 — 반복을 멈추는 신호예요.
에러
| HTTP | detailCode | 무슨 일인가요 | 어떻게 하나요 |
|---|---|---|---|
400 | OAPI_MNGR_0103 | cursor 가 올바르지 않아요. 앞 응답의 nextCursor 가 아니에요. | cursor 없이 첫 장부터 다시 받아요. |
401 | 없음 | 토큰이 없거나 무효해요. | DJ 에게 재연동을 안내해요. |
403 | 없음 | donations.read 권한이 없어요. | 권한을 추가해 재동의를 받아요. |
404 | OAPI_MNGR_0301 | 방송 중이 아니에요. | 오류가 아니라 상태예요. 잠시 뒤 다시 불러요. |
502 | OAPI_MNGR_2302 | 방송 서버 오류예요. | 백오프 후 재시도해요. |
이 표에 없는 상태도 올 수 있어요. 잘못된 메서드·Content-Type(400 · 405 · 415,
OAPI_MNGR_0103) · 호출 한도(429, 본문 없음) · 서버 오류(500, OAPI_MNGR_1002)는
모든 엔드포인트에 공통이에요 — 에러 코드에 있어요.
관련 문서
- 엔드포인트 목록 — 전체 엔드포인트 목록이에요.
- 이벤트 스트림 · donation — 실시간 후원과 한마디예요.
- 팬 랭킹 조회 — 지금 방에 있는 사람의 누적 후원 순위예요.
- 에러 코드 — 전체 에러 코드 목록이에요.