API reference
https://api.donutapi.app, and you do not need a key to try it. API keys and paid plans are not available yet.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.
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 "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.
| When | What |
|---|---|
| July 3, 2026 | DonutSMP 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, 2026 | DonutSMP 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 2026 | In 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 "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.
| Header | Meaning |
|---|---|
| x-ratelimit-plan | keyless, free, developer or pro. |
| x-ratelimit-limit-minute | Calls allowed per minute. |
| x-ratelimit-remaining-minute | Calls left in the current minute. |
| x-ratelimit-limit-month | Calls allowed per month. |
| x-ratelimit-remaining-month | Calls left this month. |
Errors
Errors are JSON objects with an error field.
| Status | Body 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
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.
| Field | Description |
|---|---|
| ah.ts, orders.ts | When the snapshot was taken, as Unix time in milliseconds. |
| pages | Number of pages read. |
| lignes | Number of rows read. |
| complet | 1 when every page was read, 0 when the page cap was reached. |
| bots[].name | Name of the bot. |
| bots[].online | Whether the bot is connected to the game. It turns false after five minutes without a sign of life. |
| bots[].last_seen | When the bot was last seen, as Unix time in milliseconds. |
| bots[].task | What the bot is doing: ah, orders or leaderboard during a snapshot, otherwise chat or player. null when the bot is offline. |
| now | Server time, as Unix time in milliseconds. |
curl "https://api.donutapi.app/v1/status"{
"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
Offers from the latest auction house snapshot.
| Parameter | Description |
|---|---|
| item | Minecraft item id, for example ender_chest. Filters the offers. |
| limit | Maximum number of offers to return. |
| Field | Description |
|---|---|
| item | Item id. |
| name | Display name of the item. |
| count | Number of items in the offer. |
| price | Total price of the offer. |
| unit_price | Price per single item. |
| snapshot.at | When the snapshot was taken, as Unix time in milliseconds. |
| snapshot.pages | Number of pages read. |
| snapshot.complete | Whether every page was read. |
curl "https://api.donutapi.app/v1/ah?item=ender_chest&limit=2"{
"snapshot": { "at": 1794387666192, "pages": 80, "complete": false },
"listings": [
{ "item": "ender_chest", "name": "Ender Chest", "count": 1,
"price": 13800, "unit_price": 13800 }
]
}Auction house 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 "https://api.donutapi.app/v1/ah/items"{
"snapshot": { "at": 1794387666192, "pages": 80, "complete": false },
"items": [
{ "item": "acacia_button", "listings": 2,
"lowest_unit_price": 5700, "total_count": 2 }
]
}Buy orders
Open buy orders from the latest orders snapshot.
| Field | Description |
|---|---|
| item | Item id. |
| name | Display name of the item. |
| price_each | Price offered per item. |
| delivered | How many items were already delivered. |
| wanted | How many items the order asks for. |
curl "https://api.donutapi.app/v1/orders?limit=2"{
"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
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.
| Parameter | Description |
|---|---|
| item | Minecraft item id. |
| type | ah for the auction house or orders for buy orders. |
| hours | How many hours back to look. Your plan sets the maximum: 24 hours without a key. |
curl "https://api.donutapi.app/v1/history?item=ender_chest&type=ah&hours=24"{
"item": "ender_chest",
"type": "ah",
"points": [
{ "at": 1794383619041, "price": 18500, "listings": 201 },
{ "at": 1794387666192, "price": 13800, "listings": 1 }
]
}Public chat
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.
| Parameter | Description |
|---|---|
| limit | Number of messages to return. 50 by default, 200 at most. |
| after | Only return messages with an id above this one. |
| q | Words to search for, separated by spaces. Example: /v1/chat?q=base+raid. |
| Field | Description |
|---|---|
| id | Message id, increasing. |
| at | When the message was seen, as Unix time in milliseconds. |
| author | Anonymous id of the player. Never the player name. |
| text | The message, as written by the player. |
| latest_id | Id of the newest message. Pass it as after on your next call. |
curl "https://api.donutapi.app/v1/chat?limit=2"{
"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
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.
| Parameter | Description |
|---|---|
| limit | Number of players to return, 200 at most. |
| Field | Description |
|---|---|
| rank | Position in the leaderboard, starting at 1. |
| player | Player name. |
| money | Balance, rounded as in the game. |
| snapshot.at | When the snapshot was taken, as Unix time in milliseconds. |
curl "https://api.donutapi.app/v1/leaderboard/money?limit=2"{
"snapshot": { "at": 1794387284205 },
"players": [
{ "rank": 1, "player": "Abuodee", "money": 5900000000000 },
{ "rank": 2, "player": "Wampiptiii01", "money": 3700000000000 }
]
}Player stats Beta
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.
| Field | Description |
|---|---|
| player | Player name. |
| at | When the stats were read, as Unix time in milliseconds. |
| cached | Whether the answer came from the cache. |
| stats.money | Balance, rounded as in the game. |
| stats.kills | Number of kills. |
| stats.deaths | Number of deaths. |
| stats.playtime | Time played, as a string such as 180d 12h. |
| stats.shards | Number of shards. Can be missing. |
| stats.blocks_placed | Blocks placed. Can be missing. |
| stats.blocks_broken | Blocks broken. Can be missing. |
| stats.mobs_killed | Mobs 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 "https://api.donutapi.app/v1/player/Abuodee"{
"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
}
}| Status | Meaning |
|---|---|
| 200 | Stats found. |
| 202 | Every bot is busy. Body: {"status":"pending","retry_in_seconds":5}. Retry after that delay. Not counted against your limits. |
| 400 | Invalid player name. |
| 404 | Player not found, or no answer from the game. This can take about seven seconds. |
| 503 | Too many lookups waiting. Try again later. |
Staff Beta
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.
| Field | Description |
|---|---|
| staff | Always an array, empty when there is nobody to list. |
| staff[].player | Player name of the staff member. |
| staff[].online | Whether the bots see this member right now. Only in /v1/staff. |
| staff[].last_seen | When the member was last seen, as Unix time in milliseconds. Only in /v1/staff. |
| staff[].first_seen | When the bots first saw the member, as Unix time in milliseconds. Only in /v1/staff. |
| snapshot.at | When 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 "https://api.donutapi.app/v1/staff"{
"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 "https://api.donutapi.app/v1/staff/online"{
"snapshot": { "at": 1794390362302 },
"staff": [
{ "player": "BobIsFound" },
{ "player": "Napooo_" },
{ "player": "u_vv" }
]
}