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/assess

Assess 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

NameWhereTypeRequiredDescription
reference_textformstring✓The text the learner read. 1 to 300 characters; a text of only spaces is refused.
languageformstring✓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.
audioformfile✓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-Keyheader<customer key>✓Your API key from the operator console.

Response

FieldTypeDescription
accuracynumber (0–100)How closely the sounds matched the expected ones.
native_likenessnumber (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.
completenessnumber (0–100)Share of the expected sounds that were said.
fluencynumber (0–100)Steady speech without long pauses, weighted by accuracy.
claritynumber (0–100)Clarity: how clearly a listener would understand it.
speech_rate_rationumber (0–100)Share of the time between the first and last sound spent speaking rather than pausing.
durationnumber (s)Length of the recording in seconds.
analyzed_textobject[]One entry per word of the text, with the fields below.
analyzed_text[].wordstringThe word as written in reference_text.
analyzed_text[].accuracy, claritynumber (0–100)The word’s own accuracy and clarity.
analyzed_text[].start_time, end_timenumber (s) | nullStart and end in seconds from the start of the recording; null when it was not heard.
analyzed_text[].user_audio_base64string (base64 WAV) | nullThe learner’s recording of this word, base64 WAV; null when it was not heard.
analyzed_text[].grapheme_to_phonemeobject[]Which letters make each sound: for each phoneme_index, the graphemes (positions of the letters in the word).
analyzed_text[].phoneme_detailsobject[]One entry per expected sound, and per extra sound heard, with the fields below.
analyzed_text[].phoneme_details[].phonemestringThe expected sound in IPA; empty for an extra sound.
analyzed_text[].phoneme_details[].predictedstringThe sound heard, in IPA; empty ("") when it was left out.
analyzed_text[].phoneme_details[].confidencenumber (0–1)How sure the model is of the sound heard, from 0 to 1.
analyzed_text[].phoneme_details[].is_insertionbooleanAn extra sound the text does not have.
analyzed_text[].phoneme_details[].is_closebooleanA wrong sound, but close to the expected one.
analyzed_text[].phoneme_details[].is_referencebooleanThe sound is one the text has (false for an extra sound).
analyzed_text[].phoneme_details[].expected_phone, heard_phonestring | nullThe 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_offbooleantrue 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_base64string (base64 WAV) | nullThe learner’s recording of this sound, base64 WAV; null when it has no timing.
analyzed_text[].phoneme_details[].start_time, end_timenumber (s) | nullStart and end in seconds from the start of the recording; null when it was not heard.
aligned_phonemesobject[]The recording as a sequence of sounds: symbol, start, end, duration, word_start, the likeliest candidates and a clip.
waveformnumber[]Amplitude samples for drawing the recording.
processed_audio_base64string (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.wav

Example 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.