본문으로 건너뛰기

후원 내역 조회

현재 방송에서 받은 후원 스티커 내역을 조회해요. 이벤트 스트림의 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} 형식이에요.

쿼리 파라미터​

이름타입필수설명
cursorstring이전 응답의 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설명
donationsobject[]아니오현재 방송에서 받은 후원이에요.
donations[].userobject아니오후원한 사람이에요.
donations[].user.idstring아니오후원한 사람을 가리키는 값이에요. 암호화한 값이에요.
donations[].user.nicknamestring아니오닉네임이에요.
donations[].stickerNamestring아니오스티커의 표시 이름이에요. 앱 화면에 보이는 이름 그대로예요.
donations[].amountnumber아니오이번 후원의 총 스푼이에요. 이벤트 스트림 donation 의 amount 와 같은 값이에요.
donations[].timestring아니오후원 시각이에요. ISO-8601 UTC(Z) 예요.
nextCursorstring예다음 장의 커서예요. 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= 에 넣으면 안 돼요 — 반복을 멈추는 신호예요.

에러​

HTTPdetailCode무슨 일인가요어떻게 하나요
400OAPI_MNGR_0103cursor 가 올바르지 않아요. 앞 응답의 nextCursor 가 아니에요.cursor 없이 첫 장부터 다시 받아요.
401없음토큰이 없거나 무효해요.DJ 에게 재연동을 안내해요.
403없음donations.read 권한이 없어요.권한을 추가해 재동의를 받아요.
404OAPI_MNGR_0301방송 중이 아니에요.오류가 아니라 상태예요. 잠시 뒤 다시 불러요.
502OAPI_MNGR_2302방송 서버 오류예요.백오프 후 재시도해요.

이 표에 없는 상태도 올 수 있어요. 잘못된 메서드·Content-Type(400 · 405 · 415, OAPI_MNGR_0103) · 호출 한도(429, 본문 없음) · 서버 오류(500, OAPI_MNGR_1002)는 모든 엔드포인트에 공통이에요 — 에러 코드에 있어요.

관련 문서​