REST API v1 · Developers · API reference
Giveaways API reference
All 6 Giveaways endpoints of the BotGG REST API v1: scopes, parameters, fields, errors and ready-to-run curl, JavaScript and Python examples.
Developer docs
On this page
Endpoints
Paths are relative to https://console.botgg.app/api/v1. Send your API key as Authorization: Bearer botgg_…; IDs are strings.
| Endpoint | Scope | Description |
|---|---|---|
GET/guilds/{guildId}/giveaways | giveaways:read | List giveaways |
POST/guilds/{guildId}/giveaways | giveaways:write | Start a giveaway |
GET/guilds/{guildId}/giveaways/{giveawayId} | giveaways:read | Get a giveaway |
GET/guilds/{guildId}/giveaways/{giveawayId}/entries | giveaways:read | List a giveaway’s entrants |
POST/guilds/{guildId}/giveaways/{giveawayId}/end | giveaways:write | End a giveaway now |
POST/guilds/{guildId}/giveaways/{giveawayId}/reroll | giveaways:write | Reroll winners |
Besides its own errors below, every endpoint can return these common ones:
List giveaways
GET/guilds/{guildId}/giveaways
status: active, scheduled, ended, cancelled or open (= scheduled + active). Running ones by end time, others newest first.
Needs this feature enabled on the server: Giveaways
| Name | In | Type | Required | Description |
|---|---|---|---|---|
guildId | path | string | Yes | Your server’s ID · Discord ID (string) |
status | query | string | No | One of scheduled, active, ended, cancelled, open |
page | query | integer | No | 1–10,000 · Default 1 |
limit | query | integer | No | 1–100 · Default 25 |
Response
| Field | Type | Required | Description |
|---|---|---|---|
items | array of object | Yes | |
items[].id | integer | Yes | |
items[].status | string | Yes | One of scheduled, active, ended, cancelled |
items[].prize | string | Yes | |
items[].description | string | null | Yes | |
items[].channelId | string | Yes | |
items[].messageUrl | string | null | Yes | null until the bot posted it |
items[].winnersCount | integer | Yes | |
items[].startsAt | string | Yes | |
items[].endsAt | string | Yes | |
items[].endedAt | string | null | Yes | |
items[].entrants | integer | Yes | |
items[].entries | integer | Yes | Tickets incl. bonus entries |
items[].winnerIds | array of string | Yes | |
items[].hostId | string | Yes | |
items[].requirements | object | Yes | |
items[].pingRoleId | string | null | Yes | |
items[].dmWinners | boolean | Yes | |
items[].createdAt | string | Yes | |
total | integer | Yes | |
page | integer | Yes | |
nextPage | integer | null | Yes |
{
"items": [
{
"id": 98765,
"status": "scheduled",
"prize": "Nitro",
"description": "example",
"channelId": "234567890123456789",
"messageUrl": "https://example.com",
"winnersCount": 10,
"startsAt": "2026-10-07T12:00:00.000Z",
"endsAt": "2026-10-07T12:00:00.000Z",
"endedAt": "2026-10-07T12:00:00.000Z",
"entrants": 42,
"entries": 10,
"winnerIds": [
"234567890123456789"
],
"hostId": "234567890123456789",
"requirements": {},
"pingRoleId": "234567890123456789",
"dmWinners": true,
"createdAt": "2026-10-07T12:00:00.000Z"
}
],
"total": 10,
"page": 10,
"nextPage": 10
}Examples
curl -s "https://console.botgg.app/api/v1/guilds/123456789012345678/giveaways?page=1&limit=25" \
-H "Authorization: Bearer $BOTGG_API_KEY"// Node.js 18+ (built-in fetch) — on your server, never in a browser
const res = await fetch('https://console.botgg.app/api/v1/guilds/123456789012345678/giveaways?page=1&limit=25', {
method: 'GET',
headers: {
Authorization: `Bearer ${process.env.BOTGG_API_KEY}`,
},
});
const data = await res.json();
if (!res.ok) throw new Error(`${res.status} ${data.error}: ${data.message}`);
console.log(data);import os
import requests
res = requests.get(
"https://console.botgg.app/api/v1/guilds/123456789012345678/giveaways",
headers={
"Authorization": f"Bearer {os.environ['BOTGG_API_KEY']}",
},
params={
"page": 1,
"limit": 25,
},
timeout=10,
)
data = res.json()
if not res.ok:
raise RuntimeError(f"{res.status_code} {data['error']}: {data['message']}")
print(data)Errors
| HTTP | Codes |
|---|---|
| 409 | FEATURE_DISABLED |
| 503 | SERVICE_UNAVAILABLE |
Start a giveaway
POST/guilds/{guildId}/giveaways
Saved as scheduled; the bot posts it in the channel within ~10 s (or at startsAt). Same rules as the console editor. Look and DM setting default to the server’s Giveaways settings.
Needs this feature enabled on the server: Giveaways
| Name | In | Type | Required | Description |
|---|---|---|---|---|
guildId | path | string | Yes | Your server’s ID · Discord ID (string) |
Idempotency-Key | header | string | No | Optional. Send a unique value (a UUID) and reuse it when you retry: the change happens only once. How it works |
Request body
| Field | Type | Required | Description |
|---|---|---|---|
channelId | string | Yes | Discord ID (string) |
prize | string | Yes | 1–200 characters |
description | string | null | No | up to 1,000 characters |
winners | integer | No | Default 1 1–50 |
startsAt | string | null | No | ISO 8601; omitted / null = start now ISO 8601 date-time (UTC) |
endsAt | string | No | ISO 8601; or send durationSeconds ISO 8601 date-time (UTC) |
durationSeconds | integer | No | From the start; or send endsAt 60–5,184,000 |
requirements | object | No | Required / blocked roles, account and member age, level, entry cost, bonus roles |
requirements.roleIds | array of string | No | up to 20 items · Default [] |
requirements.roleMode | string | No | One of any, all · Default "any" |
requirements.blockedRoleIds | array of string | No | up to 20 items · Default [] |
requirements.minAccountAgeDays | integer | No | 0–3,650 · Default 0 |
requirements.minMemberDays | integer | No | 0–3,650 · Default 0 |
requirements.minLevel | integer | No | 0–1,000 · Default 0 |
requirements.entryCost | integer | No | 0–1,000,000,000 · Default 0 |
requirements.bonusRoles | array of object | No | up to 10 items · Default [] |
requirements.bonusRoles[].roleId | string | Yes | Discord ID (string) |
requirements.bonusRoles[].entries | integer | Yes | 2–10 |
embed | object | No | Default: the server’s Giveaways look |
embed.color | string | No | Default "#D94CFF" |
embed.endedColor | string | No | Default "#4E5058" |
embed.emoji | string | No | 1–64 characters · Default "🎉" |
embed.imageUrl | string | null | No | up to 500 characters · Default null |
embed.thumbnailUrl | string | null | No | up to 500 characters · Default null |
pingRoleId | string | null | No | Discord ID (string) |
dmWinners | boolean | No | Default: the server setting |
hostId | string | null | No | Shown as the host; default: the API key’s creator Discord ID (string) |
Response
| Field | Type | Required | Description |
|---|---|---|---|
id | integer | Yes | |
status | string | Yes | One of scheduled, active, ended, cancelled |
prize | string | Yes | |
description | string | null | Yes | |
channelId | string | Yes | |
messageUrl | string | null | Yes | null until the bot posted it |
winnersCount | integer | Yes | |
startsAt | string | Yes | |
endsAt | string | Yes | |
endedAt | string | null | Yes | |
entrants | integer | Yes | |
entries | integer | Yes | Tickets incl. bonus entries |
winnerIds | array of string | Yes | |
hostId | string | Yes | |
requirements | object | Yes | |
pingRoleId | string | null | Yes | |
dmWinners | boolean | Yes | |
createdAt | string | Yes |
{
"id": 98765,
"status": "scheduled",
"prize": "Nitro",
"description": "example",
"channelId": "234567890123456789",
"messageUrl": "https://example.com",
"winnersCount": 10,
"startsAt": "2026-10-07T12:00:00.000Z",
"endsAt": "2026-10-07T12:00:00.000Z",
"endedAt": "2026-10-07T12:00:00.000Z",
"entrants": 42,
"entries": 10,
"winnerIds": [
"234567890123456789"
],
"hostId": "234567890123456789",
"requirements": {},
"pingRoleId": "234567890123456789",
"dmWinners": true,
"createdAt": "2026-10-07T12:00:00.000Z"
}Examples
curl -s -X POST "https://console.botgg.app/api/v1/guilds/123456789012345678/giveaways" \
-H "Authorization: Bearer $BOTGG_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"channelId":"234567890123456789","prize":"Nitro","description":"example","winners":1,"startsAt":"2026-10-07T12:00:00.000Z","endsAt":"2026-10-07T12:00:00.000Z","durationSeconds":3600,"requirements":{"roleIds":[],"roleMode":"any","blockedRoleIds":[],"minAccountAgeDays":0,"minMemberDays":0,"minLevel":0,"entryCost":0,"bonusRoles":[]},"embed":{"color":"#D94CFF","endedColor":"#4E5058","emoji":"🎉","imageUrl":"https://example.com","thumbnailUrl":"https://example.com"},"pingRoleId":"234567890123456789","dmWinners":true,"hostId":"234567890123456789"}'// Node.js 18+ (built-in fetch) — on your server, never in a browser
const res = await fetch('https://console.botgg.app/api/v1/guilds/123456789012345678/giveaways', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.BOTGG_API_KEY}`,
'Content-Type': 'application/json',
'Idempotency-Key': crypto.randomUUID(), // reuse it when you retry this request
},
body: JSON.stringify({
channelId: '234567890123456789',
prize: 'Nitro',
description: 'example',
winners: 1,
startsAt: '2026-10-07T12:00:00.000Z',
endsAt: '2026-10-07T12:00:00.000Z',
durationSeconds: 3600,
requirements: {
roleIds: [],
roleMode: 'any',
blockedRoleIds: [],
minAccountAgeDays: 0,
minMemberDays: 0,
minLevel: 0,
entryCost: 0,
bonusRoles: [],
},
embed: {
color: '#D94CFF',
endedColor: '#4E5058',
emoji: '🎉',
imageUrl: 'https://example.com',
thumbnailUrl: 'https://example.com',
},
pingRoleId: '234567890123456789',
dmWinners: true,
hostId: '234567890123456789',
}),
});
const data = await res.json();
if (!res.ok) throw new Error(`${res.status} ${data.error}: ${data.message}`);
console.log(data);import os, uuid
import requests
res = requests.post(
"https://console.botgg.app/api/v1/guilds/123456789012345678/giveaways",
headers={
"Authorization": f"Bearer {os.environ['BOTGG_API_KEY']}",
"Idempotency-Key": str(uuid.uuid4()), # reuse it when you retry this request
},
json={
"channelId": "234567890123456789",
"prize": "Nitro",
"description": "example",
"winners": 1,
"startsAt": "2026-10-07T12:00:00.000Z",
"endsAt": "2026-10-07T12:00:00.000Z",
"durationSeconds": 3600,
"requirements": {
"roleIds": [],
"roleMode": "any",
"blockedRoleIds": [],
"minAccountAgeDays": 0,
"minMemberDays": 0,
"minLevel": 0,
"entryCost": 0,
"bonusRoles": [],
},
"embed": {
"color": "#D94CFF",
"endedColor": "#4E5058",
"emoji": "🎉",
"imageUrl": "https://example.com",
"thumbnailUrl": "https://example.com",
},
"pingRoleId": "234567890123456789",
"dmWinners": True,
"hostId": "234567890123456789",
},
timeout=10,
)
data = res.json()
if not res.ok:
raise RuntimeError(f"{res.status_code} {data['error']}: {data['message']}")
print(data)Errors
| HTTP | Codes |
|---|---|
| 400 | INVALID_JSON |
| 402 | PLAN_LIMIT |
| 409 | FEATURE_DISABLED, BANK_DISABLED |
| 413 | PAYLOAD_TOO_LARGE |
| 415 | UNSUPPORTED_MEDIA_TYPE |
| 422 | IDEMPOTENCY_KEY_REUSED |
| 503 | SERVICE_UNAVAILABLE |
Get a giveaway
GET/guilds/{guildId}/giveaways/{giveawayId}
With entry counts, winners per draw and the latest queued actions (to follow an end / reroll).
Needs this feature enabled on the server: Giveaways
| Name | In | Type | Required | Description |
|---|---|---|---|---|
guildId | path | string | Yes | Your server’s ID · Discord ID (string) |
giveawayId | path | string | Yes |
Response
| Field | Type | Required | Description |
|---|---|---|---|
id | integer | Yes | |
status | string | Yes | One of scheduled, active, ended, cancelled |
prize | string | Yes | |
description | string | null | Yes | |
channelId | string | Yes | |
messageUrl | string | null | Yes | null until the bot posted it |
winnersCount | integer | Yes | |
startsAt | string | Yes | |
endsAt | string | Yes | |
endedAt | string | null | Yes | |
entrants | integer | Yes | |
entries | integer | Yes | Tickets incl. bonus entries |
winnerIds | array of string | Yes | |
hostId | string | Yes | |
requirements | object | Yes | |
pingRoleId | string | null | Yes | |
dmWinners | boolean | Yes | |
createdAt | string | Yes | |
winners | array of object | Yes | |
winners[].userId | string | Yes | |
winners[].draw | integer | Yes | 0 = the end draw, 1.. = rerolls |
winners[].drawnAt | string | Yes | |
actions | array of object | Yes | The latest queued actions (end / reroll / …), newest first |
actions[].id | integer | Yes | |
actions[].action | string | Yes | |
actions[].status | string | Yes | One of pending, processing, done, failed |
actions[].error | string | null | Yes | |
actions[].createdAt | string | Yes | |
actions[].processedAt | string | null | Yes |
{
"id": 98765,
"status": "scheduled",
"prize": "Nitro",
"description": "example",
"channelId": "234567890123456789",
"messageUrl": "https://example.com",
"winnersCount": 10,
"startsAt": "2026-10-07T12:00:00.000Z",
"endsAt": "2026-10-07T12:00:00.000Z",
"endedAt": "2026-10-07T12:00:00.000Z",
"entrants": 42,
"entries": 10,
"winnerIds": [
"234567890123456789"
],
"hostId": "234567890123456789",
"requirements": {},
"pingRoleId": "234567890123456789",
"dmWinners": true,
"createdAt": "2026-10-07T12:00:00.000Z",
"winners": [
{
"userId": "876543210987654321",
"draw": 10,
"drawnAt": "2026-10-07T12:00:00.000Z"
}
],
"actions": [
{
"id": 98765,
"action": "example",
"status": "pending",
"error": "example",
"createdAt": "2026-10-07T12:00:00.000Z",
"processedAt": "2026-10-07T12:00:00.000Z"
}
]
}Examples
curl -s "https://console.botgg.app/api/v1/guilds/123456789012345678/giveaways/234567890123456789" \
-H "Authorization: Bearer $BOTGG_API_KEY"// Node.js 18+ (built-in fetch) — on your server, never in a browser
const res = await fetch('https://console.botgg.app/api/v1/guilds/123456789012345678/giveaways/234567890123456789', {
method: 'GET',
headers: {
Authorization: `Bearer ${process.env.BOTGG_API_KEY}`,
},
});
const data = await res.json();
if (!res.ok) throw new Error(`${res.status} ${data.error}: ${data.message}`);
console.log(data);import os
import requests
res = requests.get(
"https://console.botgg.app/api/v1/guilds/123456789012345678/giveaways/234567890123456789",
headers={
"Authorization": f"Bearer {os.environ['BOTGG_API_KEY']}",
},
timeout=10,
)
data = res.json()
if not res.ok:
raise RuntimeError(f"{res.status_code} {data['error']}: {data['message']}")
print(data)Errors
| HTTP | Codes |
|---|---|
| 404 | GIVEAWAY_NOT_FOUND |
| 409 | FEATURE_DISABLED |
| 503 | SERVICE_UNAVAILABLE |
List a giveaway’s entrants
GET/guilds/{guildId}/giveaways/{giveawayId}/entries
Ordered by user id. Pass after=<nextCursor> for the next page. weight = tickets (bonus roles), paid = entry cost paid.
Needs this feature enabled on the server: Giveaways
| Name | In | Type | Required | Description |
|---|---|---|---|---|
guildId | path | string | Yes | Your server’s ID · Discord ID (string) |
giveawayId | path | string | Yes | |
limit | query | integer | No | 1–100 · Default 50 |
after | query | string | No | Discord ID (string) |
Response
| Field | Type | Required | Description |
|---|---|---|---|
items | array of object | Yes | |
items[].userId | string | Yes | |
items[].weight | integer | Yes | |
items[].paid | integer | Yes | |
items[].enteredAt | string | Yes | |
nextCursor | string | null | Yes |
{
"items": [
{
"userId": "876543210987654321",
"weight": 10,
"paid": 10,
"enteredAt": "2026-10-07T12:00:00.000Z"
}
],
"nextCursor": "98700"
}Examples
curl -s "https://console.botgg.app/api/v1/guilds/123456789012345678/giveaways/234567890123456789/entries?limit=50" \
-H "Authorization: Bearer $BOTGG_API_KEY"// Node.js 18+ (built-in fetch) — on your server, never in a browser
const res = await fetch('https://console.botgg.app/api/v1/guilds/123456789012345678/giveaways/234567890123456789/entries?limit=50', {
method: 'GET',
headers: {
Authorization: `Bearer ${process.env.BOTGG_API_KEY}`,
},
});
const data = await res.json();
if (!res.ok) throw new Error(`${res.status} ${data.error}: ${data.message}`);
console.log(data);import os
import requests
res = requests.get(
"https://console.botgg.app/api/v1/guilds/123456789012345678/giveaways/234567890123456789/entries",
headers={
"Authorization": f"Bearer {os.environ['BOTGG_API_KEY']}",
},
params={
"limit": 50,
},
timeout=10,
)
data = res.json()
if not res.ok:
raise RuntimeError(f"{res.status_code} {data['error']}: {data['message']}")
print(data)Errors
| HTTP | Codes |
|---|---|
| 404 | GIVEAWAY_NOT_FOUND |
| 409 | FEATURE_DISABLED |
| 503 | SERVICE_UNAVAILABLE |
End a giveaway now
POST/guilds/{guildId}/giveaways/{giveawayId}/end
Queued for the bot, which draws the winners and announces them within ~5 s (202). Follow it with GET …/giveaways/{giveawayId}.
Needs this feature enabled on the server: Giveaways
| Name | In | Type | Required | Description |
|---|---|---|---|---|
guildId | path | string | Yes | Your server’s ID · Discord ID (string) |
giveawayId | path | string | Yes | |
Idempotency-Key | header | string | No | Optional. Send a unique value (a UUID) and reuse it when you retry: the change happens only once. How it works |
Response
| Field | Type | Required | Description |
|---|---|---|---|
giveawayId | integer | Yes | |
action | object | Yes | |
action.id | integer | Yes | |
action.action | string | Yes | |
action.status | string | Yes | One of pending, processing, done, failed |
action.error | string | null | Yes | |
action.createdAt | string | Yes | |
action.processedAt | string | null | Yes |
{
"giveawayId": 98765,
"action": {
"id": 98765,
"action": "example",
"status": "pending",
"error": "example",
"createdAt": "2026-10-07T12:00:00.000Z",
"processedAt": "2026-10-07T12:00:00.000Z"
}
}Examples
curl -s -X POST "https://console.botgg.app/api/v1/guilds/123456789012345678/giveaways/234567890123456789/end" \
-H "Authorization: Bearer $BOTGG_API_KEY" \
-H "Idempotency-Key: $(uuidgen)"// Node.js 18+ (built-in fetch) — on your server, never in a browser
const res = await fetch('https://console.botgg.app/api/v1/guilds/123456789012345678/giveaways/234567890123456789/end', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.BOTGG_API_KEY}`,
'Idempotency-Key': crypto.randomUUID(), // reuse it when you retry this request
},
});
const data = await res.json();
if (!res.ok) throw new Error(`${res.status} ${data.error}: ${data.message}`);
console.log(data);import os, uuid
import requests
res = requests.post(
"https://console.botgg.app/api/v1/guilds/123456789012345678/giveaways/234567890123456789/end",
headers={
"Authorization": f"Bearer {os.environ['BOTGG_API_KEY']}",
"Idempotency-Key": str(uuid.uuid4()), # reuse it when you retry this request
},
timeout=10,
)
data = res.json()
if not res.ok:
raise RuntimeError(f"{res.status_code} {data['error']}: {data['message']}")
print(data)Errors
| HTTP | Codes |
|---|---|
| 404 | GIVEAWAY_NOT_FOUND |
| 409 | FEATURE_DISABLED, GIVEAWAY_BAD_STATE |
| 422 | IDEMPOTENCY_KEY_REUSED |
| 503 | SERVICE_UNAVAILABLE |
Reroll winners
POST/guilds/{guildId}/giveaways/{giveawayId}/reroll
For an ended giveaway: the bot draws count new winners (never someone who already won) within ~5 s (202).
Needs this feature enabled on the server: Giveaways
| Name | In | Type | Required | Description |
|---|---|---|---|---|
guildId | path | string | Yes | Your server’s ID · Discord ID (string) |
giveawayId | path | string | Yes | |
Idempotency-Key | header | string | No | Optional. Send a unique value (a UUID) and reuse it when you retry: the change happens only once. How it works |
Request body
| Field | Type | Required | Description |
|---|---|---|---|
count | integer | No | Default 1 1–20 |
Response
| Field | Type | Required | Description |
|---|---|---|---|
giveawayId | integer | Yes | |
action | object | Yes | |
action.id | integer | Yes | |
action.action | string | Yes | |
action.status | string | Yes | One of pending, processing, done, failed |
action.error | string | null | Yes | |
action.createdAt | string | Yes | |
action.processedAt | string | null | Yes |
{
"giveawayId": 98765,
"action": {
"id": 98765,
"action": "example",
"status": "pending",
"error": "example",
"createdAt": "2026-10-07T12:00:00.000Z",
"processedAt": "2026-10-07T12:00:00.000Z"
}
}Examples
curl -s -X POST "https://console.botgg.app/api/v1/guilds/123456789012345678/giveaways/234567890123456789/reroll" \
-H "Authorization: Bearer $BOTGG_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"count":12}'// Node.js 18+ (built-in fetch) — on your server, never in a browser
const res = await fetch('https://console.botgg.app/api/v1/guilds/123456789012345678/giveaways/234567890123456789/reroll', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.BOTGG_API_KEY}`,
'Content-Type': 'application/json',
'Idempotency-Key': crypto.randomUUID(), // reuse it when you retry this request
},
body: JSON.stringify({
count: 12,
}),
});
const data = await res.json();
if (!res.ok) throw new Error(`${res.status} ${data.error}: ${data.message}`);
console.log(data);import os, uuid
import requests
res = requests.post(
"https://console.botgg.app/api/v1/guilds/123456789012345678/giveaways/234567890123456789/reroll",
headers={
"Authorization": f"Bearer {os.environ['BOTGG_API_KEY']}",
"Idempotency-Key": str(uuid.uuid4()), # reuse it when you retry this request
},
json={
"count": 12,
},
timeout=10,
)
data = res.json()
if not res.ok:
raise RuntimeError(f"{res.status_code} {data['error']}: {data['message']}")
print(data)Errors
| HTTP | Codes |
|---|---|
| 400 | INVALID_JSON |
| 404 | GIVEAWAY_NOT_FOUND |
| 409 | FEATURE_DISABLED, GIVEAWAY_BAD_STATE |
| 413 | PAYLOAD_TOO_LARGE |
| 415 | UNSUPPORTED_MEDIA_TYPE |
| 422 | IDEMPOTENCY_KEY_REUSED |
| 503 | SERVICE_UNAVAILABLE |