Battle of the Bits API overview¶
This page serves as a general introduction to concepts used in the Battle of the Bits API, with information useful both for pyBotB users and anyone else wanting to add support for it. For official documentation, see “BotB API v1” on the Lyceum.
Overview¶
The BotB API base URL is https://battleofthebits.com/api/v1/. The API URLs follow the following format:
https://battleofthebits.com/api/v1/{object_type}/{command}/...
For a list of object types, see Object types.
For most object types, the available commands are:
/api/v1/{object_type}/load/{id}- retrieves an object by its ID (idproperty)./api/v1/{object_type}/list/[{page_number=0}/{page_length=25}]- retrieves multiple objects at once; also accepts filters, conditions and sorting options. Pagination is optional./api/v1/{object_type}/random- gets a random object of this type./api/v1/{object_type}/search/{query}- search for objects whose name/title contains the provided query string; only available for object types with a name/title attribute.
Some object types have additional endpoints; a full list can be fetched through the API’s documentation index. The documentation index also contains all possible properties that object types can have (with some caveats - see Documentation index quirks).
Note
Make sure to use HTTPS for all API queries!
Getting responses in XML format¶
The BotB API returns data in JSON format by default; it is also possible to get data in XML by adding ?format=xml to the URL or adding the .xml extension to the URL. (TODO - verify which endpoints this works for)
Object types¶
Available object types are:
battle- a battle, either a major battle or an X Hour Battle (XHB).botbr- a BotBr, user of the site.botbr_points- the points for a BotBr for a specific class.entry- an entry, a single submission to a battle.favorite- a single favorite on an entry.format- a battle format.group_thread- a thread of messages; forum threads and comments on entries/battles/BotBr profiles are group threads.lyceum_article- an article on the Lyceum.palette- an on-site color palette.playlist- a single playlist containing entries.playlist_to_entry- join table joining playlist and entry.
tag- a single tag given to an entry.botbr_stats- a single statistic for a BotBr.daily_stats- the site-wide statistic for a specific day.
For an overview of properties for each object type, see pyBotB’s implementation: Data classes.
Basic commands¶
All object commands have at least 3 of the 4 basic commands: load, list, random and search. The following is a table of available basic commands per object type:
Object type |
Load |
List |
Random |
Search |
|---|---|---|---|---|
|
✔ |
✔ |
✔ |
✔ |
|
✔ |
✔ |
✔ |
✔ |
|
✔ |
✔ |
✔ |
✘ |
|
✔ |
✔ |
✔ |
✔ |
|
✔ |
✔ |
✔ |
✘ |
|
✔ |
✔ |
✔ |
✘ |
|
✔ |
✔ |
✔ |
✔ |
|
✔ |
✔ |
✔ |
✔ |
|
✔ |
✔ |
✔ |
✘ |
|
✔ |
✔ |
✔ |
✔ |
|
✔* |
✔ |
✔ |
✔ |
|
✔ |
✔ |
✔ |
✔ |
|
✔* |
✔ |
✔ |
✘ |
|
✔ |
✔ |
✔ |
✘ |
* does not have an user-exposed ID, thus the load option is not very useful
Load (/api/v1/{object_type}/load/{id})¶
GET/api/v1/{object_type}/load/{id}Load a single object by its ID.
URL parameters:
object_type(str) - object type to perform the query for.id(int) - ID of the object that will be fetched.
Returns:
On success: (HTTP
200) - JSON-encoded object data.On failure:
Not found: (HTTP
500) - JSON response:{"response_type":"FAIL","response_message":"Object of type '{object_type}' with id '{id}' unfounded."}
Other server errors: (HTTP
500)
Note
Be aware that the “not found” error has HTTP status code 500, not 404!
List (/api/v1/{object_type}/list)¶
GET/api/v1/{object_type}/list[/{page_number}[/{page_length}]][?sort={key}&desc=true&filters={key}~{val}^{key2}~{val2}]Load a single object by its ID.
Parameters:
object_type(str) - object type to perform the query for.id(int) - ID of the object that will be fetched.page_number(int, optional, default =0) - number of the page, for pagination.page_length(int, optional, default =25max. =500) - length of a single page, for pagination.
URL parameters:
sort(str) - key to sort by.desc(bool, default =false) - iftrue, sort in descending order by the key specified insort. Has no effect ifsortis not set.filters(custom format) - perform an exact match on the given key’s value. The format for a filter is{key}~{val}; multiple filters can be joined together with^, e.g.{key1}~{val1}^{key2}~{val2}.A more powerful alternative to filters are conditions - see below.
Returns:
On success: (HTTP
200) - list of object data dicts; the list is empty if no objects match.On failure:
Malformed parameters (HTTP
400) - plain-text error message, followed by<br><br><i><b>Please RTFM</b></i><br><a href="https://battleofthebits.com/lyceum/View/BotB+API+v1/">https://battleofthebits.com/lyceum/View/BotB+API+v1/</a>Server error (HTTP
500)
List conditions¶
Conditions are a more powerful alternative to filters; they allow for performing a subset of SQL-like filters on the query.
JSON-encoded conditions look as follows:
"conditions": [
{"property": "(property)", "operator": "(operator)", "operand": (operand)}
]
where property is the property to filter to, operator is the operator, and operand is the value passed through the operator.
All conditions have to be met for an item to match (think of it like an AND match).
Note
Some API queries within the site’s client-side JS also provide a “key” property (usually set to the same value as “property”); however, it appears to be unused and can safely be ignored (TODO?).
Available operators:
=- check if the value of the property equals the operand. Only works with numerical operands (returns “non numeric operand must use different operator” error otherwise); for strings, useLIKEinstead.<>- check if the value of the property does not equal the operand. Only works with numerical operands (returns “non numeric operand must use different operator” error otherwise); for strings, useNOT LIKEinstead.<,>,<=,>=- standard mathematical checks; only work with numerical operands.&- binary AND operator; only works with numerical operands.LIKE/NOT LIKE- check if the string value of the property matches the operand. The operand is either:The string itself for an exact match (e.g.
weave);The string prepended/appended with
%to perform a glob.%vewill matchweaveandeve,dream%will matchdreamghost.it,%dad%will matchdads,asdad,qwertydaduiop
IN- check if the value of the property is contained within the list provided in the operand. The operand must be a list of values.IN_SUBQUERY:{subquery}- exact mechanics are not known yet. Known subqueries are:On
/api/v1/entry/list:id,IN_SUBQUERY:botbr_entry_list,{botbr_id}(prop, operator, operand) - returns entries which the BotBr either submitted or was added as a colaborator on. Used on the Entries page of a BotBr’s profile.id,IN_SUBQUERY:botbr_favorites,{botbr_id}(prop, operator, operand) - returns the favorite entries of the BotBr with the given ID. Used on the Favorites page of a BotBr’s profile.
IS/IS NOT- can be used in combination with an operand ofNULLorNOT NULLto check if a field is/is not NULL. All other operands are not allowed.
Sending list conditions¶
Sending conditions is done by sending an AJAX POST request with the conditions passed in the conditions property (and optionally with sort and desc included). Filters are ignored when passing conditions.
In jQuery, this request can be prepared as such:
$.ajax({
type: "POST",
url: "https://battleofthebits.com/api/v1/{object_type}/list",
data: {
conditions: [
{property: "property1", operand: "<>", operator: "operator1"},
{property: "property2", operand: "<>", operator: "operator2"},
/* ... */
],
/* optionally: */
sort: "id",
desc: true
}
});
In other languages - you must send a POST request, the Content-Type header must be set to multipart/form-data, and the data must be sent in form data format. The conditions list must then be converted into the following format:
conditions[0][property]=property1
conditions[0][operand]=<>
conditions[0][operator]=operator1
conditions[1][property]=property2
conditions[1][operand]=LIKE
conditions[1][operator]=operator2
...
conditions[{n}][property]=...
conditions[{n}][operand]=...
conditions[{n}][operator]=...
For cases where the operator is a list (IN operand), it must be passed like so:
conditions[{n}][operator][0]=val1
conditions[{n}][operator][1]=val1
...
conditions[{n}][operator][{i}]=valI
Note
Most HTTP request libraries, upon being passed a dictionary-like object, will attempt to send JSON data raw, using the application/json content type. This is not supported by the BotB API and your arguments will be ignored. You must use the method described above.
Random /api/v1/{object_type}/random¶
GET/api/v1/{object_type}/randomLoad a random object with the given object type.
Parameters:
object_type(str) - object type to perform the query for.
Returns:
On success: (HTTP
200) - list containing a single object data dict. (TODO - is it possible to get multiple values?)On failure:
Server error (HTTP
500)
Search /api/v1/{object_type}/search/{query}¶
GET/api/v1/{object_type}/search/{query}[/{page_number}[/{page_length}]]Search for objects of the given type whose name/title property contains the query substring.
This command is only available for objects with a title/name or equivalent attribute.
Parameters:
object_type(str) - object type to perform the query for.query(str) - Substring to find in the names. Case-insensitive, matches inside of the title as well.page_number(int, optional, default =0) - number of the page, for pagination.page_length(int, optional, default =25max. =500) - length of a single page, for pagination.
Returns:
On success: (HTTP
200) - list containing the matching objects; empty if no results were found.On failure:
Malformed parameters (HTTP
400) - plain-text error message, followed by<br><br><i><b>Please RTFM</b></i><br><a href="https://battleofthebits.com/lyceum/View/BotB+API+v1/">https://battleofthebits.com/lyceum/View/BotB+API+v1/</a>Server error (HTTP
500)
Object type-specific commands¶
Some object types also implement additional commands. These are outlined in this section.
Battle (battle)¶
Current battles (/api/v1/battle/current)¶
GET/api/v1/battle/currentGet a list of upcoming and ongoing battles.
Returns:
On success: (HTTP
200) - list containing upcoming and ongoing battles.On failure:
Server error (HTTP
500)
List by date (/api/v1/battle/list_by_date/{date})¶
GET/api/v1/battle/list_by_date/{date}Get a list of all battles that happened on this date (date in YYYY-MM-DD format).
Parameters:
date(str) - date inYYYY-MM-DD(EST timezone).
Returns:
On success: (HTTP
200) - list containing battles that happened on the requested date.On failure:
Malformed parameters (HTTP
400) - plain-text error message, followed by<br><br><i><b>Please RTFM</b></i><br><a href="https://battleofthebits.com/lyceum/View/BotB+API+v1/">https://battleofthebits.com/lyceum/View/BotB+API+v1/</a>Server error (HTTP
500)
List by month (/api/v1/battle/list_by_month/{date})¶
GET/api/v1/battle/list_by_month/{date}Get a list of all battles that happened during this month (date in YYYY-MM format).
Parameters:
date(str) - date inYYYY-MM(EST timezone).
Returns:
On success: (HTTP
200) - list containing battles that happened during the requested month.On failure:
Malformed parameters (HTTP
400) - plain-text error message, followed by<br><br><i><b>Please RTFM</b></i><br><a href="https://battleofthebits.com/lyceum/View/BotB+API+v1/">https://battleofthebits.com/lyceum/View/BotB+API+v1/</a>Server error (HTTP
500)
BotBr (botbr)¶
Get level-up points (/api/v1/botbr/levels)¶
GET/api/v1/botbr/levelsGet a list where the index is the level, and the value is the amount of points required to reach that level.
Returns:
On success: (HTTP
200) - list containing level-up point requirements.On failure:
Server error (HTTP
500)
Entry (entry)¶
Battle activity list (/api/v1/entry/battle_activity_playlist)¶
GET/api/v1/entry/battle_activity_playlistGet a list of the last 40 winning entries in battles (“battle activity” box on main page).
Returns:
On success: (HTTP
200) - list containing winning entries.On failure:
Server error (HTTP
500)
Entry activity list (/api/v1/entry/entry_activity_playlist)¶
GET/api/v1/entry/entry_activity_playlistGet a list of the last 40 submitted entries (“entry activity” box on main page).
Returns:
On success: (HTTP
200) - list containing recent entries.On failure:
Server error (HTTP
500)
Playlist entries (/api/v1/entry/playlist_playlist/{playlist_id})¶
GET/api/v1/entry/playlist_playlist/{playlist_id}List all entries in the playlist with the given ID.
Parameters:
playlist_id(int) - ID of the playlist to get the entries of.
Returns:
On success: (HTTP
200) - list containing recent entries.On failure:
Playlist not found: (HTTP
500) - empty response (0 bytes).Server error (HTTP
500)
BotBr favorites (/api/v1/entry/botbr_favorites_playlist/{botbr_id})¶
GET/api/v1/entry/playlist_playlist/{botbr_id}List all favorite entries of the BotBr with the given ID.
Note
This seems to be missing some favorites; to get an accurate list, do a query
to /api/v1/entry/list with the following condition: (“id”, “IN_SUBQUERY:botbr_favorites”,
(BotBr ID)) (property, operator, operand).
Parameters:
botbr_id(int) - ID of the BotBr to get the favorites of.
Returns:
On success: (HTTP
200) - list containing recent entries.On failure:
Playlist not found: (HTTP
500) - empty response (0 bytes).Server error (HTTP
500)
Covers/entries covered in Decadent Decade battles (/api/v1/entry/decadent_covered)¶
GET/api/v1/entry/decadent_coveredList all cover entries and original entries that were covered in Decadent Decade cover battles.
Note
The first entry is covered, the rest are covers…? TODO
Returns:
On success: (HTTP
200) - list containing entries.On failure:
Server error (HTTP
500)
Tag (tag)¶
Tag cloud by substring (/api/v1/tag/cloud_by_substring/{substring})¶
GET/api/v1/tag/cloud_by_substring/{substring}Get a HTML representation of a tag cloud containg clouds matching the given substring.
Parameters:
substring(str) - Substring to find in tags.
Returns:
On success: (HTTP
200) - list containing a single string with HTML representation of tag word cloud.On failure:
Server error (HTTP
500)
Palette¶
Get current default palette (/api/v1/palette/current_default)¶
GET/api/v1/palette/current_defaultGet the current default on-site palette.
Returns:
On success: (HTTP
200) - list containing a single palette object representing the current default palette.On failure:
Server error (HTTP
500)
BotBr stats (botbr_stats)¶
BotBr stats by BotBr ID (/api/v1/botbr_stats/by_botbr_id/{botbr_id})¶
GET/api/v1/botbr_stats/by_botbr_id/{botbr_id}List all BotBr stats for the BotBr with the given ID.
Parameters:
botbr_id(int) - ID of the BotBr to fetch the stats for.
Returns:
On success: (HTTP
200) - list containing BotBr statistics table entries.On failure:
Server error (HTTP
500)
BotBr stats by BotBr ID from the last N days (/api/v1/botbr_stats/days_back/{botbr_id}/{n_days})¶
GET/api/v1/botbr_stats/days_back/{botbr_id}/{n_days}List BotBr stats from the last n_days for the BotBr with the given ID.
Parameters:
botbr_id(int) - ID of the BotBr to fetch the stats for.n_days(int) - the amount of days back for which to fetch the stats.
Returns:
On success: (HTTP
200) - list containing BotBr statistics table entries.On failure:
Server error (HTTP
500)
Other¶
List point types (/api/v1/point/types)¶
GET/api/v1/point/typesGet a list of point types (BotBr classes).
Note
There are glitched lowercase point types, as well as an empty point type, that can be seen on the site; this endpoint only returns normal, valid classes, and should not be treated as an absolute authority on the types of classes you’ll see in the wild.
Returns:
On success: (HTTP
200) - list containing point type/class names.On failure:
Server error (HTTP
500)
Interpret Firki markup (/api/v1/firki/interpret)¶
POST/api/v1/firki/interpretInterpret a Firki markup string into HTML.
Post form data:
firki_string(str) - the string to convert. (Note that this string always ends with</span>(although no opening span tag is provided); you may want to remove it.)
On success: (HTTP
200) - JSON list with a single item, containing the interpreted text as a string.On failure:
Server error (HTTP
500)
Spriteshit version (/api/v1/spriteshit/version)¶
GET/api/v1/spriteshit/versionGet the current version of BotB’s icon spritesheet (affectionately named the “spriteshit”).
The spritesheet PNG can be fetched from the following URL: https://battleofthebits.com/styles/spriteshit/{version}.png
Returns:
On success: (HTTP
200) - JSON object with a single property named “spriteshit_version” containing the version as a string.On failure:
Server error (HTTP
500)
Known quirks¶
This is a WIP list of quirks with the BotB API and various objects that exist within BotB’s nearly 20-year-old database. These are all worked around in pyBotB; they are listed here as they could be of interest to other API implementations.
Battle: Battles fetched through the
/api/v1/battle/loadendpoint do not haveperiodandperiod_end(_*)properties. If you need to access them, use thelistendpoint with a filter to match for the ID.This also applies to the
battleobject returned byentryAPIs.
Battle: Battles in the Tally Period do not return a
periodvalue (though they do have aperiod_endvalue).Entry: Some older entries do not have comment threads attached to them; for those entries, the
postsproperty will be missing. If you depend on it being there, you can assume that a missingpostsproperty is equivalent to apostsvalue of0.
Documentation index quirks¶
If you plan to parse data using the documentation index, keep the following quirks in mind:
entryobject endpoints, besides containing the properties listed in thepropertiesvariable, also returnbotbr,battleandformatproperties, which contain the entry author’s BotBr object, the battle object for the battle the entry was in, and the Format object for the entry’s format respectively.battle.disable_penaltyis not returned by the API.group_thread.last_post_timestampis not returned by the API.
TODO¶
Parts of the API that I have yet to figure out:
/api/v1/battle/entries_by_formatdoes not seem to return anything; likely someone needs to figure out the right parameters./api/v1/entry/botbr_favorites_playlist/{id}does not return all favorites. TODO.Need to figure out how the top 40 endpoints are to be interpreted (
https://battleofthebits.com/api/v1/entry/top40_by_day_count,https://battleofthebits.com/api/v1/entry/top40majors,https://battleofthebits.com/api/v1/entry/top40minors)Need to figure out how
https://battleofthebits.com/api/v1/entry/decadent_coveredworks/where it’s usedNeed to figure out what
/api/v1/points/type_m/type_mediumandtype_q/type_qualitymean