API v1 · read-only · JSON
Lexodus for developers
Every one of the 4,830 English word journeys in the atlas (1,025 curated, 3,805 machine-extracted), plus 30,817 journeys into other languages, as JSON: each attested form, the year it appeared, how it moved, and how sure scholars are about every hop. No key is needed to try it.
Quickstart
Fetch a journey without a key (anonymous requests get 100 a day per IP):
curl https://lexodus.app/api/v1/words/amenWith a key from your account, send it as a Bearer token:
curl -H "Authorization: Bearer $LEXODUS_KEY" \
"https://lexodus.app/api/v1/search?q=yohanan"From JavaScript (CORS is open for GET, so this works in a browser too — but keep keys on your server):
const res = await fetch("https://lexodus.app/api/v1/words?category=trade&limit=5", {
headers: { Authorization: `Bearer ${process.env.LEXODUS_KEY}` },
});
if (!res.ok) throw new Error((await res.json()).error.message);
const { data, next_cursor } = await res.json();
for (const w of data) console.log(w.headword, w.from.name, "→", w.to.name, w.confidence);Lists are paged with limit and cursor: pass the next_cursor of one page as the cursor of the next, until it is null.
Keys and limits
Create, name and revoke keys on your account page. A key is shown in full once, when you create it; we store only a hash. ?api_key= also works for quick tests, but it ends up in logs and browser history, so use the header in real code.
| Who | Requests per day |
|---|---|
| No key | 100 per IP address |
| Free plan, with a key | 100 per account, across all its keys |
| Pro plan, with a key | 1,000 per account, across all its keys |
| API plan, with a key | 50,000 per account, across all its keys |
Days are UTC. Every response says where you stand: X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset (Unix seconds of the next midnight UTC) and X-RateLimit-Scope (key or ip). Past the limit you get a 429 with Retry-After. Every answered request counts, errors and 304s included. Responses carry an ETag; send it back as If-None-Match to get a body-less 304 when nothing changed.
Reading the data
Each step after the first has a confidence for the link into it — certain, probable, possible or disputed — and each word has an overall grade equal to its weakest link. A disputed journey is in the atlas because the claim is popular, not because it is true; many such words carry a myth field explaining the wrong story. Forms marked * are reconstructed, not attested. Please keep the grades visible when you show a journey; the method page explains them.
Every word has a provenance. curated words are written and checked by us; extracted ones (and everything under /x) were generated from Wiktionary and nobody has reviewed them yet. Years with approx: true are estimated from the language’s period.
License. Extracted items are derived from Wiktionary and licensed CC BY-SA 4.0: each carries a license object, and if you reuse them you must show its attribution and share adaptations alike. The license of the curated words is to be decided by the owner; until one is published, ask before republishing them in bulk.
Errors
Errors are JSON, { "error": { "code": "…", "message": "…" } }, with a matching HTTP status. The API is read-only: methods other than GET (and OPTIONS, for CORS) get a bare 405.
| Status | Code | When |
|---|---|---|
| 400 | invalid_parameter | A query parameter is missing or not one of the allowed values. |
| 401 | invalid_key | The key isn't a Lexodus key, or doesn't exist. |
| 401 | revoked_key | The key was revoked on the account page. |
| 404 | not_found | No word, language or root with that slug or id, or no such endpoint. |
| 429 | rate_limited | Today's limit is used up. Retry-After says how many seconds until 00:00 UTC. |
| 500 | internal | Our fault. Try again; tell us if it keeps happening. |
| 503 | keys_unavailable | Keys can't be checked right now. Requests without a key still work. |
Endpoints
All under /api/v1. The machine-readable version is /api/v1/openapi.json.
GET /words
List words
Every English word journey in the atlas — curated and machine-extracted (provenance) — sorted by slug, filtered and paged. Filters combine with AND; a comma-separated filter matches any of its values. Journeys that end in other languages are under /x.
| Parameter | Type | Description |
|---|---|---|
q | string | Text in the headword or in any form or transliteration (diacritics and case ignored). |
category | string | Category filter. |
origin | string | Source-language family filter. |
lang | string | Only words whose journey passes through this language id (main line or branch). |
confidence | string | Overall (weakest-link) confidence filter. |
provenance | string | curated: written and reviewed by Lexodus. extracted: machine-extracted from Wiktionary, unreviewed, CC BY-SA 4.0. |
limit | integer | Results per page (1–100, default 20). |
cursor | string | next_cursor from the previous page. |
GET /words/{slug}
One word's journey
The full journey: every step with its form, gloss, year, how it arrived (via) and how sure scholarship is (confidence), side branches, sources, and the computed playback order the site animates (origin first, then every hop by the year it landed).
| Parameter | Type | Description |
|---|---|---|
slug required path | string | Word slug. |
Try it: /api/v1/words/amen
GET /x
List destination languages
Journeys that end in a language other than English (Persian ابریق, Spanish alcalde…), machine-extracted from Wiktionary. One entry per destination language, with its word count.
Try it: /api/v1/x
GET /x/{code}
List words for one destination language
The journeys ending in one destination language, with the same filters and paging as /words. Every item is extracted and carries the CC BY-SA 4.0 license.
| Parameter | Type | Description |
|---|---|---|
code required path | string | Destination code from /x, e.g. fa. |
q | string | Text in the headword or in any form or transliteration. |
origin | string | Source-language family filter. |
lang | string | Only words whose journey passes through this language id. |
limit | integer | Results per page (1–100, default 20). |
cursor | string | next_cursor from the previous page. |
Try it: /api/v1/x/fa?limit=2
GET /x/{code}/{slug}
One foreign-destination journey
The same shape as /words/{slug}, for a journey that ends in the destination language.
| Parameter | Type | Description |
|---|---|---|
code required path | string | Destination code from /x, e.g. fa. |
slug required path | string | Word slug within that destination. |
Try it: /api/v1/x/fa/a-ila
GET /languages
List languages
Every language a journey may pass through, with its family, a representative center, its span of written use and how many atlas words pass through it.
| Parameter | Type | Description |
|---|---|---|
active | true | false | true: only languages at least one word passes through. |
Try it: /api/v1/languages?active=true
GET /languages/{id}
One language's profile
Which languages words arrived from, which they were passed on to (counted in distinct words), and the words that start, pass through, end, or only branch here.
| Parameter | Type | Description |
|---|---|---|
id required path | string | Language id, e.g. hbo, grc-koi, yi. |
Try it: /api/v1/languages/yi
GET /roots
List root families
Semitic consonantal roots and the atlas words built on them. The list is filled as research is reviewed, so it may be short or empty.
Try it: /api/v1/roots
GET /roots/{slug}
One root family
A root with its meaning, words, related forms that are not atlas entries, and sources.
| Parameter | Type | Description |
|---|---|---|
slug required path | string | Root slug, e.g. s-l-m. |
Try it: /api/v1/roots/s-l-m
GET /search
Search words
The site's search box as an endpoint, over every journey (curated, extracted and every destination language): diacritics, case and ʾ ʿ ' * are ignored, and a query matches the headword or any form or transliteration on any step (so Yōḥānān, yohanan and יוחנן all find John). Ranked: exact headword, headword prefix, headword, then form; curated before extracted within each rank.
| Parameter | Type | Description |
|---|---|---|
q required | string | Search text (at most 100 characters). |
limit | integer | Results per page (1–50, default 10). |
Try it: /api/v1/search?q=yohanan
GET /flows
Aggregated hops between languages
Every hop of every curated journey (the same set the site's flow map draws), aggregated by language pair: how many distinct words made it and when. Filter to hops touching one language, and to hops that landed in a year range.
| Parameter | Type | Description |
|---|---|---|
lang | string | Only hops into or out of this language id. |
from | integer | Earliest landing year (negative = BCE). |
to | integer | Latest landing year. |
Try it: /api/v1/flows?lang=grc-koi