API

One endpoint: send text lines and optional alignment pairs, get back a shareable Word Aligner URL. No API key, no sign-up.

OpenAPI schema: /api/align/openapi.json

Want your AI assistant to call this for you? Install the Word Aligner agent skill for Claude, ChatGPT, or any tool-using agent.

Prefer a native tool? Connect the MCP server at https://aligner.tinygods.dev/mcp. See the skill page for setup in Claude and ChatGPT.

POST /api/align

The main endpoint. Returns a URL to Word Aligner with the given lines and alignment links pre-filled.

Request body

Content-Type: application/json

FieldTypeDescription
lines(string | LineInput)[] requiredText lines, top to bottom. 1–8 entries. Each may be a plain string or a LineInput object with per-line visual options.
alignments[int,int,int,int][] optionalWord-alignment pairs as [lineA, wordA, lineB, wordB]. Lines A and B must be adjacent (|A−B| = 1). Indices are 0-based. Multiple pairs can share the same word (many-to-one) — they receive the same color automatically.
settingsSettingsInput optionalGlobal visual overrides: palette, line style, thickness, opacity, background. See Visual settings.
pairsPairInput[] optionalPer-pair controls: vertical gap between adjacent lines or hiding connectors for a specific pair. See Pair controls.

Response

{ "url": "https://aligner.tinygods.dev/?data=..." }

Example — simple alignment

curl -X POST https://aligner.tinygods.dev/api/align \
  -H "Content-Type: application/json" \
  -d '{
    "lines": ["Hello world", "Bonjour le monde"],
    "alignments": [
      [0, 0, 1, 0],
      [0, 1, 1, 1],
      [0, 1, 1, 2]
    ]
  }'

Links "Hello" → "Bonjour" (word 0 → word 0). "world" → "le" + "monde" (words 1 and 2) — the French article is part of the noun phrase, so both words share the same color.

Example — many-to-one (one word maps to several)

curl -X POST https://aligner.tinygods.dev/api/align \
  -H "Content-Type: application/json" \
  -d '{
    "lines": ["Я ходил", "I have been going"],
    "alignments": [
      [0, 0, 1, 0],
      [0, 1, 1, 1],
      [0, 1, 1, 2],
      [0, 1, 1, 3]
    ]
  }'

"ходил" (word 1) maps to three English words. All three connections share the same color automatically.

GET /api/align

Simple variant: pass lines as repeated query parameters. No alignments. Useful for opening the editor pre-filled via a plain link.

GET /api/align?lines=Hello+world&lines=Bonjour+le+monde

Parameter: lines — repeat for each line (1–8).

Per-line options (LineInput)

Instead of a plain string, each entry in lines can be an object:

FieldTypeDefaultDescription
textstring requiredLine text.
fontstringInterGoogle Fonts family name, e.g. "Noto Serif", "Noto Sans Arabic", "Noto Sans Hebrew".
sizePxinteger 12–6436Text size in px.
gapPxinteger 0–5614Horizontal gap between word tokens in px.
rtlbooleanfalseRight-to-left layout. Use for Hebrew, Arabic, Farsi, Urdu, etc. Word indices remain 0-based left-to-right in logical order.
orientationupright vertical sidewaysuprightHow this line's glyphs are set, visible when settings.axis is columns. vertical stacks the characters (Japanese, Chinese); sideways rotates the whole line a quarter turn (traditional Mongolian, and Latin runs inside vertical text).

Example — Hebrew with RTL and custom font

curl -X POST https://aligner.tinygods.dev/api/align \
  -H "Content-Type: application/json" \
  -d '{
    "lines": [
      { "text": "שלום עולם", "rtl": true, "sizePx": 48, "font": "Noto Sans Hebrew" },
      { "text": "Hello world", "sizePx": 40 }
    ],
    "alignments": [
      [0, 0, 1, 0],
      [0, 1, 1, 1]
    ]
  }'

Example — Japanese tategaki beside an English translation

settings.axis turns the whole diagram into columns; orientation decides which line stacks its characters. The first line is the leftmost column, so the translation comes first and the Japanese lands on the right, where a vertical text belongs.

curl -X POST https://aligner.tinygods.dev/api/align \
  -H "Content-Type: application/json" \
  -d '{
    "lines": [
      { "text": "The cat ate the fish", "sizePx": 26 },
      { "text": "猫 が 魚 を 食べた", "orientation": "vertical", "font": "Noto Serif JP", "sizePx": 34 }
    ],
    "alignments": [
      [0, 0, 1, 0],
      [0, 1, 1, 0],
      [0, 3, 1, 2],
      [0, 4, 1, 2],
      [0, 2, 1, 4]
    ],
    "settings": { "axis": "columns" }
  }'

Japanese and Chinese are written without spaces and nothing is segmented for you: put spaces where the alignment units should be. For traditional Mongolian use "orientation": "sideways" instead, and keep the script line first, since its columns read left to right.

Visual settings (SettingsInput)

The optional settings object overrides global visual parameters. Unset fields inherit defaults.

FieldValuesDefaultDescription
axisrows columnsrowsFlow direction of the diagram. columns stands every line up as a vertical column and runs the connectors sideways, for Japanese, Chinese, and Mongolian. The first line is the leftmost column, so reorder lines to move a script to the other side.
palettepastel vividpastelColor palette for connection lines and token tints.
lineStylecurved straightcurvedShape of connection lines.
lineThicknessnumber 1–83Stroke width of connection lines.
lineOpacitynumber 0.2–11Opacity of connection lines.
backgroundlight darklightPreview background color.
themelight darklightUI theme (affects token chip color).
showNumbersbooleanfalseShow line numbers next to each line.
colorTokensByLinkbooleantrueTint word tokens in the color of their connection.
tokenSplitCharsstring.-|Characters (besides whitespace) that split text into tokens. The split character is not rendered. Set to -| to keep periods inside Leipzig gloss morphemes (e.g. go.PST.IPFV stays one token).
tokenMergeCharstring (1 char)+Joins parts into one token while rendering as a space, e.g. is+playing → "is playing" (one word).

Example — vivid palette, straight lines, dark background

curl -X POST https://aligner.tinygods.dev/api/align \
  -H "Content-Type: application/json" \
  -d '{
    "lines": ["Hello world", "Bonjour le monde"],
    "alignments": [[0, 0, 1, 0], [0, 1, 1, 2]],
    "settings": {
      "palette": "vivid",
      "lineStyle": "straight",
      "background": "dark",
      "theme": "dark",
      "lineThickness": 2
    }
  }'

Pair controls (PairInput)

The optional pairs array lets you adjust the vertical gap between specific adjacent line pairs, or hide connectors entirely for a pair (useful when some lines are glosses that annotate but do not align to the line above).

FieldTypeDefaultDescription
upperinteger required0-based index of the earlier line in the stack: the upper one in rows, the left one in columns.
lowerinteger required0-based index of the next line in the stack (must equal upper + 1): below in rows, to the right in columns.
gapPxinteger 12–156120Gap between the two lines in px, measured across the stack.
showConnectorsbooleantrueWhen false, connection lines are not drawn between this pair. Alignment data is still encoded.

Example — interlinear (Leipzig) gloss

curl -X POST https://aligner.tinygods.dev/api/align \
  -H "Content-Type: application/json" \
  -d '{
    "lines": [
      { "text": "1SG.NOM go.PST.IPFV", "sizePx": 22 },
      { "text": "Я ходил", "sizePx": 40 },
      { "text": "I have been going", "sizePx": 36 }
    ],
    "alignments": [
      [0, 0, 1, 0], [0, 1, 1, 1],
      [1, 0, 2, 0],
      [1, 1, 2, 1], [1, 1, 2, 2], [1, 1, 2, 3]
    ],
    "settings": { "tokenSplitChars": "-|" },
    "pairs": [
      { "upper": 0, "lower": 1, "gapPx": 12, "showConnectors": false }
    ]
  }'

The gloss tier sits directly above the source (lines 0–1) with a tight 12 px gap and hidden arcs — its tokens are color-matched to the source words but no lines are drawn. The source→translation pair keeps its arcs. Note "tokenSplitChars": "-|": this drops the period from the split set so a Leipzig morpheme like go.PST.IPFV stays a single token instead of rendering as goPSTIPFV.

Word indices and tokenization

Words are counted from 0, left to right as written (logical order — even for RTL lines). The default split rules: whitespace always splits, and the characters . - | also create word boundaries. Punctuation is not split into separate tokens by default.

The split character itself is not rendered — it is consumed as a boundary. So go.PST.IPFV with the default split set displays as three tokens go PST IPFV with the dots removed. If you need the period to stay (common in Leipzig glosses), override settings.tokenSplitChars to "-|".

Example — "Bonjour le monde":

IndexWord
0Bonjour
1le
2monde

If you are unsure about word counts, use the GET endpoint without alignments first — open the returned URL and count the word boxes in the editor.

Errors

Errors return HTTP 400 with a JSON body:

{ "error": "alignments[0]: lines 0 and 2 are not adjacent" }

All endpoints support CORS (Access-Control-Allow-Origin: *).