Read the fan ranking
The top 30 donors of this broadcast, among the listeners currently in the room. It resets when the broadcast ends.
| Method and path | GET /v1/live/fans |
| Permission | fans.read |
| Success | 200 |
| When not on air | 404 (OAPI_MNGR_0301) |
export SPOON_BASE_URL="https://kr-openapi.spooncast.net"
export SPOON_ACCESS_TOKEN="{your access token}"
For the Japan region use https://jp-openapi.spooncast.net — see regions.
Request
GET /v1/live/fans
Headers
| Name | Required | Description |
|---|---|---|
Authorization | ✔ | Bearer {access_token}. |
There are no path parameters, query parameters, or body. There is no paging either — the top 30 arrive in one call.
Request example
curl -H "Authorization: Bearer $SPOON_ACCESS_TOKEN" "$SPOON_BASE_URL/v1/live/fans"
Response
{
"totalCount": 2,
"fans": [
{ "id": "Ab1Cd2Ef3Gh4Ij5Kl6Mn7O", "nickname": "SpoonListener", "rank": 1, "spoonCount": 1200 },
{ "id": "Pq8Rs9Tu0Vw1Xy2Za3Bc4D", "nickname": "RadioFan", "rank": 2, "spoonCount": 300 }
]
}
| Field | Type | Nullable | Description |
|---|---|---|---|
totalCount | number | No | The size of the returned list — at most 30, not the total number of fans. |
fans | object[] | No | Ordered by cumulative donations, highest first. |
fans[].id | string | No | Identifies the listener. An encrypted value. |
fans[].nickname | string | No | The nickname. |
fans[].rank | number | No | The rank, starting at 1. |
fans[].spoonCount | number | Yes | Spoons that listener spent in this broadcast. It can be absent, so handle null. |
idid is this person's unique identifier. The same person always gets the same value, so use
it as the key for attendance, running totals, and rankings. Do not identify people by
nickname — it changes and it repeats.
The value is scoped to your bot and that DJ. It survives a DJ disconnecting and granting consent again.
Code
// fans.ts — node --experimental-strip-types fans.ts
interface Fan {
id: string;
nickname: string;
rank: number;
spoonCount: number | null;
}
interface Fans {
totalCount: number;
fans: Fan[];
}
const res = await fetch(`${process.env.SPOON_BASE_URL}/v1/live/fans`, {
headers: { Authorization: `Bearer ${process.env.SPOON_ACCESS_TOKEN}` },
});
if (!res.ok) throw new Error(`Could not read the fan ranking: ${res.status}`);
const { totalCount, fans } = (await res.json()) as Fans;
console.log(`${totalCount} listeners on the board (max 30)`);
for (const fan of fans.slice(0, 3)) {
const spoons = fan.spoonCount ?? 0; // this can be null
console.log(`#${fan.rank} ${fan.nickname} — ${spoons} spoons`);
}
const top = fans.find((f) => f.rank === 1);
if (top) console.log(`Today's top fan is ${top.nickname} with ${top.spoonCount ?? 0} spoons!`);
node --experimental-strip-types fans.ts
Watch out
The bot account appears on the board too if it has donation history in this broadcast. You may
need to filter its id out.
You get at most 30. There is no paging — past 30th place a ranking stops meaning anything,
so it is not offered. totalCount is the size of the returned list (at most 30), not the
total number of fans. For every listener, use List listeners — that one
covers everyone in the room and pages with a cursor.
Only people currently in the room appear. Someone who donated and left drops off the board, and the order shifts as people come and go. For a whole-broadcast total, accumulate it yourself from the event stream.
The ranking is cumulative within this broadcast, not the DJ's all-time fan ranking.
The rank at the moment someone joins also arrives as fanRank on the
event stream. For a "top fan just joined!" greeting you
do not need to call this endpoint at all.
Errors
| HTTP | detailCode | What happened | What to do |
|---|---|---|---|
401 | none | The token is missing or invalid. | Ask the DJ to authorize again. |
403 | none | The fans.read permission is missing. | Add the permission and get consent again. |
404 | OAPI_MNGR_0301 | The DJ is not on air. | A state, not an error. Call again shortly. |
502 | OAPI_MNGR_2302 | Broadcast server error. | Retry after a backoff. |
States outside this table can arrive too. A wrong method or Content-Type
(400, 405, 415 — OAPI_MNGR_0103), the call quota (429, no body), and server errors
(500 — OAPI_MNGR_1002) are common to every endpoint — see
Error codes.
Related documents
- Endpoint list — the full endpoint list.
- List listeners — everyone in the room, not just the top 30.
- Event stream — accumulate donations yourself.
- Error codes — the full list of error codes.