API Reference
Read-only REST API. Proof-of-work token required — no API key needed.
Base URL
All responses are application/json; charset=utf-8. Song and artist names are in Sinhala Unicode (name_si) and English (name).
Authentication
All endpoints except /api/stats require a short-lived Proof-of-Work token. There are no API keys or accounts.
Request a challenge
POST /api/request-challenge — returns a { prefix, target, expiresAt } object. No token needed for this call.
Solve the Proof-of-Work
Find a nonce string such that SHA-256(prefix + nonce) ≤ target when interpreted as a 256-bit big-endian integer. Iterate nonce = "0", "1", "2", … until the condition is met.
Send requests with the token
Include Authorization: Bearer {prefix}:{nonce} in every API request. The same token is valid for 5 minutes — you do not need to re-solve for every request.
Proof-of-Work Algorithm
- Hash function: SHA-256
- Input: concatenation of
prefixandnonce(no separator) - Condition:
SHA256(prefix + nonce) ≤ target(big-endian integer comparison) - Difficulty: 20 leading zero bits (~1 million SHA-256 iterations on average, <1 second)
- Token format:
{prefix}:{nonce} - Token lifetime: 5 minutes — one token can be reused for all requests within the window
import hashlib, requests
def get_token(base_url):
ch = requests.post(f"{base_url}/request-challenge").json()
prefix, target = ch["prefix"], ch["target"]
target_int = int(target, 16)
nonce = 0
while True:
h = hashlib.sha256((prefix + str(nonce)).encode()).hexdigest()
if int(h, 16) <= target_int:
return f"{prefix}:{nonce}", ch["expiresAt"]
nonce += 1
token, expires_at = get_token("https://mathaka.org/api")
headers = {"Authorization": f"Bearer {token}"}
songs = requests.get("https://mathaka.org/api/songs", headers=headers).json()
print(songs["pagination"]["total"], "songs")import { createHash } from "node:crypto";
async function getToken(baseUrl) {
const { prefix, target, expiresAt } = await fetch(`${baseUrl}/request-challenge`, {
method: "POST",
}).then((r) => r.json());
const targetBig = BigInt("0x" + target);
let nonce = 0;
while (true) {
const hash = createHash("sha256").update(prefix + nonce).digest("hex");
if (BigInt("0x" + hash) <= targetBig) break;
nonce++;
}
return { token: `${prefix}:${nonce}`, expiresAt };
}
const { token } = await getToken("https://mathaka.org/api");
const songs = await fetch(`https://mathaka.org/api/songs`, {
headers: { Authorization: `Bearer ${token}` },
}).then((r) => r.json());
console.log(songs.pagination.total, "songs");/api/request-challengeNo token requiredGenerate a fresh prefix and target for the cryptographic challenge. Each challenge expires after 5 minutes. Request a new challenge when your token nears expiry.
{
"prefix": "WUftu7mc7WNA-pLzn9Web17FT1c_Mf8sYkr2kXdH_A",
"target": "00000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF",
"expiresAt": 1780222355621
}
// target has 20 leading zero bits — average ~1 million SHA-256 iterations (<1 second)
// expiresAt is a Unix timestamp in milliseconds/api/statsNo token requiredReturns database statistics. This endpoint does not require a token.
Example Response
{
"songs": 8946,
"artists": 3517,
"lastUpdated": "2026-01-09 17:07:29"
}/api/songsPaginated list of songs ordered by insertion date (newest first). Returns Sinhala and English names, clean YouTube link (watch?v= only), and comma-separated artist names for both languages.
Parameters
| Name | Type | Description |
|---|---|---|
page | integer | Page number (default: 1) |
limit | integer | Results per page, max 50 (default: 24) |
artist | integer | Filter to songs for a specific artist ID |
Example Response
{
"songs": [
{
"id": "d2e9c27c-66d8-4d19-bcfe-ad562ae91664",
"name": "Pem Kathandare",
"name_si": "පෙම් කතන්දරේ",
"album": null,
"youtube_link": "https://www.youtube.com/watch?v=37jb8I4OybE",
"updated_at": "2026-01-09 17:07:29",
"artist_names": "Bathiya n Santhush",
"artist_names_si": "භාතිය සන්තුෂ්"
}
],
"pagination": {
"page": 1,
"limit": 2,
"total": 8946,
"pages": 4473
}
}/api/songs/:idFull song detail including complete Sinhala lyrics, composer, lyricist, and all singers. Song IDs are UUID v4 strings — obtain them from the list or search endpoints.
Parameters
| Name | Type | Description |
|---|---|---|
id* | string (UUID) | Song UUID — e.g. d2e9c27c-66d8-4d19-bcfe-ad562ae91664 |
Example Response
{
"id": "d0a4a2b7-c7bf-4faf-a1d3-4ede317603d4",
"src_id": 2,
"name": "Kalpana Lowa Mal Wane",
"name_si": "කල්පනා ලොව මල් වනේ",
"lyrics": "කල්පනා ලොව මල් වනේ\nසිහින රජ දහනේ\nආදරේ....ආදරේ....",
"youtube_link": "https://www.youtube.com/watch?v=tvXPN_yKYm0",
"album": null,
"composer_id": 557,
"writer_id": 272,
"composer_name": "Amila Muthugala",
"composer_name_si": "අමිල මුතුගල",
"writer_name": "Mahagama Sekera",
"writer_name_si": "මහාගම සේකර",
"status": "APPROVED",
"updated_at": "2019-03-15 00:49:14",
"singers": [
{
"id": 1,
"name": "Abeywardana Balasuriya",
"name_si": "අබේවර්ධන බාලසූරිය"
}
]
}/api/artistsPaginated list of artists ordered by song count descending. Only artists with at least one song are returned. Artist IDs are stable integers.
Parameters
| Name | Type | Description |
|---|---|---|
page | integer | Page number (default: 1) |
limit | integer | Results per page, max 100 (default: 48) |
role | string | Filter by role: singer | composer | writer |
Example Response
{
"artists": [
{
"id": 9,
"name": "Jothipala H R",
"name_si": "ජෝතිපාල H R",
"is_singer": 1,
"is_writer": 1,
"is_composer": 1,
"song_count": 247
}
],
"pagination": { "page": 1, "limit": 2, "total": 1245, "pages": 623 }
}/api/artists/:idFull artist detail with their 50 most recent songs. Songs list includes UUIDs for fetching individual lyrics.
Parameters
| Name | Type | Description |
|---|---|---|
id* | integer | Artist ID (stable integer) |
Example Response
{
"id": 9,
"name": "Jothipala H R",
"name_si": "ජෝතිපාල H R",
"is_singer": 1,
"is_writer": 1,
"is_composer": 1,
"active": 1,
"songCount": 247,
"songs": [
{
"id": "07ac88b5-5dbd-49c9-8dac-d59476e29836",
"name": "Bambareku Handuwa",
"name_si": "බඹරෙකු හැඬුවා",
"album": null,
"youtube_link": null
}
]
}/api/searchFull-text search over song titles (Sinhala and English) using FTS5 with prefix matching. Also returns matching artists by name. Sinhala Unicode queries are fully supported.
Parameters
| Name | Type | Description |
|---|---|---|
q* | string | Search query (Sinhala Unicode or romanized English) |
page | integer | Page number (default: 1) |
limit | integer | Results per page, max 50 (default: 24) |
Example Response
{
"songs": [
{
"id": "3fa2c11e-8b7d-4f2a-9c1e-5d6e7f8a9b0c",
"name": "Adaraya Nathi Wela",
"name_si": "ආදරය නැති වෙලා",
"artist_names": "Nanda Malini",
"artist_names_si": "නන්දා මාලනී",
"album": null,
"youtube_link": "https://www.youtube.com/watch?v=exampleId"
}
],
"artists": [
{
"id": 55,
"name": "Nanda Malani",
"name_si": "නන්දා මාලනී",
"is_singer": 1,
"song_count": 170
}
],
"pagination": { "page": 1, "limit": 2, "total": 48, "pages": 24 },
"query": "ආදරය"
}Data Model Notes
songs.idUUID v4 string (e.g. d0a4a2b7-c7bf-4faf-a1d3-4ede317603d4). Randomly assigned at import — stable but not sequential.
songs.src_idOriginal numeric ID from the source database. Useful for cross-referencing with older datasets.
songs.youtube_linkClean YouTube URL containing only the ?v= parameter — tracking params (list=, start_radio=, index=) are stripped. May be null.
songs.lyricsFull Sinhala lyrics as a plain text string. Lines separated by \r\n. Verse breaks are double newlines. Only present on /songs/:id.
artists.idStable integer ID. Artist IDs will not change across database updates.
is_singer / is_writer / is_composerSQLite integers: 1 = true, 0 = false. An artist can hold multiple roles simultaneously.
artist_names / artist_names_siComma-separated singer names on list responses. Use /artists/:id for full role breakdown.
Data Dumps
Monthly full exports for research, ML, and self-hosting. All data is licensed under ODbL.
SQLiteSQLite Database
Full normalized DB for self-hosting or offline querying
JSONJSON Export
Flat JSON arrays of all songs and artists
CSVCSV Export
Spreadsheet-ready export for data science pipelines