Taiwan Mandarin
⌘ K
Display

Saved on this device.

Pronunciation
how readings are shown
Changes apply instantly.
json api · reference

The data, as JSON.

Every character and every word on this site is available as JSON — frequency ranks, readings, definitions, radicals, TOCFL levels and example words. No key, no sign-up, no rate limit to negotiate. Point a browser, a script, or a language-learning app at it.

endpoints

The endpoints

Everything hangs off https://api.taiwanmandarin.com. Responses are application/json, UTF-8, and identical for every caller — there is nothing to authenticate. Two entities — characters and words — two ways to browse them, by Kangxi radical and by TOCFL level, and a search for when you have a meaning rather than a glyph.

GET/characters

List characters, ordered by Taiwan frequency rank.

The whole inventory of 4,974 characters, most common first, narrowed by any combination of the filters below. Filters combine with AND.

NameTypeNotes
minRankintegerOnly characters whose taiwan_rank is greater than or equal to this.
maxRankintegerOnly characters whose taiwan_rank is less than or equal to this. maxRank=100 is “the hundred characters worth learning first”.
radicalstringExact Kangxi radical character, e.g. . Percent-encode it in the URL.
strokesintegerExact total stroke count.
pageintegerdefault 11-indexed, up to 1,000. Past that is a 400 — never a different page than the one you asked for.
pageSizeintegerdefault 100Clamped to the range 1–500.
Seven-stroke characters built on the 木 radical
https://api.taiwanmandarin.com/characters?strokes=7&radical=木
Response
{
  "page": 1,
  "pageSize": 100,
  "total": 2,
  "totalPages": 1,
  "results": [ { "char": "的", … }, { "char": "一", … } ]
}

A page past the end is not an error — it returns an empty results array with the real total.

GET/characters/:char

Look up one character by exact match.

The character itself is the path segment, percent-encoded — /characters/%E6%B0%B4 for . Returns the full row plus example_words. A character we don't hold returns 404.

GET/words

List words, ordered by Taiwan frequency rank.

The same envelope as /characters, over the 30,117 words in the table. Only words carrying a frequency rank are listed — the handful of TOCFL-graded words the corpus never attests are still reachable by name, and in full at /tocfl/:level.

NameTypeNotes
minRankintegerOnly words whose taiwan_rank is greater than or equal to this.
maxRankintegerOnly words whose taiwan_rank is less than or equal to this.
containsstringOne character; returns the words containing it anywhere — contains=學 gives 學生, 大學, 同學. The character-to-vocabulary direction. More than one character is a 400, not an empty page.
pageintegerdefault 11-indexed, up to 1,000. Past that is a 400 — never a different page than the one you asked for.
pageSizeintegerdefault 100Clamped to the range 1–500.
The 50 most common words in Taiwan
https://api.taiwanmandarin.com/words?maxRank=50
Vocabulary built on 學, most common first
https://api.taiwanmandarin.com/words?contains=學
GET/words/:word

Look up one word by exact match.

The word itself is the path segment, percent-encoded. Returns the word's row plus characters — the complete record for each component character, so a client can explain the word from its parts without a second round-trip. A word we don't hold returns 404.

GET/radicals

The Kangxi radicals in use, in Kangxi order.

The 214 semantic components characters are built from — the traditional index of a Chinese dictionary. Each entry carries the glyph, its ordinal, a one-word English gloss, and count: how many characters in the inventory use it. Radicals no character in the inventory uses are omitted.

Every radical, with its size
https://api.taiwanmandarin.com/radicals
GET/radicals/:radical

The characters built on one radical.

The radical glyph is the path segment, percent-encoded. Ordered most useful first — Taiwan frequency, then stroke count, the way a paper dictionary arranges a radical's section. The response leads with the radical's own number and meaning. A glyph outside the 214 returns 404; a radical with no characters is simply an empty results array.

NameTypeNotes
pageintegerdefault 11-indexed, up to 1,000. Past that is a 400 — never a different page than the one you asked for.
pageSizeintegerdefault 100Clamped to the range 1–500.

/characters?radical=水 answers a similar question, but as a plain frequency list with no radical metadata and without the characters the frequency corpus never attests. Use that one to combine the radical with other filters, this one to present the radical as a group.

GET/tocfl

The seven TOCFL levels, with entry counts.

Taiwan's official graded vocabulary, 華語八千詞. Each level carries its slug — pass that to the endpoint below. The slugs matter because the stored ordinal and the display name differ: ordinal 5 is “Level 3”.

The levels and their sizes
https://api.taiwanmandarin.com/tocfl
GET/tocfl/:level

The vocabulary at one TOCFL level, frequency-ordered.

Characters and words together, projected from both tables, unranked entries last. The level is addressed by slug — novice-1, novice-2, level-1level-5. An unknown slug returns 404 with the valid list.

NameTypeNotes
kindstringdefault bothchar for single-character entries, word for multi-character vocabulary, both for everything.
pageintegerdefault 11-indexed, up to 1,000. Past that is a 400 — never a different page than the one you asked for.
pageSizeintegerdefault 100Clamped to the range 1–500.
Response
{
  "level": 1,
  "levelName": "Novice 1",
  "levelSlug": "novice-1",
  "kind": "both",
  "page": 1,
  "pageSize": 100,
  "total": 1268,
  "totalPages": 13,
  "results": [
    { "kind": "word", "word": "我們", "taiwan_rank": 63, "tocfl_level": 1, … },
    { "kind": "char", "char": "的",   "taiwan_rank": 1,  "tocfl_level": 1, … }
  ]
}

Entries are ordinary character and word objects — the same fields you get from /characters and /words. Read kind to know which of char or word holds the term; no separate parser needed.

GET/search

Find characters and words by meaning or pronunciation.

For when you have an English meaning or a reading rather than a glyph. Matches definitions and pinyin — with tone marks, tone digits, or neither — and returns characters and words in separate arrays, best match first: exact reading, then a definition that leads with the searched meaning, then frequency. Lexical, not semantic: a near-synonym the definition doesn't use won't be found.

NameTypeNotes
qstringrequiredEnglish, pinyin, or the Han text itself. Missing or empty returns 400.
kindstringdefault bothchar, word, or both. The unsearched array comes back empty rather than absent.
limitintegerdefault 20Results per kind. Clamped to the range 1–50.
Response
{
  "query": "computer",
  "kind": "both",
  "characters": [],
  "words": [ { "word": "電腦", … }, { "word": "記憶體", … }, { "word": "滑鼠", … } ]
}

A query that matches nothing returns two empty arrays, not an error.

The root, https://api.taiwanmandarin.com/, returns a short discovery document listing these same endpoints — handy for a client that wants to introspect rather than read this page.

response

What a character looks like

One flat object per character. Every field except char and unicode can be null — the honest answer when a source doesn't cover that character, rather than a filled-in guess.

NameTypeNotes
charstringThe character itself. Traditional script, Taiwan standard glyph forms.
unicodestringCodepoint, e.g. U+6C34.
taiwan_rankinteger1 = most frequent in Taiwan. Derived from the COCT 通用詞頻表, a Taiwan-native traditional-script corpus.
pinyinstringPrimary reading, tone marks included.
pinyin_readingsstring[]Every reading, most frequent first. Always an array, never null — a single-reading character returns one element, matching pinyin.
bopomofostring注音 for the primary reading, Moedict format (the neutral-tone dot leads).
englishstringA learner-facing gloss, written by a language model from the Unihan and Moedict zh-TW definitions — the one field here that is generated rather than sourced.
stroke_countintegerTotal strokes, from Unihan kTotalStrokes.
radicalstringThe Kangxi radical character, from Unihan kRSUnicode.
radical_meaningstringOne-word English gloss for that radical — → “tree”.
radical_numberintegerIts Kangxi ordinal, 1–214 — the key that lines a character up with /radicals, and the traditional sort order.
tocfl_levelinteger1–7 if the character is itself a TOCFL 華語八千詞 term: 1–2 Novice, 3–7 Levels 1–5. Note the offset — the ordinal is not the displayed level.
tocfl_level_namestringWhat that ordinal is called: 5 → “Level 3”. Read this rather than rendering the ordinal.
tocfl_level_slugstringThe same level as a URL key — level-3. What /tocfl/:level takes.
posstringPart of speech from the TOCFL list, raw tag (N, V, Ptc…). Composite tags are slash-joined.
pos_labelstringThe same tag in English — Vs → “stative verb (adj.)”.
example_wordsstring[]single lookup onlyUp to ten common words built on the character, most frequent first. Absent from list rows.
GET https://api.taiwanmandarin.com/characters/水
{
  "char": "水",
  "unicode": "U+6C34",
  "taiwan_rank": 181,
  "pinyin": "shuǐ",
  "pinyin_readings": ["shuǐ"],
  "bopomofo": "ㄕㄨㄟˇ",
  "english": "Water; liquid; juice; river; to swim; …",
  "stroke_count": 4,
  "radical": "水",
  "radical_meaning": "water",
  "radical_number": 85,
  "tocfl_level": 1,
  "tocfl_level_name": "Novice 1",
  "tocfl_level_slug": "novice-1",
  "pos": "N",
  "pos_label": "noun",
  "example_words": ["水果", "水準", "水庫", "薪水", "淡水"]
}
response

What a word looks like

Shorter than a character — a word has no radical and no stroke count of its own, because those belong to the characters it's made of. Everything else carries the same meaning as the character field of the same name.

NameTypeNotes
wordstringThe word itself, in traditional characters.
taiwan_rankinteger1 = most frequent in Taiwan, from the same COCT 通用詞頻表 counts that rank the characters. null for a TOCFL word the corpus never attests.
pinyinstringTOCFL's reading where the word is graded — pinned to the graded pronunciation — Moedict's otherwise. Note the two sources space syllables differently.
bopomofostring注音 for the same reading.
englishstringA learner-facing gloss, written by a language model. Roughly half the word inventory is still null here — the generation run is incremental and unfinished.
tocfl_levelinteger1–7 if the word is on the 華語八千詞 list, with tocfl_level_name and tocfl_level_slug alongside it, exactly as on a character.
posstringPart of speech from the TOCFL list, raw tag, plus pos_label in English.
charactersobject[]single lookup onlyThe component characters, in written order, each a full character object. Absent from list rows.
GET https://api.taiwanmandarin.com/words/電腦
{
  "word": "電腦",
  "taiwan_rank": 538,
  "pinyin": "diànnǎo",
  "bopomofo": "ㄉㄧㄢˋ ㄋㄠˇ",
  "english": "computer; an electronic device that processes data …",
  "tocfl_level": 1,
  "tocfl_level_name": "Novice 1",
  "tocfl_level_slug": "novice-1",
  "pos": "N",
  "pos_label": "noun",
  "characters": [
    { "char": "電", "pinyin": "diàn", "english": "Lightning; electricity; …", … },
    { "char": "腦", "pinyin": "nǎo", "english": "brain; mind; head; …", … }
  ]
}
conventions

Using it

There's an OpenAPI document

The whole contract above is published as OpenAPI 3.1 at https://api.taiwanmandarin.com/openapi.json — point a client generator, an HTTP client or an agent at it and skip reading this page. It's generated from the same modules that enforce the limits, so it can't promise bounds the Worker doesn't apply.

No authentication

There are no keys and no accounts. The data is public, read-only, and free to use — including in something you charge for. An attribution link back is appreciated, not required.

Cross-origin requests work

Every response carries Access-Control-Allow-Origin: *, so a browser app can call this directly with no proxy in between.

Cached, and stable between reseeds

Responses are cacheable (five minutes in the browser, an hour at the edge). The underlying data only changes when the database is rebuilt, so a cached response is a current one.

Errors are JSON too

A parameter that should be an integer and isn't returns 400 with {"error": …, "value": …}. A character or word we don't hold returns 404 with {"error": "not found", …}. A filter that matches nothing is not an error — it's an empty results array.

Hand the same data to an LLM over MCP ›Where the data comes from ›Browse the ranking as pages ›