API reference

OpenThe API answers at https://api.donutapi.app, and you do not need a key to try it. API keys and paid plans are not available yet.
ContextDonutSMP disabled its official API in 2026. Donut API is an independent, unofficial replacement. What happened to the official API?

Building with an AI agent?

Copy a prompt that describes every route, its parameters and its response format, ready to paste into Claude Code, Cursor or ChatGPT.

Open in Claude Open in ChatGPT llms.txt

Overview

The base URL is https://api.donutapi.app. Every route is a read only GET request that returns JSON, and the API can be called from a browser (CORS is open).

Data comes from bots in the game that read the auction house and the buy orders every few minutes. Items are identified by their Minecraft ids, such as diamond or netherite_ingot. Fields named at are Unix time in milliseconds.

The auction house and orders snapshots are partial: the bots read a capped number of pages, cheapest first. Each response carries a snapshot object, and snapshot.complete is false when the cap was reached. An item that only has expensive listings can be missing from a snapshot.

curl first call
curl "https://api.donutapi.app/v1/ah/items"

What happened to the official DonutSMP API?

DonutSMP used to give API keys to its players through the in-game /api command. In 2026 that API was restricted, then disabled.

WhenWhat
July 3, 2026DonutSMP staff announced in their updates channel that keys were now limited to ranked players: "For abuse & safety reasons from now on API keys are only available to users with a rank, if you are a normal player and you wish to get an API key you will need to apply for it in the support website in "Other", you will need to finish your project fully and showcase it before requesting access."
September 29, 2026DonutSMP staff member archivePedro wrote on the official Discord: "The API has been fully disabled." On Reddit he added that it was removed entirely for abuse and safety reasons.
November 2026In the game, /api answers that the API has been disabled.

Source: archivePedro's comment on r/DonutSMP, in the thread "/api command doesn't work", and the same statements on the official DonutSMP Discord.

Some community guides, wikis and mods still tell players to run /api to get a key. That no longer works.

Donut API is an independent, unofficial replacement. It provides the same kind of data by reading it through bots in the game, and it is not affiliated with or endorsed by DonutSMP.

Keys and limits

Without a key, the API answers in keyless mode, limited per IP address: 10 calls per minute, 3,000 calls per month and 24 hours of history.

With a key, send it in one of these two headers. Keys start with dk_. They are not issued yet: the plans are on the pricing page.

curl with a key
curl "https://api.donutapi.app/v1/ah/items" \
  -H "Authorization: Bearer dk_your_key"

curl "https://api.donutapi.app/v1/ah/items" \
  -H "x-api-key: dk_your_key"

Every counted response tells you where you stand. /v1/status is never counted, and neither are 404 and 202 answers.

HeaderMeaning
x-ratelimit-plankeyless, free, developer or pro.
x-ratelimit-limit-minuteCalls allowed per minute.
x-ratelimit-remaining-minuteCalls left in the current minute.
x-ratelimit-limit-monthCalls allowed per month.
x-ratelimit-remaining-monthCalls left this month.

Errors

Errors are JSON objects with an error field.

StatusBody and meaning
401{"error":"invalid or revoked API key"}
404{"error":"not found"}
429{"error":"rate limit: too many calls this minute"}, with a retry-after header that says how many seconds to wait.
429{"error":"monthly quota reached"}
502{"error":"origin unreachable"}. The data server behind the API did not answer.

Status

GET/v1/status

Tells you when the latest auction house and orders snapshots were taken, how large they were, and whether each bot is in the game. Use it to check that the data is fresh. This route is not counted against your limits.

FieldDescription
ah.ts, orders.tsWhen the snapshot was taken, as Unix time in milliseconds.
pagesNumber of pages read.
lignesNumber of rows read.
complet1 when every page was read, 0 when the page cap was reached.
bots[].nameName of the bot.
bots[].onlineWhether the bot is connected to the game. It turns false after five minutes without a sign of life.
bots[].last_seenWhen the bot was last seen, as Unix time in milliseconds.
bots[].taskWhat the bot is doing: ah, orders or leaderboard during a snapshot, otherwise chat or player. null when the bot is offline.
nowServer time, as Unix time in milliseconds.
curl request
curl "https://api.donutapi.app/v1/status"
Response (real data)
200 OK application/json
{
  "ah": { "id": 30, "type": "ah", "ts": 1794389528215,
    "pages": 80, "lignes": 3600, "complet": 0, "bot": "bot2" },
  "orders": { "id": 28, "type": "ordres", "ts": 1794389383497,
    "pages": 40, "lignes": 1800, "complet": 0, "bot": "bot1" },
  "bots": [
    { "name": "bot1", "online": true, "last_seen": 1794389621453, "task": null },
    { "name": "bot2", "online": true, "last_seen": 1794389688338, "task": null }
  ],
  "now": 1794389696268
}

The field names of this route are internal and may be renamed. A live view of it is on the status page.

Auction house offers

GET/v1/ah?item=<id>&limit=<n>

Offers from the latest auction house snapshot.

ParameterDescription
itemMinecraft item id, for example ender_chest. Filters the offers.
limitMaximum number of offers to return.
FieldDescription
itemItem id.
nameDisplay name of the item.
countNumber of items in the offer.
priceTotal price of the offer.
unit_pricePrice per single item.
snapshot.atWhen the snapshot was taken, as Unix time in milliseconds.
snapshot.pagesNumber of pages read.
snapshot.completeWhether every page was read.
curl request
curl "https://api.donutapi.app/v1/ah?item=ender_chest&limit=2"
Response (real data)
200 OK application/json
{
  "snapshot": { "at": 1794387666192, "pages": 80, "complete": false },
  "listings": [
    { "item": "ender_chest", "name": "Ender Chest", "count": 1,
      "price": 13800, "unit_price": 13800 }
  ]
}

Auction house items

GET/v1/ah/items

One summary line per item in the latest snapshot: how many listings it has, the lowest unit price, and the total number of items on sale. Items are sorted by name.

curl request
curl "https://api.donutapi.app/v1/ah/items"
Response (real data, trimmed to one item)
200 OK application/json
{
  "snapshot": { "at": 1794387666192, "pages": 80, "complete": false },
  "items": [
    { "item": "acacia_button", "listings": 2,
      "lowest_unit_price": 5700, "total_count": 2 }
  ]
}

Buy orders

GET/v1/orders?item=<id>&limit=<n>

Open buy orders from the latest orders snapshot.

FieldDescription
itemItem id.
nameDisplay name of the item.
price_eachPrice offered per item.
deliveredHow many items were already delivered.
wantedHow many items the order asks for.
curl request
curl "https://api.donutapi.app/v1/orders?limit=2"
Response (real data)
200 OK application/json
{
  "snapshot": { "at": 1794387262306, "pages": 40, "complete": false },
  "orders": [
    { "item": "gold_block", "name": "Blocks of Gold",
      "price_each": 37000, "delivered": 744000, "wanted": 1700000 },
    { "item": "pumpkin", "name": "Pumpkins",
      "price_each": 72.52, "delivered": 328000000, "wanted": 400000000 }
  ]
}

History

GET/v1/history?item=<id>&type=ah|orders&hours=<n>

Points from past snapshots for one item, ready to chart. Each point has the time of the snapshot, the price and the number of listings. A snapshot in which the item did not appear gives no point.

ParameterDescription
itemMinecraft item id.
typeah for the auction house or orders for buy orders.
hoursHow many hours back to look. Your plan sets the maximum: 24 hours without a key.
curl request
curl "https://api.donutapi.app/v1/history?item=ender_chest&type=ah&hours=24"
Response (real data)
200 OK application/json
{
  "item": "ender_chest",
  "type": "ah",
  "points": [
    { "at": 1794383619041, "price": 18500, "listings": 201 },
    { "at": 1794387666192, "price": 13800, "listings": 1 }
  ]
}

Public chat

GET/v1/chat?limit=<n>&after=<id>&q=<words>

The public chat of the server, as the bots see it. Player names are never exposed: author is an anonymous id that stays the same for a given player. Messages are kept for 30 days.

Without after, you get the latest messages, oldest first. To follow the chat, call again with after set to the latest_id you received: you only get newer messages. One call every 10 to 15 seconds is enough.

To search, pass q: you get the latest messages that contain every word, oldest first, from the whole history that is kept. The search ignores case and matches the start of words, so raid finds "raid" and "raids" but not "unraid". Use up to 6 words of 2 to 32 characters. The answer repeats the words that were searched in a q field.

The text is written by players. Show it as plain text and never as HTML.

ParameterDescription
limitNumber of messages to return. 50 by default, 200 at most.
afterOnly return messages with an id above this one.
qWords to search for, separated by spaces. Example: /v1/chat?q=base+raid.
FieldDescription
idMessage id, increasing.
atWhen the message was seen, as Unix time in milliseconds.
authorAnonymous id of the player. Never the player name.
textThe message, as written by the player.
latest_idId of the newest message. Pass it as after on your next call.
curl request
curl "https://api.donutapi.app/v1/chat?limit=2"
Response (real data)
200 OK application/json
{
  "messages": [
    { "id": 692, "at": 1794388047324, "author": "u_83666249ba",
      "text": "i have a strategy to get many free glided blackstone come if you want" },
    { "id": 693, "at": 1794388047627, "author": "u_2044581161",
      "text": "TPA" }
  ],
  "latest_id": 693
}

Money leaderboard Beta

GET/v1/leaderboard/money?limit=<n>

The richest players on the server, ranked by balance, from the in-game leaderboard. Amounts are rounded by the game to two or three significant digits.

ParameterDescription
limitNumber of players to return, 200 at most.
FieldDescription
rankPosition in the leaderboard, starting at 1.
playerPlayer name.
moneyBalance, rounded as in the game.
snapshot.atWhen the snapshot was taken, as Unix time in milliseconds.
curl request
curl "https://api.donutapi.app/v1/leaderboard/money?limit=2"
Response (real data)
200 OK application/json
{
  "snapshot": { "at": 1794387284205 },
  "players": [
    { "rank": 1, "player": "Abuodee", "money": 5900000000000 },
    { "rank": 2, "player": "Wampiptiii01", "money": 3700000000000 }
  ]
}

Player stats Beta

GET/v1/player/<name>

Stats of one player, read from the player's stats page in the game and rounded the same way the game shows them. The name is a Java name, or a Bedrock name with its leading dot, 2 to 16 characters.

Answers come from a 10 minute cache. When a player is not in the cache, a bot asks the game, which takes about five seconds. If every bot is busy, the route answers 202 and you retry after the delay it gives. A 202 answer is not counted against your limits.

FieldDescription
playerPlayer name.
atWhen the stats were read, as Unix time in milliseconds.
cachedWhether the answer came from the cache.
stats.moneyBalance, rounded as in the game.
stats.killsNumber of kills.
stats.deathsNumber of deaths.
stats.playtimeTime played, as a string such as 180d 12h.
stats.shardsNumber of shards. Can be missing.
stats.blocks_placedBlocks placed. Can be missing.
stats.blocks_brokenBlocks broken. Can be missing.
stats.mobs_killedMobs killed. Can be missing.

Numbers are rounded by the game to two or three significant digits. The last four fields come from the full profile: when the game only returns the short one, they are left out, so treat them as optional.

curl request
curl "https://api.donutapi.app/v1/player/Abuodee"
Response (real data)
200 OK application/json
{
  "player": "Abuodee",
  "at": 1794389609645,
  "cached": false,
  "stats": {
    "money": 5900000000000,
    "kills": 8000,
    "deaths": 1500,
    "playtime": "180d 12h",
    "shards": 129000,
    "blocks_placed": 509000,
    "blocks_broken": 221000,
    "mobs_killed": 52500
  }
}
StatusMeaning
200Stats found.
202Every bot is busy. Body: {"status":"pending","retry_in_seconds":5}. Retry after that delay. Not counted against your limits.
400Invalid player name.
404Player not found, or no answer from the game. This can take about seven seconds.
503Too many lookups waiting. Try again later.

Staff Beta

GET/v1/staff
GET/v1/staff/online

The DonutSMP staff members our bots have seen in the player list of the game, where staff carry a marker in front of their name. The bots only read that list: they send no command and no message. It is refreshed every 30 seconds.

/v1/staff lists every staff member the bots have met so far, most recently seen first, and says who is online now. The list fills up as staff members connect. /v1/staff/online only returns the ones seen online right now.

Read it as "staff seen online", not as "all staff online". A staff member who is hidden does not appear, so a name that is missing or marked offline does not prove that person is offline. One call per minute is enough.

FieldDescription
staffAlways an array, empty when there is nobody to list.
staff[].playerPlayer name of the staff member.
staff[].onlineWhether the bots see this member right now. Only in /v1/staff.
staff[].last_seenWhen the member was last seen, as Unix time in milliseconds. Only in /v1/staff.
staff[].first_seenWhen the bots first saw the member, as Unix time in milliseconds. Only in /v1/staff.
snapshot.atWhen the list was read, as Unix time in milliseconds. Only in /v1/staff/online, and snapshot is null when no bot is in the game.
curl request
curl "https://api.donutapi.app/v1/staff"
Response (real data)
200 OK application/json
{
  "staff": [
    { "player": "Napooo_", "last_seen": 1794390657165,
      "first_seen": 1794390567159, "online": true },
    { "player": "BobIsFound", "last_seen": 1794390657162,
      "first_seen": 1794390567155, "online": true },
    { "player": "u_vv", "last_seen": 1794390657147,
      "first_seen": 1794390567150, "online": true }
  ]
}
curl request
curl "https://api.donutapi.app/v1/staff/online"
Response (real data)
200 OK application/json
{
  "snapshot": { "at": 1794390362302 },
  "staff": [
    { "player": "BobIsFound" },
    { "player": "Napooo_" },
    { "player": "u_vv" }
  ]
}