Contribute data
Thottbot was built from what players saw in game, uploaded through the Cosmos plugin. Thottbot Forever works the same way. An addon records items, drops, vendors, quest givers, spawn locations and objects as you play, and you upload what it saw. Every upload is stored as received, less your character’s name and realm, and then merged into the database, so you can always see what yours did.
Not sure where to go? Wanted lists what nobody has recorded yet: creatures and objects nobody has placed, quests nobody has seen given or handed in and items nobody has found a way to get, zone by zone.
Upload from the game
Uploads are paused for now: the site is taking none, from the uploader or the API. Try again later.
The game cannot send anything itself. The Thottbot Forever addon records what you see as you
play and writes it to a file on your computer when you log out or /reload; you then
upload that file with the Thottbot Forever Uploader, a program for Windows.
- Get the addon. It is not available to download yet. When it is, this page will say where to get it.
- Install it. Copy its
ThottbotForeverfolder intoWorld of Warcraft/_forever_/Interface/AddOns/, then/reloador restart the game. It records creatures you target, loot tables you open, merchants, quest givers, trade skill recipes and the auction house’s prices — and nothing about other players or your chat. Of your bags it notes only which recipe item went as you learned a recipe./tbfshows what it has. - Upload it with the Thottbot Forever Uploader. Log out
or
/reload, then open the uploader. It finds the file the addon saved, lets you choose which characters and which kinds of data go, and sends it, however large it grows. Upload as often as you like: nothing is counted twice. This site takes no upload in the browser.
How uploads are merged
Two rules keep uploads from damaging the database.
- Existing data is never overwritten. A record creates an entity if it is missing; if it already exists, only fields that are still empty get filled in. Sending a partial item is safe and useful, and sending a wrong value cannot destroy a known one. A value that differs from a known one — one every player sees the same, such as a level or a sell price — is kept for a moderator to decide which stands, and the page of your uploads shows what became of yours. A value taken is listed as different in Forever.
- Drop rates are counted, not declared. Nobody can send a drop rate. Every loot table opened counts, the items in it and the times it held nothing, and the rate is how often each item appeared across all of them. A counted rate only replaces an existing one once there are at least five tables, and pages show the sample size next to the percentage.
For developers
The rest of this page is for people writing their own collector, or sending uploads through the API rather than the uploader.
Upload format
POST /api/v1/contributions with a bearer token (create one here) and a JSON body:
{
"client": { "name": "ThottbotForever", "version": "0.7.0" },
"gameVersion": "forever-1.60.1.70009",
"locale": "enUS",
"character": { "name": "Sundermoon Ashbrook", "faction": "horde", "level": 60 },
"records": [
{
"type": "npc_loot",
"observedAt": "2026-09-17T20:11:00Z",
"data": {
"npcId": 11502,
"npcName": "Ragnaros",
"items": [{ "itemId": 17204, "itemName": "Eye of Sulfuras" }]
}
},
{
"type": "npc_location",
"data": {
"npcId": 3446,
"npcName": "Mankrik",
"zoneId": 17,
"zoneName": "The Barrens",
"x": 48.2,
"y": 59.0
}
},
{
"type": "object_location",
"data": {
"objectId": 176589,
"objectName": "Black Lotus",
"zoneId": 618,
"zoneName": "Winterspring",
"x": 60.4,
"y": 38.2
}
},
{
"type": "spell_craft",
"data": {
"spellId": 17187,
"spellName": "Transmute: Arcanite",
"itemId": 12360,
"itemName": "Arcanite Bar",
"quantityMin": 1
}
},
{
"type": "spell_reagent",
"data": { "spellId": 17187, "itemId": 12359, "itemName": "Thorium Bar", "quantity": 1 }
},
{
"type": "vendor_item",
"data": { "npcId": 3934, "npcName": "Brog Hamfist", "itemId": 117, "price": 25 }
},
{
"type": "item",
"data": {
"id": 19019,
"name": "Thunderfury, Blessed Blade of the Windseeker",
"quality": 5,
"itemLevel": 80,
"requiredLevel": 60
}
}
]
} The answer is a receipt. The batch is stored at once and merged shortly after, and GET /api/v1/me/contributions then says what each batch applied and why any record was
rejected.
POST /api/v1/contributions 202 Accepted{
"data": {
"id": 48213,
"recordCount": 7,
"receivedAt": "2026-09-17T20:14:02.000Z",
"status": "received"
},
"meta": {
"requestId": "a556f7d01c2e9b44",
"generatedAt": "2026-09-17T20:14:02.318Z"
}
}clientidentifies the uploading tool, andgameVersionthe game’s build.characteris optional. Itsname— the full name, surname and all — is used for one thing: taking it out of quest text. Neither it nor the character’s realm is kept with the upload. Anauction_pricesays what the game client said of the server whose auction house it was read at: its realm’s name and id, the character’s native (home) realm’s id, and whether hardcore rules were on. Those are kept with the price, beside the account that sent it, so the site can tell which gameplay style’s market it was.localeis the client’s language, such asenUSordeDE. The site is in English: an upload from a client in any other language adds numbers and relations to entries the site has, and never creates one or writes its names and text.- Quest text comes with the reader’s name written into it. Put it back to the game’s own
placeholder,
$N, before sending: the full name first, then the first name on its own. recordsis 1 to 5,000 observations. Each has atypeand adataobject; include the game ids you have and the names you saw, so that anything not in the database yet can be created from them.- Entity records:
item,npc,quest,zone,object,spell,faction,item_sighting. - Relation records:
npc_loot,npc_drop,object_loot,object_drop,gathered_loot,npc_location,object_location,quest_location,ui_map,flight_point,vendor_item,vendor_restock,npc_respawn,quest_giver,quest_reward,quest_required_item,quest_required_kill,quest_reputation,kill_reputation,spell_reagent,spell_craft,spell_teacher,recipe_item,recipe_colors,gather_skill,auction_price. - Batches are limited to 1 MB and 30 uploads per minute per token, and an account to 600 batches an hour however it sends them, a file counting the batches it is stored as.
How each record is merged
The rules above, in terms of the format: there is no dropChance field to send, and npc_loot reports a whole loot table, the items it contained even when that is none.
An NPC’s services (innkeeper, flight_master, banker, …) and its vendor and repair flags only ever add up: send what you saw it
do.
npc_drop and object_drop report a single sighting and count as one
loot table containing one item, so prefer npc_loot where your client can see the whole
table. Repeated spawn locations within about 1.5 map units are treated as the same point rather than
piling up.
Loot that is neither a kill nor a chest goes in gathered_loot, counted the same way
but kept apart, so a skinning never turns into a drop. Its kind says what the loot
was taken from: skin and pickpocket name a creature (npcId), fishing a zone (zoneId, or the uiMapId the catch was made on), and container and disenchant the item opened or disenchanted (sourceItemId). Any loot
item may carry the smallest and largest stack seen, as quantityMin and quantityMax; a range only ever widens.
A flight_point is keyed by the game’s taxi node id (nodeId) and fills
in as it is seen: its name and faction (alliance, horde or neutral), where it stands — a zoneId or the uiMapId it was drawn on, with x and y there, both or
neither — and the flight master who flies from it (npcId, with npcName for
one the site has not seen). The first name, side and flight master stand, and a place is taken whole,
only when it says more than the one the node has.
A quest_required_item is something a quest asks to be brought (questId, itemId) and how many (quantity, 1 if not given) — the number the quest
wants, as the panel its ender shows says, not the number the player is carrying. It is the same
for everyone who does the quest, so the latest one sent stands.
A quest_required_kill is a creature a quest asks you to kill (questId, npcId): the objective it counts toward, 1 for the first in the quest’s
own order, and the quantity that objective wants. Send one only when you know the kill
moved that objective; the addon credits one only when a single kill moved a single objective by one.
The latest word stands.
A vendor_item is a merchant’s listing (npcId, itemId):
the price in copper for one purchase of stack items, and costs, what that purchase takes besides or instead — at most three, each an item (itemId, itemName) or a currency (currencyId, currencyName)
with its count. Bought with those alone, the price is 0. The dearest price seen
stands, since reputation discounts it; what the listing costs besides is the merchant’s, so the
first word on what and how many stands, and a later one only names a currency it left unnamed.
Send the costs whole or not at all: a list short of one would understate the price.
The collector and the uploader
The addon’s source is in this project’s repository, under addon/ThottbotForever/,
and what it writes is settled: the saved format is documented in src/lib/collector/records.ts, and both the desktop uploader and the command-line
uploader read it. Any addon that writes that file can upload through either. Keep client.name stable so batches can be traced back to their source.
To send a file from the command line instead, from a checkout of the repository, create a token on your account and run:
# The token, from your account page export THOTTBOT_TOKEN=tbf_… # Send the file the addon saved npm run collector:upload -- \ --file "World of Warcraft/_forever_/WTF/Account/<ACCOUNT>/SavedVariables/ThottbotForever.lua" \ --url "https://thottbot-forever.com"