This guide assumes your server is installed at example.com
, although you can
change this address to match your actual installed location.
This address should be available to any devices that will be communicating with it. For example, if you wish to scrobble using 4G, the external API should be publicly accessible.
The internal API should ALWAYS remain hidden behind a firewall, as it allows direct manipulation of the database without authentication.
You may want to keep track of individual devices which submit data to the API. To submit any data to the API, you will need at least one device, which will have been set up after you start the server for the first time. If you wish to add more, go to the "devices" table within the database, and add a new device there. Your device ID must be a randomly generated UUID, and you should keep your API key secure.
For future reference in this document, <API-KEY>
refers to an API key
generated for a device in this database.
Unfortunately, due to connecting to various services, several of these endpoints
are not consistent with the others (eg. sending apiKey
as a body parameter, or
in the URL, or as an HTTP header). While I have also tried to make the response
payload consistent across all the endpoints, some are still different, so if you
choose to connect to these endpoints manually, please check that you are sending
the correct authorisation parameters and checking for the correct responses.
The main scrobbling endpoint matches the official ListenBrainz one - you should
only need to change the endpoint from https://api.listenbrainz.org
to
https://example.com/api/listenbrainz
Authorization
:token <API-KEY>
(eg.token e2cbe426-d193-...
)Content-Type
:application/json
listen_type
: "single", "import", or "playing_now".
if you send "playing_now", it will set the currently played track on the website, but will not save the scrobble to the database. This usually happens at the start of the song, and a "single" payload will be sent near the end.payload
, an array of objects, containing the following properties:listened_at
: unix timestamp in secondstrack_metadata
, an object containing the following properties:artist_name
track_name
release_name
additional_info
, an optional object containing the following properties:date
: The date this song/album was released, in ISO8601 formattags
: An array of strings, containing the music genres of the tracktracknumber
: An integerduration_ms
: An integer, the length of the song in milliseconds
Typically, more information about this track may be included by scrobblers, but the information provided above is all Everything uses to store scrobbles.
{
"listen_type": "single",
"payload": [
{
"listened_at": 1717347046,
"track_metadata": {
"artist_name": "TomboFry",
"release_name": "Floating Amongst the Stars",
"track_name": "Taken For a Ride",
"additional_info": {
"date": "2024-04-26",
"tags": ["Progressive Rock", "Indie Rock"],
"tracknumber": 2,
"duration_ms": 451736
}
}
}
]
}
200, successful response:
{
"status": "ok"
}
500, failure:
{
"status": "error",
"code": 500,
"error": "Information about the error"
}
No headers required, no payload required.
200, valid token
{
"code": 200,
"message": "Token valid.",
"valid": true,
"user": "A description about this device"
}
200, invalid token
{
"code": 200,
"message": "Token invalid.",
"valid": false
}
You can set up automation services - such as IFTTT or Zapier - to POST to this endpoint when periodically checking your Liked Videos playlist on YouTube. Alternatively, you can also set up an iOS Shortcut to POST to this endpoint when receiving YouTube URLs from the Share sheet for manual submissions.
Content-Type
:application/json
url
: The YouTube URL, can be in the formatyoutube.com/watch?v=...
oryoutu.be/...
title
: Optional, to override the real title of the videocreated_at
: Optional, to override the liked date, in ISO8601 formatapiKey
: Value of<API-KEY>
{
"url": "https://www.youtube.com/watch?v=4Q0p2WnVvMU",
"title": "Floating Amongst The Stars (Official Lyric Video)",
"created_at": "2024-01-01T09:00:00.000Z",
"apiKey": "0ccdd0ad-dd26-...",
}
200, successful:
{
"status": "ok"
}
400, failure:
{
"status": "Information about the error"
}
You can set up automation services - such as IFTTT or Zapier - to POST to this endpoint when periodically checking other services, such as Pocket. Alternatively, you can also set up an iOS Shortcut to POST to this endpoint when receiving URLs from the Share sheet for manual submissions.
Content-Type
:application/json
apiKey
: Value of<API-KEY>
url
: The page URL being bookmarkedtitle
: The title of the pagecreated_at
: Optional, set a custom date, in ISO8601 date/time format
{
"url": "https://www.tombofry.co.uk/",
"title": "TomboFry - 8-Bit / Chiptune Music",
"apiKey": "a732cb0b-c24f-...",
"created_at": "2024-01-01T09:45:00.000Z"
}
200, successful:
{
"status": "ok"
}
400, failure:
{
"status": "Information about the error."
}
Authorization
:<API-KEY>
(do not include "Bearer" or "token")Content-Type
:application/json
battery_level
: A number between 0.0 and 1.0 (eg.0.56
is 56%)battery_state
: One of:true
,false
, or a free-form string containing the state (eg. "charging", "unplugged", etc)
{
"battery_level": 0.56,
"battery_state": "charging"
}
200, successful:
{
"status": "ok"
}
400, failure:
{
"status": "err",
"message": "Information about the error"
}
An endpoint which follows the Overland API specification, which can be used with the Overland app for Android or iOS to log your location.
You can set the app up by changing the Server URL in the Settings tab as
follows: https://example.com/api/device/overland?apiKey=<API-KEY>
Make sure you put your API key in the URL query, but otherwise the payload and response information can be found in the link above.
NOTE: All endpoints in this category require the following headers, and return the same responses:
Authorization
:<API-KEY>
(do not include "Bearer" or "token")Content-Type
:application/json
200, successful:
{
"status": "ok"
}
400, failure:
{
"status": "Information about the error."
}
💡 If you're tracking steps with Apple Health, you could set up an automation using Shortcuts to fetch the number of steps taken today, and POST it to this endpoint at 23:59 every day.
created_at
: the date you're logging steps for, inYYYY-MM-DD
format. You can provide a full ISO8601 date but it will be truncated to the whole day in the database, so you only need to log this once per day. If not provided, the API will default to the current date.steps
: number of steps taken on this day
{
"created_at": "2024-01-01",
"steps": 3056
}
💡 You could set up an iOS Shortcut to log your weight to both Apple Health and POST to this endpoint at the same time.
created_at
: the date you're logging weight for, inYYYY-MM-DD
format. You can provide a full ISO8601 date but it will be truncated to the whole day in the database, so you only need to log this once per day. If not provided, the API will default to the current date.weight_kgs
: weight in kilograms, as a number (technically there's nothing stopping you from logging pounds, stone, or some other unit).
{
"created_at": "2024-01-01",
"weight_kgs": 70.0
}
created_at
: full date/time in ISO8601 format. If not provided, the API will default to the current date/time.name
: what you're currently eating/drinkingtype
: used to categorise food types. You may wish to track takeouts, caffeinated drinks, and normal meals separately from one another. Some examples include: "food", "drink", "soft drink", "coffee", "tea", "takeout", "snack", etc. If not provided, the API will default to "food".
{
"created_at": "2024-01-01T09:00:00.000Z",
"name": "Chicken and mushroom curry",
"type": "takeout"
}
💡 You could set up an iOS Shortcut to display a list of categories, then POST to this endpoint to start/end a session.
created_at
: date/time, in ISO8601ended_at
: date/time, in ISO8601category
: what you're time tracking
You do not have to provide created_at
or ended_at
- if you send a payload
without dates, it will start timetracking at the current date/time. If you send
another payload afterwards, the previous session will end and a new one will
begin at the current date/time. You can send created_at
without ended_at
if
you wish to start a timetracking session with a custom date/time.
Also, if you send the category
as "stop" while a session is currently running,
it will end the session.
Start a sleep session (which will end an existing session, if it is running):
{
"category": "sleep"
}
Start a cooking/eating session, which will start the session at the specifed time, and end the previous session at that time as well.
{
"category": "cooking / eating",
"created_at": "2024-01-01T09:00:00.000Z"
}
To keep track of your income/outgoings, you'll need to set up a webhook using
your Monzo account, and point it to the
following address: https://example.com/api/purchases?apiKey=<API-KEY>
.
Alternatively, if you don't have a Monzo account, you can still post to this endpoint using the following payload.
Content-Type
:application/json
account_id
: string, which MUST match theTOMBOIS_MONZO_ACCOUNT_ID
value provided in.env
, otherwise an error will be thrownamount
: number, an outgoing payment is negative, and 100 times the amount of the transaction (eg. 5.49 is-549
). Income is positive (eg. 15.32 is1532
)currency
: The 3-letter code of the currency used by the transaction (eg. £ isGBP
, and $ could beUSD
,AUD
,NZD
, etc.)created
: the date/time of the transaction in ISO8601 format.category
: arbitrary value used to categorise the transaction.description
: arbitrary value - will only be used if merchant name is not providedmerchant
: an object containing the following properties:name
: The name of the store/website/service the transaction was made with.
{
"account_id": "acc_df50c4ee-1333-...",
"amount": -500,
"currency": "GBP",
"created": "2024-01-01T09:00:00.000Z",
"category": "groceries",
"merchant": {
"name": "Tesco"
}
}
200, successful:
{
"status": "ok"
}
400, failure:
{
"status": "Information about the error."
}