Skip to main content

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 pathGET /v1/live/fans
Permissionfans.read
Success200
When not on air404 (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​

NameRequiredDescription
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 }
]
}
FieldTypeNullableDescription
totalCountnumberNoThe size of the returned list — at most 30, not the total number of fans.
fansobject[]NoOrdered by cumulative donations, highest first.
fans[].idstringNoIdentifies the listener. An encrypted value.
fans[].nicknamestringNoThe nickname.
fans[].ranknumberNoThe rank, starting at 1.
fans[].spoonCountnumberYesSpoons that listener spent in this broadcast. It can be absent, so handle null.
Identify people by id

id 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​

HTTPdetailCodeWhat happenedWhat to do
401noneThe token is missing or invalid.Ask the DJ to authorize again.
403noneThe fans.read permission is missing.Add the permission and get consent again.
404OAPI_MNGR_0301The DJ is not on air.A state, not an error. Call again shortly.
502OAPI_MNGR_2302Broadcast 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.