# Languages

Automatic language detection, explicit language codes, the rules the API enforces, the full list of accepted codes, and which model transcribes what.

Sandchest detects the language of your audio by default. You can also pin it, hint
it, or require a confidence floor. This page covers each mode, the exact validation
rules, and the codes the API accepts.

## Detection is the default

Omit both `language_code` and `language_detection` and Sandchest detects the
language:

```json
{ "audio_url": "https://example.com/meeting.mp3" }
```

The completed transcript reports what it found:

```json
{
  "language_code": "de",
  "language_confidence": 0.97,
  "language_detection": true
}
```

`language_confidence` is between 0 and 1 and comes from the genuine transcript —
Sandchest never fabricates a language for audio it could not read. Detection needs
speech: a file with no spoken audio fails with
`{"error":"language_detection cannot be performed on files with no spoken audio."}`
on the transcript, rather than guessing.

## Pinning a language

When you already know the language, say so. It skips detection and avoids a wrong
guess on short or noisy clips.

```json
{ "audio_url": "https://example.com/meeting.mp3", "language_code": "es" }
```

The response then carries `language_detection: false` and the `language_code` you
asked for.

## Hinting and fallbacks

`language_detection_options` narrows the search or names a safety net.

```json
{
  "audio_url": "https://example.com/support-call.mp3",
  "language_detection": true,
  "language_confidence_threshold": 0.6,
  "language_detection_options": {
    "expected_languages": ["en", "fr", "de"],
    "fallback_language": "en"
  }
}
```

| Field | What it does |
| --- | --- |
| `expected_languages` | Restrict detection to these codes. `["all"]` means no restriction. |
| `fallback_language` | Use this code when detection is not confident. `"auto"` leaves it unrestricted. |
| `language_confidence_threshold` | 0–1. Fail rather than proceed below this confidence. |

## The rules

The API enforces four rules and gives you the exact reason each time.

| Situation | HTTP | Body |
| --- | --- | --- |
| No `language_code`, and `language_detection: false` | 400 | `` {"error":"Either `language_detection` must be set to True, or one of `language_code` or `language_codes` must must be specified."} `` |
| A `language_code` **and** `language_detection: true` | 400 | `` {"error":"`language_detection` is not available when `language_code` is specified."} `` |
| `language_confidence_threshold` with detection off | 400 | `{"error":"language_confidence_threshold requires language_detection."}` |
| `fallback_language` not in `expected_languages` | 400 | `` {"error":"fallback_language must be in expected_languages or auto."} `` |

That last rule is satisfied when any of these is true: `expected_languages` is
absent, `expected_languages` is `["all"]`, `fallback_language` is `"auto"`, or
`expected_languages` contains the fallback. When it is omitted, the fallback is
compared as `en`. English locale codes fold together for this comparison —
`en_au`, `en_uk` and `en_us` all count as `en` — while `de_ch` stays distinct
from `de`.

## Models

| Model | Covers | When it runs |
| --- | --- | --- |
| `nvidia/parakeet-tdt-0.6b-v2` | English | Explicit English requests, and a refinement pass over uncertain detected-English audio |
| `nvidia/parakeet-tdt-0.6b-v3` | 25 languages, listed below | Automatic detection and every non-English request |

Both models stay warm, so neither routing decision costs you a cold start. The
transcript's `speech_model_used` names whichever one produced it. Self-hosted
deployments choose their own models with `SANDCHEST_MODEL` and
`SANDCHEST_MULTILINGUAL_MODEL` — see [Self-hosting](/docs/self-hosting).

The multilingual model decodes these 25 languages:

| Code | Language | Code | Language |
| --- | --- | --- | --- |
| `bg` | Bulgarian | `hr` | Croatian |
| `cs` | Czech | `da` | Danish |
| `nl` | Dutch | `en` | English |
| `et` | Estonian | `fi` | Finnish |
| `fr` | French | `de` | German |
| `el` | Greek | `hu` | Hungarian |
| `it` | Italian | `lv` | Latvian |
| `lt` | Lithuanian | `mt` | Maltese |
| `pl` | Polish | `pt` | Portuguese |
| `ro` | Romanian | `sk` | Slovak |
| `sl` | Slovenian | `es` | Spanish |
| `sv` | Swedish | `ru` | Russian |
| `uk` | Ukrainian | | |

> [!WARNING]
> The API accepts the full AssemblyAI-compatible code list below so that existing
> code validates, but a request for a language outside the table above cannot be
> transcribed by the Parakeet engine. It is refused at inference time and the
> transcript finishes with `status: "error"` and an `error` such as
> `Parakeet does not support the requested language: ja`. Check the table above
> before pinning a language.

Self-hosted deployments have a second option: the worker can be built with an
optional Whisper engine and started with `SANDCHEST_INFERENCE_ENGINE=whisper`,
which covers the whole accepted code list below. Parakeet is the default engine
and the one behind the hosted service. See [Self-hosting](/docs/self-hosting).

## Accepted language codes

These 103 codes pass validation on `language_code`
and inside `language_detection_options.expected_languages`. Anything else is rejected
with `{"error":"The request contains an unsupported option or an invalid option value."}`.

| Code | Language | Code | Language |
| --- | --- | --- | --- |
| `en` | English | `es` | Spanish |
| `fr` | French | `de` | German |
| `it` | Italian | `pt` | Portuguese |
| `nl` | Dutch | `hi` | Hindi |
| `ja` | Japanese | `zh` | Chinese |
| `fi` | Finnish | `ko` | Korean |
| `pl` | Polish | `ru` | Russian |
| `tr` | Turkish | `uk` | Ukrainian |
| `vi` | Vietnamese | `af` | Afrikaans |
| `sq` | Albanian | `am` | Amharic |
| `ar` | Arabic | `hy` | Armenian |
| `as` | Assamese | `az` | Azerbaijani |
| `ba` | Bashkir | `eu` | Basque |
| `be` | Belarusian | `bn` | Bangla |
| `bs` | Bosnian | `br` | Breton |
| `bg` | Bulgarian | `my` | Burmese |
| `ca` | Catalan | `hr` | Croatian |
| `cs` | Czech | `da` | Danish |
| `et` | Estonian | `fo` | Faroese |
| `gl` | Galician | `ka` | Georgian |
| `el` | Greek | `gu` | Gujarati |
| `ht` | Haitian Creole | `ha` | Hausa |
| `haw` | Hawaiian | `he` | Hebrew |
| `hu` | Hungarian | `is` | Icelandic |
| `id` | Indonesian | `jw` | Javanese |
| `kn` | Kannada | `kk` | Kazakh |
| `km` | Khmer | `lo` | Lao |
| `la` | Latin | `lv` | Latvian |
| `ln` | Lingala | `lt` | Lithuanian |
| `lb` | Luxembourgish | `mk` | Macedonian |
| `mg` | Malagasy | `ms` | Malay |
| `ml` | Malayalam | `mt` | Maltese |
| `mi` | Māori | `mr` | Marathi |
| `mn` | Mongolian | `ne` | Nepali |
| `no` | Norwegian | `nn` | Norwegian Nynorsk |
| `oc` | Occitan | `pa` | Punjabi |
| `ps` | Pashto | `fa` | Persian |
| `ro` | Romanian | `sa` | Sanskrit |
| `sr` | Serbian | `sn` | Shona |
| `sd` | Sindhi | `si` | Sinhala |
| `sk` | Slovak | `sl` | Slovenian |
| `so` | Somali | `su` | Sundanese |
| `sw` | Swahili | `sv` | Swedish |
| `tl` | Tagalog | `tg` | Tajik |
| `ta` | Tamil | `tt` | Tatar |
| `te` | Telugu | `th` | Thai |
| `bo` | Tibetan | `tk` | Turkmen |
| `ur` | Urdu | `uz` | Uzbek |
| `cy` | Welsh | `yi` | Yiddish |
| `yo` | Yoruba | `en_au` | Australian English |
| `en_uk` | English (United Kingdom) | `en_us` | American English |
| `de_ch` | Swiss High German | | |

## Verify

Detect the language of a file and read back what was found:

```bash
curl -sS "https://stt-api.sandchest.com/v2/transcript/$ID" \
  -H "Authorization: $SANDCHEST_API_KEY" \
  | jq '{status, language_detection, language_code, language_confidence, speech_model_used}'
```

```json title="expected output"
{
  "status": "completed",
  "language_detection": true,
  "language_code": "en",
  "language_confidence": 0.99,
  "speech_model_used": "nvidia/parakeet-tdt-0.6b-v3"
}
```

Detected-English audio may report either model: detection runs on the multilingual
model, and an uncertain English result is refined on the English one. Whichever
finished the job is the one named in `speech_model_used`.

## Next

[Webhooks](/docs/webhooks) — stop polling and get told when a transcript is done.
