For developers
API reference
Every endpoint, with its parameters, the fields it answers and a curl example you can run. Find one by name or path below; what each error means is under API errors.
Operations
POST
/api/assessAssess pronunciation
Send a recording and the text that was read; get scores, every word and every sound back.
Multipart form upload. Returns six scores from 0 to 100 (accuracy, native_likeness, completeness, fluency, clarity and speech_rate_ratio), a breakdown of every word with each expected and heard sound, and aligned_phonemes: the recording cut into sounds, with timings, for drawing it.
Your API key. Required: an X-API-Key request header with the key from your operator console.
Parameters
| Name | Where | Type | Required | Description |
|---|---|---|---|---|
| reference_text | form | string | ✓ | The text the learner read. 1 to 300 characters; a text of only spaces is refused. |
| language | form | string | ✓ | Language of the text, exactly one of en-GB, en-US, es-ES, de-DE, fr-FR, it-IT, ca-ES. Case matters: en-gb is refused with 400 assess.unsupportedLanguage. en-GB is British English, en-US American. |
| audio | form | file | ✓ | The recording. WAV (16 kHz, 16-bit, mono) is best; other formats such as WebM or OGG are converted. Uploads over about 1 MB get HTTP 413, and only the first 15 seconds of a recording are scored. |
| X-API-Key | header | <customer key> | ✓ | Your API key from the operator console. |
Response
| Field | Type | Description |
|---|---|---|
| accuracy | number (0–100) | How closely the sounds matched the expected ones. |
| native_likeness | number (0–100) | Share of the expected sounds said the way native speakers say them, fine detail included; a sound that is right but lacks the native detail counts in accuracy, not here. |
| completeness | number (0–100) | Share of the expected sounds that were said. |
| fluency | number (0–100) | Steady speech without long pauses, weighted by accuracy. |
| clarity | number (0–100) | Clarity: how clearly a listener would understand it. |
| speech_rate_ratio | number (0–100) | Share of the time between the first and last sound spent speaking rather than pausing. |
| duration | number (s) | Length of the recording in seconds. |
| analyzed_text | object[] | One entry per word of the text, with the fields below. |
| analyzed_text[].word | string | The word as written in reference_text. |
| analyzed_text[].accuracy, clarity | number (0–100) | The word’s own accuracy and clarity. |
| analyzed_text[].start_time, end_time | number (s) | null | Start and end in seconds from the start of the recording; null when it was not heard. |
| analyzed_text[].user_audio_base64 | string (base64 WAV) | null | The learner’s recording of this word, base64 WAV; null when it was not heard. |
| analyzed_text[].grapheme_to_phoneme | object[] | Which letters make each sound: for each phoneme_index, the graphemes (positions of the letters in the word). |
| analyzed_text[].phoneme_details | object[] | One entry per expected sound, and per extra sound heard, with the fields below. |
| analyzed_text[].phoneme_details[].phoneme | string | The expected sound in IPA; empty for an extra sound. |
| analyzed_text[].phoneme_details[].predicted | string | The sound heard, in IPA; empty ("") when it was left out. |
| analyzed_text[].phoneme_details[].confidence | number (0–1) | How sure the model is of the sound heard, from 0 to 1. |
| analyzed_text[].phoneme_details[].is_insertion | boolean | An extra sound the text does not have. |
| analyzed_text[].phoneme_details[].is_close | boolean | A wrong sound, but close to the expected one. |
| analyzed_text[].phoneme_details[].is_reference | boolean | The sound is one the text has (false for an extra sound). |
| analyzed_text[].phoneme_details[].expected_phone, heard_phone | string | null | The expected and the heard sound with the detail native speakers give it, e.g. tʰ or d̪; null when there is none. |
| analyzed_text[].phoneme_details[].detail_off | boolean | true when the right sound was said without its native detail (t for tʰ): it counts in accuracy but not in native_likeness. |
| analyzed_text[].phoneme_details[].user_audio_base64 | string (base64 WAV) | null | The learner’s recording of this sound, base64 WAV; null when it has no timing. |
| analyzed_text[].phoneme_details[].start_time, end_time | number (s) | null | Start and end in seconds from the start of the recording; null when it was not heard. |
| aligned_phonemes | object[] | The recording as a sequence of sounds: symbol, start, end, duration, word_start, the likeliest candidates and a clip. |
| waveform | number[] | Amplitude samples for drawing the recording. |
| processed_audio_base64 | string (base64 WAV) | The cleaned-up recording that was scored. |
Example request
curl
curl https://spelly.ch/api/assess \
-H 'X-API-Key: YOUR_KEY' \
-F 'reference_text=The weather is lovely' \
-F language=en-GB \
-F audio=@recording.wavExample response
JSON
{
"accuracy": 100,
"native_likeness": 92,
"completeness": 100,
"fluency": 100,
"clarity": 98,
"speech_rate_ratio": 100.0,
"analyzed_text": [
{
"word": "the",
"accuracy": 100.0,
"clarity": 97.93445765972137,
"phoneme_details": [
{
"phoneme": "ð",
"predicted": "ð",
"confidence": 0.9709068536758423,
"is_insertion": false,
"is_close": false,
"is_reference": true,
"expected_phone": "ð",
"heard_phone": "ð",
"detail_off": false,
"user_audio_base64": "UklGRo…",
"start_time": 0.042020833333333334,
"end_time": 0.10505208333333334
},
{
"phoneme": "ə",
"predicted": "ə",
"confidence": 0.9877822995185852,
"is_insertion": false,
"is_close": false,
"is_reference": true,
"expected_phone": "ə",
"heard_phone": "ə",
"detail_off": false,
"user_audio_base64": "UklGRi…",
"start_time": 0.08404166666666667,
"end_time": 0.16808333333333333
}
],
"start_time": 0.042020833333333334,
"end_time": 0.16808333333333333,
"user_audio_base64": "UklGRu…",
"grapheme_to_phoneme": [
{
"graphemes": [
0,
1,
2
],
"phoneme_index": 0
},
{
"graphemes": [
0,
1,
2
],
"phoneme_index": 1
}
]
}
],
"waveform": [
0.00019696683739311993,
-0.00015526238712482154,
-0.00024423340801149607,
1.6281235730275512e-05
],
"duration": 1.387,
"aligned_phonemes": [
{
"index": 0,
"start": 0.042020833333333334,
"duration": 0.06303125000000001,
"end": 0.10505208333333334,
"symbol": "ð",
"word_start": false,
"candidates": [
{
"symbol": "ð",
"confidence": 0.8176507949829102
}
],
"user_audio_base64": "UklGRo…"
},
{
"index": 1,
"start": 0.08404166666666667,
"duration": 0.08404166666666667,
"end": 0.16808333333333333,
"symbol": "ə",
"word_start": false,
"candidates": [
{
"symbol": "ə",
"confidence": 0.9877822995185852
}
],
"user_audio_base64": "UklGRi…"
}
],
"processed_audio_base64": "UklGRn…"
}Notes
- Example audio for the same text comes from /api/assess/synthesize, which you can call at the same time.