Integrate pronunciation scoring into your application with our REST API
Authenticate your requests using an API key in the Authorization header:
Authorization: Bearer lx_your_api_key_here⚠️ Keep your API key secure: Never expose your API key in client-side code or public repositories.
🔬 Syllable-Level Precision
The V3 API delivers the deepest pronunciation analysis available: every word is broken down to individual syllables with IPA transcription, pitch class and millisecond timing, across 17 languages.
/api/pronunciation/v3/checkSyllable-level pronunciation analysis with 17-language support
✨ Operating Modes:
sentence parameter → Scores pronunciation syllable-by-syllable against expected textsentence parameter → Whisper AI transcribes your speech first, then scores pronunciation| Parameter | Type | Required | Description |
|---|---|---|---|
| speechdata | file | Yes | Audio file (WAV, MP3, M4A, OGG, FLAC, WEBM). Max 10 MiB and 60 seconds; longer recordings are rejected with status code 400. |
| sentence | string | No | Expected text to compare against. If omitted, Whisper transcribes speech first ("Just Talk" mode). |
| language_code | string | No | Language code (default: ja). One of the 17 codes listed below. |
💡 Send compressed audio
Encode client-side to MP3, Opus/WebM or AAC before uploading. A 10-second utterance is roughly 320 KB as 16 kHz mono WAV but under 40 KB as 64 kbps Opus — the same score, a fraction of the upload time. Uploads over 10 MiB are rejected with status code 413.
lingolix (PyPI) · @lingolix/sdk (npm)
you should set the API key as an environment variable:
export LINGOLIX_API_KEY=your_api_key_here
# pip install lingolix (or: uv add lingolix)
from lingolix import Lingolix, QuotaExceededError
# WARNING: Keep API keys server-side only! Never expose in client-side code.
# Lingolix() reads $LINGOLIX_API_KEY. AsyncLingolix is the async twin.
with Lingolix() as client:
try:
# Known text mode
r = client.check("audio.wav", sentence="Guten Morgen", language="de")
# Just Talk mode (sentence omitted)
# r = client.check("audio.wav", language="de")
print(r.overall, r.accuracy, r.fluency, r.speaking_rate)
for word in r.words:
for s in word.syllables:
print(s.text, s.expected_ipa, "→", s.detected_ipa, s.pitch, round(s.accuracy, 2))
except QuotaExceededError as e:
print(f"{e.plan} plan is out of minutes — upgrade at {e.upgrade_url}")Each word contains a syllables array with per-syllable IPA and timing.
{
"text": "Guten Morgen",
"overall": 0.81,
"accuracy": 0.78,
"fluency": 1,
"completeness": 1,
"speaking_rate": 4.4,
"target_speaking_rate": 3,
"model_version": "2026-10",
"words": [
{
"text": "Guten",
"syllables": [
{
"text": "Gu",
"expected_ipa": "ɡuː",
"detected_ipa": "ɡuː",
"accuracy": 1,
"completeness": 1,
"pitch": "low",
"duration_ms": 200,
"start_ms": 300,
"end_ms": 500,
"is_missing": false,
"is_extra": false
},
{
"text": "ten",
"expected_ipa": "tn̩",
"detected_ipa": "tn̩",
"accuracy": 1,
"completeness": 1,
"pitch": "high",
"duration_ms": 200,
"start_ms": 520,
"end_ms": 720,
"is_missing": false,
"is_extra": false
}
],
"accuracy": 1,
"completeness": 1,
"start_ms": 300,
"end_ms": 720,
"char_start": 0,
"char_end": 5,
"hesitation_ms": null
},
{
"text": "Morgen",
"syllables": [
{
"text": "Mor",
"expected_ipa": "mɔʁ",
"detected_ipa": "ma",
"accuracy": 0.33,
"completeness": 0.67,
"pitch": "high",
"duration_ms": 200,
"start_ms": 760,
"end_ms": 960,
"is_missing": false,
"is_extra": false
},
{
"text": "gen",
"expected_ipa": "ɡn̩",
"detected_ipa": "ɡn̩",
"accuracy": 1,
"completeness": 1,
"pitch": "flat",
"duration_ms": 220,
"start_ms": 980,
"end_ms": 1200,
"is_missing": false,
"is_extra": false
}
],
"accuracy": 0.42,
"completeness": 1,
"start_ms": 760,
"end_ms": 1200,
"char_start": 6,
"char_end": 12,
"hesitation_ms": null
}
]
}Show overall as the score; accuracy and fluency explain it, and the words and syllables show where.
| Field | Description |
|---|---|
| overall | The score to show (0.0 – 1.0): accuracy and fluency weighted as human raters weigh them. Always present; equals accuracy when fluency is null |
| accuracy | How well the sounds were pronounced (0.0 – 1.0) |
| fluency | Pace and pauses (0.0 – 1.0): full credit from target_speaking_rate on, less for each hesitation. null when the utterance is too short to time (under 4 syllables) |
| completeness | Fraction of the expected utterance that was said |
| speaking_rate | Syllables (Japanese: morae) per second, from the first syllable's start to the last's |
| target_speaking_rate | Pace from which fluency gets full credit, in the same unit: 60% of a typical native pace for the language, so learners have room |
| model_version | Which model and scoring produced these numbers, e.g. "2026-10". It changes when an update shifts scores, so you can tell results from different versions apart in stored history |
| Field | Description |
|---|---|
| text | Word text |
| accuracy | How well the word's sounds were pronounced, judged by its weakest sound (0.0 – 1.0) |
| completeness | Fraction of the word's syllables detected |
| start_ms | Start offset in milliseconds |
| end_ms | End offset in milliseconds |
| char_start | Character index of this word in the response's text field. -1 for Japanese and for extra words the user said that weren't in the expected text |
| char_end | Character index (exclusive) where this word ends in the response's text field. -1 under the same conditions as char_start |
| hesitation_ms | Pause before or inside this word, in milliseconds beyond the speaker's usual gap; null if none. Pauses at punctuation never count |
| Field | Description |
|---|---|
| text | Syllable text (e.g. "ni") |
| expected_ipa | Expected IPA transcription |
| detected_ipa | Detected IPA from audio |
| accuracy | Phoneme-level accuracy (0.0 – 1.0) |
| completeness | Fraction of expected phonemes detected |
| pitch | Pitch class: high | low | flat | unknown |
| duration_ms | Syllable duration in milliseconds |
| start_ms | Start offset in milliseconds |
| end_ms | End offset in milliseconds |
| is_missing | true if the user skipped this syllable |
| is_extra | true if the user said something not in target |
| Language | Code |
|---|---|
| 🇯🇵 Japanese | ja |
| 🇬🇧 English | en |
| 🇩🇪 German | de |
| 🇫🇷 French | fr |
| 🇪🇸 Spanish | es |
| 🇭🇺 Hungarian | hu |
| 🇧🇬 Bulgarian | bg |
| 🇨🇿 Czech | cs |
| 🇬🇷 Greek | el |
| 🇫🇮 Finnish | fi |
| 🇮🇹 Italian | it |
| 🇰🇷 Korean | ko |
| 🇳🇱 Dutch | nl |
| 🇵🇱 Polish | pl |
| 🇵🇹 Portuguese | pt |
| 🇸🇪 Swedish | sv |
| 🇺🇦 Ukrainian | uk |
Your monthly quota is measured in audio minutes and is shared across all your API keys. Check your current usage in the Dashboard.
💡 Tip: Monitor your usage regularly to avoid unexpected charges. Upgrade your plan in the Subscription page.
| Status Code | Error | Solution |
|---|---|---|
| 401 | Invalid or missing API key | Check your Authorization header |
| 400 | Invalid audio format | Use WAV, MP3, M4A, OGG, FLAC, or WEBM format |
| 413 | Audio file too large | Keep uploads under 10 MiB; encode to MP3/Opus instead of WAV |
| 429 | Quota exceeded | Upgrade plan or wait for next billing period |
| 503 | Service unavailable | Retry after a short delay |