captionbob docs

Version 1.0.0
Contents ↓

HTTP API examples

Use the font routes

The font routes tell which font files the burn uses for the font names of a script. The dashboard uses them for the font list of the editor and for the live preview.

Before you start

Put your API key in place of <your API key>. The Quickstart shows how to make a key.

CAPTIONBOB_URL is the default address of the server. Change it when your server listens on another host or port. The code of each step runs after this first code. The curl code runs in bash and reads values from the answers with jq. The Python code uses the package requests. The JavaScript code runs in Node.js 22 or later as an ES module, such as a file example.mjs.

The answers on this page are real answers of a captionbob server, recorded on 2026-10-28. Your ids, names and times are different.

curl

CAPTIONBOB_URL="http://127.0.0.1:9400"
CAPTIONBOB_KEY="<your API key>"

Python

import requests

CAPTIONBOB_URL = "http://127.0.0.1:9400"
HEADERS = {"Authorization": "Bearer <your API key>"}

JavaScript

import { readFile, writeFile } from "node:fs/promises";

const CAPTIONBOB_URL = "http://127.0.0.1:9400";
const HEADERS = { Authorization: "Bearer <your API key>" };
let response;
let answer;

Step 1: List the font families

The font families that an effect can name, such as in the setting font_name.

curl

curl -s "$CAPTIONBOB_URL/api/font-families" \
  -H "Authorization: Bearer $CAPTIONBOB_KEY"

Python

response = requests.get(
    f"{CAPTIONBOB_URL}/api/font-families",
    headers=HEADERS,
)
if not response.ok:
    raise RuntimeError(response.text)
answer = response.json()
print(answer)

JavaScript

response = await fetch(`${CAPTIONBOB_URL}/api/font-families`, { headers: HEADERS });
if (!response.ok) {
  throw new Error(await response.text());
}
answer = await response.json();
console.log(JSON.stringify(answer, null, 2));

Answer: status 200.

{
  "families": [
    "DejaVu Sans",
    "DejaVu Sans Mono",
    "DejaVu Serif",
    "Inconsolata",
    "Noto Sans",
    "Noto Sans Adlam",
    "Noto Sans Anatolian Hieroglyphs",
    "Noto Sans Arabic"
  ]
}

The answer has 180 families. This page shows the first 8.

Step 2: Find the font files of a font name

The answer gives the font files that libass can use for the font name, each with the path of its file. fallbackFamily is null when a font has the name.

curl

ANSWER=$(curl -s "$CAPTIONBOB_URL/api/fonts?name=DejaVu%20Sans" \
  -H "Authorization: Bearer $CAPTIONBOB_KEY")
echo "$ANSWER"
FONT_ID=$(echo "$ANSWER" | jq -r '.fonts[0].id')

Python

response = requests.get(
    f"{CAPTIONBOB_URL}/api/fonts",
    headers=HEADERS,
    params={"name": "DejaVu Sans"},
)
if not response.ok:
    raise RuntimeError(response.text)
answer = response.json()
print(answer)
font_id = answer["fonts"][0]["id"]

JavaScript

response = await fetch(`${CAPTIONBOB_URL}/api/fonts?${new URLSearchParams({ name: "DejaVu Sans" })}`, { headers: HEADERS });
if (!response.ok) {
  throw new Error(await response.text());
}
answer = await response.json();
console.log(JSON.stringify(answer, null, 2));
const fontId = answer.fonts[0].id;

Answer: status 200.

{
  "name": "DejaVu Sans",
  "fallbackFamily": null,
  "fonts": [
    {
      "id": 1,
      "name": "DejaVu Sans",
      "path": "/api/fonts/1"
    },
    {
      "id": 64,
      "name": "DejaVu Sans Bold",
      "path": "/api/fonts/64"
    }
  ]
}

Step 3: Ask for a font name that no font has

When no font has the name, fallbackFamily names the family that libass uses in its place. The files are the files of that family.

curl

curl -s "$CAPTIONBOB_URL/api/fonts?name=No%20Such%20Font" \
  -H "Authorization: Bearer $CAPTIONBOB_KEY"

Python

response = requests.get(
    f"{CAPTIONBOB_URL}/api/fonts",
    headers=HEADERS,
    params={"name": "No Such Font"},
)
if not response.ok:
    raise RuntimeError(response.text)
answer = response.json()
print(answer)

JavaScript

response = await fetch(`${CAPTIONBOB_URL}/api/fonts?${new URLSearchParams({ name: "No Such Font" })}`, { headers: HEADERS });
if (!response.ok) {
  throw new Error(await response.text());
}
answer = await response.json();
console.log(JSON.stringify(answer, null, 2));

Answer: status 200.

{
  "name": "No Such Font",
  "fallbackFamily": "Noto Sans",
  "fonts": [
    {
      "id": 21,
      "name": "Noto Sans Regular",
      "path": "/api/fonts/21"
    },
    {
      "id": 88,
      "name": "Noto Sans Bold",
      "path": "/api/fonts/88"
    }
  ]
}

Step 4: Download a font file

The route gives one font file of the font list.

curl

curl -s -D - -o font.ttf "$CAPTIONBOB_URL/api/fonts/$FONT_ID" \
  -H "Authorization: Bearer $CAPTIONBOB_KEY"

Python

response = requests.get(
    f"{CAPTIONBOB_URL}/api/fonts/{font_id}",
    headers=HEADERS,
)
if not response.ok:
    raise RuntimeError(response.text)
with open("font.ttf", "wb") as output_file:
    output_file.write(response.content)
print(response.status_code, response.headers["Content-Type"], len(response.content))

JavaScript

response = await fetch(`${CAPTIONBOB_URL}/api/fonts/${fontId}`, { headers: HEADERS });
if (!response.ok) {
  throw new Error(await response.text());
}
await writeFile("font.ttf", Buffer.from(await response.arrayBuffer()));
console.log(response.status, response.headers.get("content-type"));

Answer: status 200. The body is the file. Its headers are:

HTTP/1.1 200 OK
Content-Type: font/ttf
Content-Length: 757076
Accept-Ranges: bytes

Step 5: Find a font for letters that the fonts do not have

Send the font names of a script and its letters. The answer names the family that the burn takes for the most letters that the fonts of the script do not have, with its font files. The family is null when the fonts of the script have every letter.

curl

curl -s -X POST "$CAPTIONBOB_URL/api/font-fallback" \
  -H "Authorization: Bearer $CAPTIONBOB_KEY" \
  -H "Content-Type: application/json" \
  -d '{"names": ["DejaVu Sans"], "letters": "नमस्ते"}'

Python

response = requests.post(
    f"{CAPTIONBOB_URL}/api/font-fallback",
    headers=HEADERS,
    json={"names": ["DejaVu Sans"], "letters": "नमस्ते"},
)
if not response.ok:
    raise RuntimeError(response.text)
answer = response.json()
print(answer)

JavaScript

response = await fetch(`${CAPTIONBOB_URL}/api/font-fallback`, {
  method: "POST",
  headers: { ...HEADERS, "Content-Type": "application/json" },
  body: JSON.stringify({ names: ["DejaVu Sans"], letters: "नमस्ते" }),
});
if (!response.ok) {
  throw new Error(await response.text());
}
answer = await response.json();
console.log(JSON.stringify(answer, null, 2));

Answer: status 200.

{
  "family": "Noto Sans Devanagari",
  "fonts": [
    {
      "id": 186,
      "name": "Noto Sans Devanagari Bold",
      "path": "/api/fonts/186"
    },
    {
      "id": 193,
      "name": "Noto Sans Devanagari Regular",
      "path": "/api/fonts/193"
    }
  ]
}