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. |
| 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.accuracy, r.completeness, 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. speaking_rate is measured in subwords (syllables) per second.
{
"text": "Guten Morgen",
"speaking_rate": 12.5,
"accuracy": 0.7777777910232544,
"completeness": 1,
"words": [
{
"text": "Guten",
"syllables": [
{
"text": "Gu",
"expected_ipa": "ɡuː",
"detected_ipa": "ɡu",
"accuracy": 1,
"completeness": 1,
"pitch": "low",
"duration_ms": 60,
"start_ms": 20,
"end_ms": 40,
"is_missing": false,
"is_extra": false
},
{
"text": "ten",
"expected_ipa": "tn̩",
"detected_ipa": "tn",
"accuracy": 1,
"completeness": 1,
"pitch": "high",
"duration_ms": 100,
"start_ms": 70,
"end_ms": 110,
"is_missing": false,
"is_extra": false
}
],
"accuracy": 1,
"completeness": 1,
"start_ms": 20,
"end_ms": 110,
"char_start": 0,
"char_end": 5
},
{
"text": "Morgen",
"syllables": [
{
"text": "Mor",
"expected_ipa": "mɔʁ",
"detected_ipa": "ma",
"accuracy": 0.3333333134651184,
"completeness": 0.6666666666666667,
"pitch": "high",
"duration_ms": 60,
"start_ms": 160,
"end_ms": 180,
"is_missing": false,
"is_extra": false
},
{
"text": "gen",
"expected_ipa": "ɡn̩",
"detected_ipa": "ɡn",
"accuracy": 1,
"completeness": 1,
"pitch": "flat",
"duration_ms": 100,
"start_ms": 220,
"end_ms": 260,
"is_missing": false,
"is_extra": false
}
],
"accuracy": 0.6000000238418579,
"completeness": 0.5,
"start_ms": 160,
"end_ms": 260,
"char_start": 6,
"char_end": 12
}
]
}| Field | Description |
|---|---|
| text | Word text |
| accuracy | Mean accuracy across the word's syllables (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 |
| 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 |