Voicer APIv2

Voicer API v2

Text to speech with ElevenLabs and Cartesia voices, voice cloning, captions, music and sound effects.

Getting started

Voicer API turns text into speech with ElevenLabs and Cartesia voices, clones voices, and builds captions. TTS is asynchronous: you create a task, poll its status, and download the MP3 when it is ready.

Base URL and format

Base URL: https://voicer.mat3u.com. All paths start with /v2. Requests and responses are JSON (UTF-8), except file uploads (multipart/form-data) and downloads of audio and captions.

Timestamps in responses are ISO 8601 with a UTC offset, for example 2026-10-11T16:34:08.984299Z.

Paginated lists take page (zero-based) and page_size (1-50) and return has_more: while it is true, request the next page.

Authentication

Every request except those marked as requiring no token sends an API token in Authorization: Bearer <token>. The token is issued by the Voicer Telegram bot. One token is one license: it has its own balance, tariff, and parallel-task limit.

A missing header or a bad token yields 401 with WWW-Authenticate: Bearer. To see who the token belongs to, call GET /v2/auth/me.

curl https://voicer.mat3u.com/v2/auth/me \
  -H "Authorization: Bearer $VOICER_TOKEN"

Error format

A business-logic error comes with an HTTP status and a body {"detail": {"code": "...", "message": "..."}}. code is stable and is what clients should branch on; message is for humans and may change. Extra fields can sit next to them, for example required and available when characters run out. The full list is in Error codes.

Auth errors are a string: {"detail": "Недействительный токен"}.

A wrong type or format yields 422 in the FastAPI list format: {"detail": [{"loc": ["body", "max_chunk_length"], "msg": "...", "type": "..."}]}. Extra fields in task JSON bodies are forbidden and also yield 422.

402 Payment Required
{
  "detail": {
    "code": "insufficient_characters",
    "message": "Недостаточно символов: нужно 12000, осталось 3400",
    "required": 12000,
    "available": 3400
  }
}

Python quick start

Full cycle: create a task, wait for the result, save the MP3. The other Python examples in this reference reuse session s and BASE from this snippet. You need the requests package and the VOICER_TOKEN environment variable.

import os
import time

import requests

BASE = "https://voicer.mat3u.com"
s = requests.Session()
s.headers["Authorization"] = f"Bearer {os.environ['VOICER_TOKEN']}"


def synthesize(text: str, voice_id: str, out_path: str) -> None:
    created = s.post(f"{BASE}/v2/tasks", json={"text": text, "voice_id": voice_id}, timeout=60)
    if created.status_code != 202:
        raise RuntimeError(created.json()["detail"])
    task_id = created.json()["task_id"]

    while True:
        task = s.get(f"{BASE}/v2/tasks/{task_id}", timeout=30).json()
        if task["status"] in ("pending", "processing", "pending_confirmation"):
            time.sleep(3)
            continue
        break

    if task["status"] == "censored":
        # Rewrite the blocked fragments and POST them to /retry (see Tasks).
        raise RuntimeError(f"Censored: {task['blocked_chunks']}")
    if task["status"] != "completed":
        raise RuntimeError(f"Task {task['status']}: {task['error']}")

    with s.get(f"{BASE}/v2/tasks/{task_id}/download", stream=True, timeout=600) as r:
        r.raise_for_status()
        with open(out_path, "wb") as f:
            for piece in r.iter_content(1 << 16):
                f.write(piece)


synthesize("Привет! Это тест Voicer API.", "JBFqnCBsd6RMkjVDRZzb", "voice.mp3")

Reference for an LLM

The whole reference in machine-readable form is at /docs.en.json: sections, endpoints, parameters with every value, curl and Python examples, errors, and gotchas. Pass the file to a model as context. OpenAPI for client generators: /openapi.en.json, interactive Swagger: /docs/swagger.

Core concepts

TTS engines

The task engine picks the provider. Allowed voices, models, and the maximum chunk size depend on it.

If engine is omitted, the API uses original, or cartesia on a tariff that does not allow original (Cartesia-only plans). An engine closed by the tariff yields 403 with engine_not_allowed. What your license allows is in tariff of GET /v2/billing/me.

engineProviderVoicesDefault model
originalElevenLabsStandard ElevenLabs voices (list)eleven_multilingual_v2
cartesiaCartesiaCartesia stock voices, your clones, and the shared clone catalogsonic-3.6

Characters, packages, and the license

TTS is billed in characters of the source text: len(text), including spaces and newlines. Characters are charged when the task is created and refunded if you cancel before synthesis starts, if the task cannot be queued, or if synthesis ends with failed.

Characters live in packages, each with its own expiry. Charge comes from the package that expires first. On an unlimited subscription nothing is charged (charged_characters = 0), but tariff limits still apply.

Tariff limits: parallel tasks on the license (max_parallel_tasks), characters per task (max_chars_per_task), chunks per task (max_chunks_per_task), allowed engines, and your Cartesia clone quota (max_cartesia_clones). null in a limit means unlimited.

Task lifecycle

A task goes through pending (queued) and processing (synthesizing) and ends in a terminal status. Poll GET /v2/tasks/{task_id} every 2-5 seconds.

StatusMeaningWhat to do
pendingQueued, characters chargedWait. You can cancel
processingSynthesizing, progress_percent growsWait
pending_confirmationPrimary TTS has degraded; the task waits for an operatorWait, the task will continue or finish on its own
completedDoneDownload the result
censoredElevenLabs blocked some fragments; the rest is synthesizedFix the text in blocked_chunks and call /retry
failedError, details in error; characters refundedCreate the task again
cancelledCancelledNothing

Chunks

Before synthesis the text is split into chunks, each is sent to the provider separately, then the chunks are concatenated into one MP3. split_type and max_chunk_length control the split. The chunk count is returned as chunks_count and is capped by the tariff.

Chunks also drive pauses (auto_pause inserts silence after every Nth chunk), split_output (a ZIP with one MP3 per chunk), and the censorship retry (only blocked chunks are resynthesized).

Account and balance

Balance and limits are counted per license, that is per the token in the header. One user can have several tokens with different tariffs.

Who this token belongs to

GET/v2/auth/meToken in the Authorization header

Returns the user and license that own the token. Useful to validate the token when a client starts.

Responseapplication/json

FieldTypeDescription
user_idstringUser ID
token_idintegerLicense ID (API token)

Errors

StatusCodeWhen
401Missing Authorization header or the token is invalid

Examples

Check the token
curl "https://voicer.mat3u.com/v2/auth/me" \
  -H "Authorization: Bearer $VOICER_TOKEN"
Response 200
{
  "user_id": "3f6c2a9e-8d41-4b7a-9c55-1e2f0a7d4b13",
  "token_id": 5521
}

License balance and tariff

GET/v2/billing/meToken in the Authorization header

Subscription, tariff, and remaining characters of the license from the Authorization header. Before a large task check remaining_characters and the tariff limits.

Responseapplication/json

FieldTypeDescription
user_idstringUser ID
user_activebooleanfalse: the user is blocked
token_idinteger | nullLicense whose balance was computed
subscription_typestringcharacters, unlimited, or no_subscription
subscription_expires_atdatetime | nullEnd of the unlimited subscription
subscription_activebooleanWhether tasks can be created now (subscription and balance)
total_charactersintegerGranted in active packages
used_charactersintegerCharged from active packages
remaining_charactersintegerRemainder
tariffobject | nullTariff limits, same as /v2/billing/tariffs
packagesarrayActive packages with remainder, in charge order, each with its own expiry
tariff_codestring | nullTariff code

Errors

StatusCodeWhen
401Missing Authorization header or the token is invalid
404The user for this token was not found

Examples

Character package
curl "https://voicer.mat3u.com/v2/billing/me" \
  -H "Authorization: Bearer $VOICER_TOKEN"
Response 200
{
  "user_id": "3f6c2a9e-8d41-4b7a-9c55-1e2f0a7d4b13",
  "user_active": true,
  "token_id": 5521,
  "subscription_type": "characters",
  "subscription_expires_at": null,
  "subscription_active": true,
  "total_characters": 1000000,
  "used_characters": 412870,
  "remaining_characters": 587130,
  "tariff_code": "characters",
  "tariff": {
    "code": "characters",
    "name": "Characters",
    "kind": "package",
    "max_parallel_tasks": 5,
    "threads_per_task": 15,
    "max_chunks_per_task": null,
    "max_chars_per_task": 200000,
    "allow_original": true,
    "allow_original_accs": false,
    "allow_cartesia": true,
    "max_cartesia_clones": 5,
    "sort_order": 0
  },
  "packages": [
    {
      "id": 18342,
      "chars_granted": 1000000,
      "chars_used": 412870,
      "expires_at": "2026-11-09T12:04:51Z",
      "tariff_code": "characters",
      "source_type": "payment",
      "created_at": "2026-10-10T12:04:51Z",
      "remaining": 587130
    }
  ]
}

Summary: balance, tasks, clones

GET/v2/billing/statsToken in the Authorization header

License balance, task counters, and used Cartesia clone quota in one request.

Responseapplication/json

FieldTypeDescription
subscription_typestringcharacters, unlimited, or no_subscription
subscription_expires_atdatetime | nullEnd of the unlimited subscription
subscription_activebooleanWhether tasks can be created now (subscription and balance)
total_charactersintegerGranted in active packages
used_charactersintegerCharged from active packages
remaining_charactersintegerRemainder
tariffobject | nullTariff limits, same as /v2/billing/tariffs
packagesarrayActive packages with remainder, in charge order, each with its own expiry
total_tasksintegerTasks in total
completed_tasksintegerCompleted
active_tasksintegerQueued or running now
clones_usedintegerYour Cartesia clones (count toward the quota)
clones_limitinteger | nullClone quota, null for unlimited

Errors

StatusCodeWhen
401Missing Authorization header or the token is invalid
404The user for this token was not found

Examples

Summary
curl "https://voicer.mat3u.com/v2/billing/stats" \
  -H "Authorization: Bearer $VOICER_TOKEN"
Response 200
{
  "user_id": "3f6c2a9e-8d41-4b7a-9c55-1e2f0a7d4b13",
  "subscription_type": "characters",
  "subscription_expires_at": null,
  "subscription_active": true,
  "total_characters": 1000000,
  "used_characters": 412870,
  "remaining_characters": 587130,
  "tariff_code": "characters",
  "tariff": {
    "code": "characters",
    "name": "Characters",
    "kind": "package",
    "max_parallel_tasks": 5,
    "threads_per_task": 15,
    "max_chunks_per_task": null,
    "max_chars_per_task": 200000,
    "allow_original": true,
    "allow_original_accs": false,
    "allow_cartesia": true,
    "max_cartesia_clones": 5,
    "sort_order": 0
  },
  "total_tasks": 214,
  "completed_tasks": 203,
  "active_tasks": 1,
  "clones_used": 2,
  "clones_limit": 5,
  "packages": [
    {
      "id": 18342,
      "chars_granted": 1000000,
      "chars_used": 412870,
      "expires_at": "2026-11-09T12:04:51Z",
      "tariff_code": "characters",
      "source_type": "payment",
      "created_at": "2026-10-10T12:04:51Z",
      "remaining": 587130
    }
  ]
}

Tariffs

GET/v2/billing/tariffsNo token

Active tariffs and their limits. Packages and subscriptions are bought in the Telegram bot.

Responseapplication/json

FieldTypeDescription
codestringTariff code
kindstringpackage (character pack) or subscription
max_parallel_tasksintegerParallel tasks on the license
threads_per_taskintegerParallel synthesis threads of one task
max_chunks_per_taskinteger | nullChunk cap per task
max_chars_per_taskinteger | nullCharacter cap per task
allow_originalbooleanoriginal engine (ElevenLabs) is allowed
allow_cartesiabooleancartesia engine is allowed
max_cartesia_clonesinteger | nullYour clone quota: 0 forbidden, null unlimited

Examples

List tariffs
curl "https://voicer.mat3u.com/v2/billing/tariffs"
Response 200
[
  {
    "code": "characters",
    "name": "Characters",
    "kind": "package",
    "max_parallel_tasks": 5,
    "threads_per_task": 15,
    "max_chunks_per_task": null,
    "max_chars_per_task": 200000,
    "allow_original": true,
    "allow_original_accs": false,
    "allow_cartesia": true,
    "max_cartesia_clones": 5,
    "sort_order": 0
  }
]

ElevenLabs voices and models

For engine=original only the standard ElevenLabs voices in the table below are accepted. The ElevenLabs library (search and featured) is how you find a voice to clone into Cartesia.

Standard ElevenLabs voices

All of these voices are multilingual and speak Russian on eleven_multilingual_v2, eleven_v3, and newer models.

Namevoice_id
AdampNInz6obpgDQGcFmaJgB
AliceXb7hH8MSUJpSbSDYk0k2
Bellahpp4J3VqNfWAUOO0d1Us
BillpqHfZKP75CvOlQylNhV4
BriannPczCjzI2devNBz1zQrb
CallumN2lVS1w4EtoT3dr4eOWO
CharlieIKne3meq5aSn9XLyUdCD
ChrisiP95p4xoKVk53GoZ742B
DanielonwK4e9ZLuTAKqWW03F9
EriccjVigY5qzO86Huf0OWal
GeorgeJBFqnCBsd6RMkjVDRZzb
HarrySOYHLrjzK2X1ezoPC6cr
JessicacgSgspJ2msm6clMCkdW9
LauraFGY2WhTYpPnrIDTdsKH5
LiamTX3LPaxmHKxFdv7VOQHJ
LilypFZP5JQG7iQjIQuC4Bku
MatildaXrExE9yKIg1WjnnlVkGX
RiverSAz9YHcvj6GT2YYXdXww
RogerCwhRBWXzGAHq8TQ4Fs17
SarahEXAVITQu4vr4xnSDxMaL
WillbIHbv24MWmeRgasZH58o

Models by engine

Without model_id the default model is used. For cartesia an ElevenLabs model (eleven_*) is silently replaced with sonic-3.6, and retired models (sonic, sonic-2*, sonic-turbo*, sonic-english, and similar) yield 400 with invalid_model. Dated snapshots such as sonic-3.6-YYYY-MM-DD are accepted.

EngineDefaultOthers
originaleleven_multilingual_v2eleven_english_v1, eleven_flash_v2, eleven_flash_v2_5, eleven_multilingual_v1, eleven_turbo_v2, eleven_turbo_v2_5, eleven_v3, eleven_v4, eleven_v4_turbo
cartesiasonic-3.6sonic-3, sonic-3-2026-01-12, sonic-3.5, sonic-3.5-2026-05-04, sonic-3.6-2026-08-27, sonic-preview

Standard ElevenLabs voices

GET/v2/voices/elevenlabsToken in the Authorization header

Voices accepted by a task with engine=original. The list is static, sorted by name.

Responseapplication/json

FieldTypeDescription
[].voice_idstringValue for the task voice_id
[].namestringVoice name

Errors

StatusCodeWhen
401Missing Authorization header or the token is invalid

Examples

List
curl "https://voicer.mat3u.com/v2/voices/elevenlabs" \
  -H "Authorization: Bearer $VOICER_TOKEN"
Response 200
[
  {
    "voice_id": "pNInz6obpgDQGcFmaJgB",
    "name": "Adam"
  },
  {
    "voice_id": "Xb7hH8MSUJpSbSDYk0k2",
    "name": "Alice"
  }
]

Models

GET/v2/voices/modelsToken in the Authorization header

Allowed model_id values per engine. The default model comes first and is marked default: true.

Responseapplication/json

FieldTypeDescription
elevenlabsarrayModels for engine=original
cartesiaarrayModels for engine=cartesia
*[].model_idstringValue for the task model_id
*[].defaultbooleanDefault model

Errors

StatusCodeWhen
401Missing Authorization header or the token is invalid

Examples

Models
curl "https://voicer.mat3u.com/v2/voices/models" \
  -H "Authorization: Bearer $VOICER_TOKEN"
Response 200
{
  "elevenlabs": [
    {
      "model_id": "eleven_multilingual_v2",
      "default": true
    },
    {
      "model_id": "eleven_v3",
      "default": false
    }
  ],
  "cartesia": [
    {
      "model_id": "sonic-3.6",
      "default": true
    },
    {
      "model_id": "sonic-3.5",
      "default": false
    }
  ]
}

Cartesia voices

For engine=cartesia three kinds of voices work: Cartesia stock voices, your clones, and the shared catalog of ElevenLabs clones. Every list requires a tariff that allows the cartesia engine.

Which voice_id to send in a task

In voice_id of a task with engine=cartesia you can pass any of the three IDs. If you pass an ElevenLabs voice ID, the service finds its ready clone: yours first, then the shared catalog.

What you passExampleWhere to get it
Cartesia clone ID8d0a6f1e-3b52-4c9e-a7d4-52e1c09b7f36Catalog, my clones, /clone response
Stock voice IDf786b574-daa5-4673-aa0c-cbe3e8534c02/v2/voices/cartesia/standard
ElevenLabs voice ID that already has a cloneWu3B7bOkdHbKnJTvbiFkElevenLabs search, then /clone or /from-labs

Your clones and the shared catalog

GET/v2/voices/cartesiaToken in the Authorization header

Your clones and the shared ElevenLabs clone catalog in one list.

Query parameters

languagestring

Language filter: a Cartesia code (ru, de, uk). Empty: any language

up to 16 characters
sourcestring

Only one source; omit for both

  • userYour clones
  • elevenlabsShared catalog of ElevenLabs voice clones
qstring

Search by name and ID

up to 100 characters
pageinteger

Page number, zero-based

Default: 0. 0-10000
page_sizeinteger

Page size

Default: 50. 1-50

Responseapplication/json

FieldTypeDescription
itemsarrayVoices, fields below
totalintegerTotal voices matching the filter
page, page_sizeintegerRequested page
has_morebooleanThere is a next page
items[].voice_idstringID for voice_id in a task with engine=cartesia
items[].name, tagline, descriptionstring | nullVoice labels
items[].languagestring | nullVoice language; a clone keeps its Cartesia language code
items[].accentstring | nullAccent
items[].genderstring | nullGender, standard voices only
items[].sourcestringstandard Cartesia library, elevenlabs shared clone catalog, user your clone
items[].preview_urlstring | nullSample for a player, no token required
items[].elevenlabs_voice_idstring | nullSource ElevenLabs voice for a clone
items[].labs_is_paidboolean | nullWhether the source ElevenLabs voice is paid
items[].is_ownerbooleanThis is your clone
items[].created_atdatetime | nullWhen the clone was created

Errors

StatusCodeWhen
401Missing Authorization header or the token is invalid
402subscription_requiredNo subscription or character package
402subscription_expiredThe unlimited subscription has expired
403engine_not_allowedThe tariff does not allow the cartesia engine
403user_blockedThe user is blocked

Examples

Russian voices from both sources
curl -G "https://voicer.mat3u.com/v2/voices/cartesia" \
  --data-urlencode "language=ru" \
  -H "Authorization: Bearer $VOICER_TOKEN"
Response 200
{
  "items": [
    {
      "voice_id": "c41f7b20-95de-4a1b-8e33-7a6d2f0c9b85",
      "name": "Мой голос",
      "tagline": null,
      "description": null,
      "language": "ru",
      "accent": null,
      "gender": null,
      "source": "user",
      "preview_url": null,
      "elevenlabs_voice_id": null,
      "labs_is_paid": null,
      "is_owner": true,
      "created_at": "2026-10-11T10:02:11Z"
    },
    {
      "voice_id": "8d0a6f1e-3b52-4c9e-a7d4-52e1c09b7f36",
      "name": "Ярослав",
      "tagline": null,
      "description": "Спокойный глубокий голос для озвучки роликов",
      "language": "ru",
      "accent": null,
      "gender": null,
      "source": "elevenlabs",
      "preview_url": "https://storage.googleapis.com/eleven-public-prod/previews/Wu3B7.mp3",
      "elevenlabs_voice_id": "Wu3B7bOkdHbKnJTvbiFk",
      "labs_is_paid": false,
      "is_owner": false,
      "created_at": "2026-09-02T08:15:40Z"
    }
  ],
  "total": 2,
  "page": 0,
  "page_size": 50,
  "has_more": false
}

Shared clone catalog

GET/v2/voices/cartesia/catalogToken in the Authorization header

Clones of ElevenLabs voices available to every user. You can synthesize with them without using your clone slots.

Query parameters

languagestring

Language filter: a Cartesia code (ru, de, uk). Empty: any language

up to 16 characters
paidboolean

Filter by the source ElevenLabs voice; omit for all

  • trueClones of paid ElevenLabs voices
  • falseClones of free voices
pageinteger

Page number, zero-based

Default: 0. 0-10000
page_sizeinteger

Page size

Default: 50. 1-50

Responseapplication/json

FieldTypeDescription
itemsarrayVoices, fields below
totalintegerTotal voices matching the filter
page, page_sizeintegerRequested page
has_morebooleanThere is a next page
items[].voice_idstringID for voice_id in a task with engine=cartesia
items[].name, tagline, descriptionstring | nullVoice labels
items[].languagestring | nullVoice language; a clone keeps its Cartesia language code
items[].accentstring | nullAccent
items[].genderstring | nullGender, standard voices only
items[].sourcestringstandard Cartesia library, elevenlabs shared clone catalog, user your clone
items[].preview_urlstring | nullSample for a player, no token required
items[].elevenlabs_voice_idstring | nullSource ElevenLabs voice for a clone
items[].labs_is_paidboolean | nullWhether the source ElevenLabs voice is paid
items[].is_ownerbooleanThis is your clone
items[].created_atdatetime | nullWhen the clone was created

Errors

StatusCodeWhen
401Missing Authorization header or the token is invalid
402subscription_requiredNo subscription or character package
402subscription_expiredThe unlimited subscription has expired
403engine_not_allowedThe tariff does not allow the cartesia engine
403user_blockedThe user is blocked

Examples

Catalog in Russian
curl -G "https://voicer.mat3u.com/v2/voices/cartesia/catalog" \
  --data-urlencode "language=ru" \
  --data-urlencode "page=0" \
  --data-urlencode "page_size=50" \
  -H "Authorization: Bearer $VOICER_TOKEN"
Response 200
{
  "items": [
    {
      "voice_id": "8d0a6f1e-3b52-4c9e-a7d4-52e1c09b7f36",
      "name": "Ярослав",
      "tagline": null,
      "description": "Спокойный глубокий голос для озвучки роликов",
      "language": "ru",
      "accent": null,
      "gender": null,
      "source": "elevenlabs",
      "preview_url": "https://storage.googleapis.com/eleven-public-prod/previews/Wu3B7.mp3",
      "elevenlabs_voice_id": "Wu3B7bOkdHbKnJTvbiFk",
      "labs_is_paid": false,
      "is_owner": false,
      "created_at": "2026-09-02T08:15:40Z"
    }
  ],
  "total": 1,
  "page": 0,
  "page_size": 50,
  "has_more": false
}

My clones and quota

GET/v2/voices/cartesia/mineToken in the Authorization header

Your clones only, and how many clone slots are used.

Query parameters

languagestring

Language filter: a Cartesia code (ru, de, uk). Empty: any language

up to 16 characters
qstring

Search by name and ID

up to 100 characters
pageinteger

Page number, zero-based

Default: 0. 0-10000
page_sizeinteger

Page size

Default: 50. 1-50

Responseapplication/json

FieldTypeDescription
itemsarrayVoices, fields below
totalintegerTotal voices matching the filter
page, page_sizeintegerRequested page
has_morebooleanThere is a next page
clones_usedintegerYour clones in any status (count toward the quota)
clones_limitinteger | nullTariff quota: null unlimited, 0 forbidden
items[].voice_idstringID for voice_id in a task with engine=cartesia
items[].name, tagline, descriptionstring | nullVoice labels
items[].languagestring | nullVoice language; a clone keeps its Cartesia language code
items[].accentstring | nullAccent
items[].genderstring | nullGender, standard voices only
items[].sourcestringstandard Cartesia library, elevenlabs shared clone catalog, user your clone
items[].preview_urlstring | nullSample for a player, no token required
items[].elevenlabs_voice_idstring | nullSource ElevenLabs voice for a clone
items[].labs_is_paidboolean | nullWhether the source ElevenLabs voice is paid
items[].is_ownerbooleanThis is your clone
items[].created_atdatetime | nullWhen the clone was created

Errors

StatusCodeWhen
401Missing Authorization header or the token is invalid
402subscription_requiredNo subscription or character package
402subscription_expiredThe unlimited subscription has expired
403engine_not_allowedThe tariff does not allow the cartesia engine
403user_blockedThe user is blocked

Examples

My clones
curl "https://voicer.mat3u.com/v2/voices/cartesia/mine" \
  -H "Authorization: Bearer $VOICER_TOKEN"
Response 200
{
  "items": [
    {
      "voice_id": "c41f7b20-95de-4a1b-8e33-7a6d2f0c9b85",
      "name": "Мой голос",
      "tagline": null,
      "description": null,
      "language": "ru",
      "accent": null,
      "gender": null,
      "source": "user",
      "preview_url": null,
      "elevenlabs_voice_id": null,
      "labs_is_paid": null,
      "is_owner": true,
      "created_at": "2026-10-11T10:02:11Z"
    }
  ],
  "total": 1,
  "page": 0,
  "page_size": 50,
  "has_more": false,
  "clones_used": 1,
  "clones_limit": 5
}

Cartesia stock voices

GET/v2/voices/cartesia/standardToken in the Authorization header

Library of ready Cartesia voices. Clone slots are not needed.

Query parameters

languagestring

Language filter: a Cartesia code (ru, de, uk). Empty: any language

up to 16 characters
genderstring

Filter by gender

  • masculineMale
  • feminineFemale
  • gender_neutralNeutral
qstring

Search by name and description

up to 100 characters
pageinteger

Page number, zero-based

Default: 0. 0-10000
page_sizeinteger

Page size

Default: 50. 1-50

Responseapplication/json

FieldTypeDescription
itemsarrayVoices, fields below
totalintegerTotal voices matching the filter
page, page_sizeintegerRequested page
has_morebooleanThere is a next page
items[].voice_idstringID for voice_id in a task with engine=cartesia
items[].name, tagline, descriptionstring | nullVoice labels
items[].languagestring | nullVoice language; a clone keeps its Cartesia language code
items[].accentstring | nullAccent
items[].genderstring | nullGender, standard voices only
items[].sourcestringstandard Cartesia library, elevenlabs shared clone catalog, user your clone
items[].preview_urlstring | nullSample for a player, no token required
items[].elevenlabs_voice_idstring | nullSource ElevenLabs voice for a clone
items[].labs_is_paidboolean | nullWhether the source ElevenLabs voice is paid
items[].is_ownerbooleanThis is your clone
items[].created_atdatetime | nullWhen the clone was created

Errors

StatusCodeWhen
401Missing Authorization header or the token is invalid
402subscription_requiredNo subscription or character package
402subscription_expiredThe unlimited subscription has expired
403engine_not_allowedThe tariff does not allow the cartesia engine
403user_blockedThe user is blocked

Examples

Female Russian voices
curl -G "https://voicer.mat3u.com/v2/voices/cartesia/standard" \
  --data-urlencode "language=ru" \
  --data-urlencode "gender=feminine" \
  -H "Authorization: Bearer $VOICER_TOKEN"
Response 200
{
  "items": [
    {
      "voice_id": "f786b574-daa5-4673-aa0c-cbe3e8534c02",
      "name": "Анна",
      "tagline": "Тёплый дикторский голос",
      "description": "Ровная подача для новостей и обучающих видео",
      "language": "ru",
      "accent": null,
      "gender": "feminine",
      "source": "standard",
      "preview_url": "https://voicer.mat3u.com/v2/voices/cartesia/standard/f786b574-daa5-4673-aa0c-cbe3e8534c02/preview",
      "elevenlabs_voice_id": null,
      "labs_is_paid": null,
      "is_owner": false,
      "created_at": null
    }
  ],
  "total": 1,
  "page": 0,
  "page_size": 50,
  "has_more": false
}

Cartesia voice card

GET/v2/voices/cartesia/{voice_id}Token in the Authorization header

Your clone, a catalog clone, or a stock voice by ID.

Path parameters

voice_idstringrequired

Voice ID

1-64

Responseapplication/json

FieldTypeDescription
voice_idstringID for voice_id in a task with engine=cartesia
name, tagline, descriptionstring | nullVoice labels
languagestring | nullVoice language; a clone keeps its Cartesia language code
accentstring | nullAccent
genderstring | nullGender, standard voices only
sourcestringstandard Cartesia library, elevenlabs shared clone catalog, user your clone
preview_urlstring | nullSample for a player, no token required
elevenlabs_voice_idstring | nullSource ElevenLabs voice for a clone
labs_is_paidboolean | nullWhether the source ElevenLabs voice is paid
is_ownerbooleanThis is your clone
created_atdatetime | nullWhen the clone was created

Errors

StatusCodeWhen
401Missing Authorization header or the token is invalid
402subscription_requiredNo subscription or character package
402subscription_expiredThe unlimited subscription has expired
403engine_not_allowedThe tariff does not allow the cartesia engine
403user_blockedThe user is blocked
404voice_not_foundThe voice is missing or it is someone else's clone

Examples

Catalog clone
curl "https://voicer.mat3u.com/v2/voices/cartesia/8d0a6f1e-3b52-4c9e-a7d4-52e1c09b7f36" \
  -H "Authorization: Bearer $VOICER_TOKEN"
Response 200
{
  "voice_id": "8d0a6f1e-3b52-4c9e-a7d4-52e1c09b7f36",
  "name": "Ярослав",
  "tagline": null,
  "description": "Спокойный глубокий голос для озвучки роликов",
  "language": "ru",
  "accent": null,
  "gender": null,
  "source": "elevenlabs",
  "preview_url": "https://storage.googleapis.com/eleven-public-prod/previews/Wu3B7.mp3",
  "elevenlabs_voice_id": "Wu3B7bOkdHbKnJTvbiFk",
  "labs_is_paid": false,
  "is_owner": false,
  "created_at": "2026-09-02T08:15:40Z"
}

Stock voice sample

GET/v2/voices/cartesia/standard/{voice_id}/previewNo token

MP3 sample of a Cartesia stock voice. The preview_url from the catalog can go into <audio> as-is: no token, the response is cached for 7 days and open for CORS.

Path parameters

voice_idstringrequired

Stock voice ID (UUID)

Responseaudio/mpeg

Errors

StatusCodeWhen
404preview_not_foundThe voice has no sample
502cartesia_unavailableCartesia did not respond or refused
503no_account_availableNo working Cartesia account, retry later

Examples

Download the sample
curl "https://voicer.mat3u.com/v2/voices/cartesia/standard/f786b574-daa5-4673-aa0c-cbe3e8534c02/preview" \
  -o preview.mp3
Response 200
MP3 (audio/mpeg)

Voice cloning

A Cartesia clone is created in two ways, and they spend the tariff quota (max_cartesia_clones) differently.

Your clone vs a clone from ElevenLabs

Your clipelevenlabs_voice_id
Clone slotUses oneDoes not use one
Who can see itOnly youEveryone, goes into the shared catalog
AudioRequired, 10-60 sOptional: the ElevenLabs sample is used
RepeatA new clone every timeReturns the existing one (reused: true)
DeleteDELETE frees the slotForbidden (403 shared_clone)

Clone a voice

POST/v2/voices/cartesia/cloneToken in the Authorization header

Creates a Cartesia clone from your audio or from an ElevenLabs voice. The request is synchronous and can take several minutes: set a client timeout of at least 300 seconds.

Form fieldsmultipart/form-data

languagestringrequired

Clone language: a Cartesia code (ru, en, de, uk, and others). An unsupported Slavic code produces a ru clone, any other unknown code an en clone

up to 16 characters
clipfile

Speech audio. Required without elevenlabs_voice_id; optional with it (the ElevenLabs sample is used)

10-60 seconds, up to 16 MB; flac, mp3, mpeg, mpga, oga, ogg, wav, webm
elevenlabs_voice_idstring

ElevenLabs voice. With it the clone goes into the shared catalog and does not use your slot

10-64 Latin letters and digits
namestring

Clone name. Defaults to the ElevenLabs voice name or clone-ru/clone-en

up to 255 characters
modestring

Cartesia cloning mode

Default: "similarity".
  • similarityAs close to the source as possible
  • stabilityMore stable sound, fewer source artifacts
taglinestring

Short label

up to 32 characters
descriptionstring

Description. Defaults to the ElevenLabs voice description

up to 1000 characters
accentstring

Accent

up to 64 characters

Responseapplication/json

FieldTypeDescription
voiceobjectThe clone, same fields as the voice card
reusedbooleantrue: a ready clone of this voice was already in the catalog
clones_usedintegerYour clones after the operation
clones_limitinteger | nullYour clone quota, null unlimited

Errors

StatusCodeWhen
401Missing Authorization header or the token is invalid
402subscription_requiredNo subscription or character package
402subscription_expiredThe unlimited subscription has expired
403engine_not_allowedThe tariff does not allow the cartesia engine
403user_blockedThe user is blocked
403clone_limit_reachedAll of your clone slots are used
413clip_too_largeThe file is larger than 16 MB
415unsupported_formatThe file format is not supported
422clip_requiredNeither clip nor elevenlabs_voice_id was sent
422invalid_durationAudio length is not 10-60 seconds
422invalid_audioThe file cannot be read as audio
422empty_clipEmpty file
422elevenlabs_voice_not_foundThe voice is not in ElevenLabs
422cartesia_rejectedCartesia rejected the clip
503elevenlabs_unavailableElevenLabs did not respond, retry later
502cartesia_unavailableCartesia did not respond or refused
503no_account_availableNo working Cartesia account, retry later

Examples

Your clone from a file (uses a slot)
curl -X POST "https://voicer.mat3u.com/v2/voices/cartesia/clone" \
  -H "Authorization: Bearer $VOICER_TOKEN" \
  -F "language=ru" \
  -F "name=Мой голос" \
  -F "clip=@voice.wav"
Response 200
{
  "voice": {
    "voice_id": "c41f7b20-95de-4a1b-8e33-7a6d2f0c9b85",
    "name": "Мой голос",
    "tagline": null,
    "description": null,
    "language": "ru",
    "accent": null,
    "gender": null,
    "source": "user",
    "preview_url": null,
    "elevenlabs_voice_id": null,
    "labs_is_paid": null,
    "is_owner": true,
    "created_at": "2026-10-11T10:02:11Z"
  },
  "reused": false,
  "clones_used": 2,
  "clones_limit": 5
}
Clone of an ElevenLabs voice (does not use a slot)
curl -X POST "https://voicer.mat3u.com/v2/voices/cartesia/clone" \
  -H "Authorization: Bearer $VOICER_TOKEN" \
  -F "language=ru" \
  -F "elevenlabs_voice_id=Wu3B7bOkdHbKnJTvbiFk"
Response 200
{
  "voice": {
    "voice_id": "8d0a6f1e-3b52-4c9e-a7d4-52e1c09b7f36",
    "name": "Ярослав",
    "tagline": null,
    "description": "Спокойный глубокий голос для озвучки роликов",
    "language": "ru",
    "accent": null,
    "gender": null,
    "source": "elevenlabs",
    "preview_url": "https://storage.googleapis.com/eleven-public-prod/previews/Wu3B7.mp3",
    "elevenlabs_voice_id": "Wu3B7bOkdHbKnJTvbiFk",
    "labs_is_paid": false,
    "is_owner": false,
    "created_at": "2026-09-02T08:15:40Z"
  },
  "reused": true,
  "clones_used": 1,
  "clones_limit": 5
}

Cloning the same ElevenLabs voice in the same language again does not create a new clone: the existing one is returned with reused: true. You can check first with /from-labs.

Check an ElevenLabs voice before cloning

GET/v2/voices/cartesia/from-labs/{voice_id}Token in the Authorization header

Whether the voice exists in ElevenLabs, whether its clone is already in the shared catalog, and whether you have one. Creates nothing.

Path parameters

voice_idstringrequired

ElevenLabs voice ID

10-64 Latin letters and digits

Query parameters

languagestring

Clone language: a Cartesia code (same as /clone); empty: a clone in any language

up to 16 characters

Responseapplication/json

FieldTypeDescription
elevenlabs_voice_idstringElevenLabs voice ID
namestring | nullName
preview_urlstring | nullElevenLabs sample
labs_is_paidboolean | nullThe voice is paid in ElevenLabs
clone_languagestring | nullClone language code, or null
can_clonebooleanThere is not yet a clone in the shared catalog
catalogobject | nullReady clone in the shared catalog
mineobject | nullYour clone of this voice

Errors

StatusCodeWhen
401Missing Authorization header or the token is invalid
402subscription_requiredNo subscription or character package
402subscription_expiredThe unlimited subscription has expired
403engine_not_allowedThe tariff does not allow the cartesia engine
403user_blockedThe user is blocked
404elevenlabs_voice_not_foundThe voice is not in ElevenLabs
503elevenlabs_unavailableElevenLabs did not respond, retry later

Examples

The clone is already in the catalog
curl -G "https://voicer.mat3u.com/v2/voices/cartesia/from-labs/Wu3B7bOkdHbKnJTvbiFk" \
  --data-urlencode "language=ru" \
  -H "Authorization: Bearer $VOICER_TOKEN"
Response 200
{
  "elevenlabs_voice_id": "Wu3B7bOkdHbKnJTvbiFk",
  "name": "Ярослав",
  "preview_url": "https://storage.googleapis.com/eleven-public-prod/previews/Wu3B7.mp3",
  "labs_is_paid": false,
  "clone_language": "ru",
  "can_clone": false,
  "catalog": {
    "voice_id": "8d0a6f1e-3b52-4c9e-a7d4-52e1c09b7f36",
    "name": "Ярослав",
    "tagline": null,
    "description": "Спокойный глубокий голос для озвучки роликов",
    "language": "ru",
    "accent": null,
    "gender": null,
    "source": "elevenlabs",
    "preview_url": "https://storage.googleapis.com/eleven-public-prod/previews/Wu3B7.mp3",
    "elevenlabs_voice_id": "Wu3B7bOkdHbKnJTvbiFk",
    "labs_is_paid": false,
    "is_owner": false,
    "created_at": "2026-09-02T08:15:40Z"
  },
  "mine": null
}

Delete your clone

DELETE/v2/voices/cartesia/{voice_id}Token in the Authorization header

Deletes the clone in Cartesia and frees the slot. Shared-catalog clones cannot be deleted.

Path parameters

voice_idstringrequired

Your clone ID

1-64

Responseapplication/json

FieldTypeDescription
voice_idstringDeleted clone

Errors

StatusCodeWhen
401Missing Authorization header or the token is invalid
403shared_cloneThis is a shared-catalog clone
404voice_not_foundThe clone is missing or it is someone else's
502cartesia_unavailableCartesia did not respond or refused
503no_account_availableNo working Cartesia account, retry later

Examples

Delete
curl -X DELETE "https://voicer.mat3u.com/v2/voices/cartesia/c41f7b20-95de-4a1b-8e33-7a6d2f0c9b85" \
  -H "Authorization: Bearer $VOICER_TOKEN"
Response 200
{
  "voice_id": "c41f7b20-95de-4a1b-8e33-7a6d2f0c9b85"
}

TTS tasks

A task belongs to the license (token) that created it. Other people's tasks do not exist for the API and yield 404. Unfinished tasks of this key: GET /v2/tasks.

Censorship and retry

ElevenLabs sometimes refuses to synthesize individual fragments. The task then ends as censored: synthesized chunks are kept, and the blocked ones are listed in blocked_chunks. Rewrite their text and POST it to /retry: only those chunks are resynthesized.

A retry is free if the new text is not longer than the original. If it is longer, only the difference is charged. With no extra charge the retry works even after the subscription ends. Retry data is stored with the task (30 days).

Retry in Python
task = s.get(f"{BASE}/v2/tasks/{task_id}", timeout=30).json()
if task["status"] == "censored":
    fixed = [
        {"index": c["index"], "text": rewrite(c["text"])}  # rewrite: your wording
        for c in task["blocked_chunks"]
    ]
    r = s.post(f"{BASE}/v2/tasks/{task_id}/retry", json={"corrected_chunks": fixed}, timeout=60)
    r.raise_for_status()
    # then poll the status again until completed

Dubbing

With dub_languages the task speaks the text, then translates it into each language and speaks the translation. By default it uses the same voice (Cartesia uses a clone in the track language); dub_voices sets another voice_id per language so the translation has no accent. The translation is fitted to the duration of every original chunk: if it comes out longer, the model shortens the wording instead of speeding up the speech. At the end, the chunks of all tracks are padded with silence to the longest one, so the files have the same length and fit one video.

Example: the original uses JBFqnCBsd6RMkjVDRZzb, English uses pNInz6obpgDQGcFmaJgB, German uses EXAVITQu4vr4xnSDxMaL. A language with no key in dub_voices keeps the original voice (Cartesia uses a clone in the track language).

The whole original is spoken first, then the languages one by one. Each track's progress is in dubs of the main task. The main task and its tracks become completed together, once the lengths are aligned.

Each track has its own task_id: /download, /subtitles and /v2/captions treat it as a regular task. For engine=original the tracks have ElevenLabs timestamps: captions are instant and match the aligned audio. For Cartesia, captions come from speech recognition via /v2/captions.

Cost: text length × (1 + 1.5 × number of languages), charged to the main task on create. If dubbing ends as failed, characters are refunded. A track cannot be cancelled on its own, only with the main task.

Languages depend on the engine and model. Eleven v3 and v4 speak every dubbing language, Multilingual v2, Flash v2.5 and Turbo v2.5 have their own list, Cartesia uses the Sonic languages. An unavailable language yields 400 unsupported_dub_language with the available list. For Cartesia the dubbing language cannot equal the text language.

Dubbing in Python: every file and its captions
import time
from pathlib import Path

body = {
    "text": text,
    "voice_id": "JBFqnCBsd6RMkjVDRZzb",  # original: George
    "model_id": "eleven_v3",
    "dub_languages": ["en", "de"],
    "dub_voices": {
        "en": "pNInz6obpgDQGcFmaJgB",  # English: Adam, not the original voice
        "de": "EXAVITQu4vr4xnSDxMaL",  # German: Sarah
    },
}
r = s.post(f"{BASE}/v2/tasks", json=body, timeout=60)
r.raise_for_status()
task = r.json()
tracks = {"original": task["task_id"]} | {d["language"]: d["task_id"] for d in task["dubs"]}

final = ("completed", "censored", "failed", "cancelled")
while s.get(f"{BASE}/v2/tasks/{task['task_id']}", timeout=30).json()["status"] not in final:
    time.sleep(5)

# every track's audio and timestamps are kept for an hour after the last language
for name, track_id in tracks.items():
    audio = s.get(f"{BASE}/v2/tasks/{track_id}/download", timeout=600)
    audio.raise_for_status()
    Path(f"{name}.mp3").write_bytes(audio.content)
    srt = s.get(f"{BASE}/v2/tasks/{track_id}/subtitles", params={"format": "srt"}, timeout=60)
    if srt.status_code == 200:  # Cartesia has no timestamps: 409, use POST /v2/captions
        Path(f"{name}.srt").write_bytes(srt.content)

Create a TTS task

POST/v2/tasksToken in the Authorization header

Queues the text and charges characters immediately. Responds 202 with task_id; wait for the result via GET /v2/tasks/{task_id}.

Request bodyapplication/json

textstringrequired

Text to synthesize. Its length in characters is the task cost

1-1 000 000 characters and no more than the tariff max_chars_per_task
voice_idstringrequired

Voice. For original, a standard ElevenLabs voice ID; for cartesia, a clone ID, a Cartesia stock voice ID, or an ElevenLabs voice that already has a clone

1-50 characters
enginestring

Engine. If omitted: original when the tariff allows it, otherwise cartesia

  • originalElevenLabs
  • cartesiaCartesia
model_idstring

Model. If omitted: eleven_multilingual_v2 or sonic-3.6. List at /v2/voices/models

up to 50 characters
languagestring

Text language, cartesia only: picks the clone of that language and the synthesis language. If omitted: the voice language

up to 16 characters, for example ru, en, de
dub_languagesarray

Dubbing: languages to translate the text into. Each language is spoken as a separate track; chunks of all tracks are padded with silence to the longest one, so the files match the original in length. The default voice is the original (Cartesia uses a clone in the track language). A different voice per language goes in dub_voices. Cost: text × (1 + 1.5 × languages)

codes from the engine catalog, for example en, de, es; duplicates are dropped
dub_voicesobject

Track voice: an object {language code: voice_id}. For example {"en": "pNInz6obpgDQGcFmaJgB", "de": "EXAVITQu4vr4xnSDxMaL"}: English and German with their own voices. The key must be in dub_languages. If omitted: Labs speaks with the original voice, Cartesia uses a clone in the track language

the same voice_id as in a task; a key outside dub_languages yields 400 unknown_dub_voice
voice_settingsobject

ElevenLabs voice settings. Cartesia ignores them. Omitted fields use ElevenLabs defaults

stabilitynumber

Intonation stability

0-1
similarity_boostnumber

Likeness to the source voice

0-1
stylenumber

Style exaggeration

0-1
use_speaker_boostboolean

Boost likeness to the speaker

speednumber

Speech rate

0.7-1.2
split_typestring

How to split the text into chunks

Default: "smart".
  • smartBy sentences glued up to max_chunk_length; quotes and direct speech are not split
  • paragraphsEach non-empty line is a chunk; max_chunk_length is ignored, a line longer than 2500 characters yields 400 invalid_split
  • max_lengthBy sentences up to max_chunk_length, ignoring quotes
max_chunk_lengthinteger

Maximum chunk length. For cartesia (except paragraphs) and for Chinese, Japanese, and Korean text it is capped at 500

Default: 1000. 500-2500
split_outputboolean

true: the result is a ZIP with one MP3 per chunk instead of a single MP3

Default: false.
auto_pauseobject

Silence after every frequency-th chunk. There is no pause after the last chunk

enabledboolean

Turn pauses on

Default: false.
durationnumber

Pause length in seconds

Default: 0.5. 0.1-30
frequencyinteger

Pause after every Nth chunk

Default: 1. 1-100

Responseapplication/json

FieldTypeDescription
task_idstringTask ID
statusstringAlways pending
enginestringChosen engine
voice_idstringVoice the provider will get (for Cartesia, the clone ID)
model_idstringChosen model
chunks_countintegerHow many chunks the text was split into
charactersintegerText length
charged_charactersintegerCharacters charged; 0 on unlimited
dubsarray | nullWith dub_languages: [{language, task_id, status, progress_percent, voice_id}], each track has its own task_id for status and download

Errors

StatusCodeWhen
401Missing Authorization header or the token is invalid
400empty_textThe text has nothing that can be spoken
400text_too_longThe text is longer than the tariff max_chars_per_task
400too_many_chunksMore chunks than the tariff max_chunks_per_task
400invalid_splitThe split is impossible (a long paragraph in paragraphs)
400voice_not_foundThe voice was not found for this engine
400voice_not_allowedThe ElevenLabs voice is not in the standard list
400invalid_modelThe model is unsupported or retired
400unsupported_dub_languageThe dubbing language is unavailable for the engine or model (unsupported, available)
400dub_same_languageThe dubbing language equals the text language
400unknown_dub_voiceA dub_voices key is not in dub_languages
402subscription_requiredNo subscription or character package
402subscription_expiredThe unlimited subscription has ended
402insufficient_charactersNot enough characters (required, available)
403user_blockedThe user is blocked
403engine_not_allowedThe engine is closed by the tariff
429too_many_active_tasksThe license max_parallel_tasks is reached
503queue_unavailableThe queue is unavailable, characters refunded
503elevenlabs_unavailableElevenLabs is unavailable to check the voice
503maintenanceMaintenance, new tasks are not accepted

Examples

ElevenLabs, minimal request
curl -X POST "https://voicer.mat3u.com/v2/tasks" \
  -H "Authorization: Bearer $VOICER_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
  "text": "Привет! Это тест Voicer API.",
  "voice_id": "JBFqnCBsd6RMkjVDRZzb"
}'
Response 202
{
  "task_id": "6b1d7e42-0c3a-4f8e-9a51-d2c47e8b9f03",
  "status": "pending",
  "engine": "original",
  "voice_id": "JBFqnCBsd6RMkjVDRZzb",
  "model_id": "eleven_multilingual_v2",
  "chunks_count": 1,
  "characters": 28,
  "charged_characters": 28
}
ElevenLabs with every setting
curl -X POST "https://voicer.mat3u.com/v2/tasks" \
  -H "Authorization: Bearer $VOICER_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
  "text": "Глава первая.\nУтро началось с дождя.\nГлава вторая.",
  "voice_id": "EXAVITQu4vr4xnSDxMaL",
  "engine": "original",
  "model_id": "eleven_v3",
  "voice_settings": {
    "stability": 0.45,
    "similarity_boost": 0.8,
    "style": 0.2,
    "use_speaker_boost": true,
    "speed": 1.05
  },
  "split_type": "paragraphs",
  "split_output": false,
  "auto_pause": {
    "enabled": true,
    "duration": 1.2,
    "frequency": 1
  }
}'
Response 202
{
  "task_id": "6b1d7e42-0c3a-4f8e-9a51-d2c47e8b9f03",
  "status": "pending",
  "engine": "original",
  "voice_id": "EXAVITQu4vr4xnSDxMaL",
  "model_id": "eleven_v3",
  "chunks_count": 3,
  "characters": 50,
  "charged_characters": 50
}
Cartesia clone, ZIP per chunk
curl -X POST "https://voicer.mat3u.com/v2/tasks" \
  -H "Authorization: Bearer $VOICER_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
  "text": "Добро пожаловать на курс. Сегодня разберём основы.",
  "voice_id": "8d0a6f1e-3b52-4c9e-a7d4-52e1c09b7f36",
  "engine": "cartesia",
  "model_id": "sonic-3.6",
  "language": "ru",
  "max_chunk_length": 500,
  "split_output": true
}'
Response 202
{
  "task_id": "6b1d7e42-0c3a-4f8e-9a51-d2c47e8b9f03",
  "status": "pending",
  "engine": "cartesia",
  "voice_id": "8d0a6f1e-3b52-4c9e-a7d4-52e1c09b7f36",
  "model_id": "sonic-3.6",
  "chunks_count": 1,
  "characters": 50,
  "charged_characters": 50
}
ElevenLabs dubbed: English and German with other voices
curl -X POST "https://voicer.mat3u.com/v2/tasks" \
  -H "Authorization: Bearer $VOICER_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
  "text": "Сегодня мы запускаем дубляж.",
  "voice_id": "JBFqnCBsd6RMkjVDRZzb",
  "model_id": "eleven_v3",
  "dub_languages": [
    "en",
    "de"
  ],
  "dub_voices": {
    "en": "pNInz6obpgDQGcFmaJgB",
    "de": "EXAVITQu4vr4xnSDxMaL"
  }
}'
Response 202
{
  "task_id": "6b1d7e42-0c3a-4f8e-9a51-d2c47e8b9f03",
  "status": "pending",
  "engine": "original",
  "voice_id": "JBFqnCBsd6RMkjVDRZzb",
  "model_id": "eleven_v3",
  "chunks_count": 1,
  "characters": 28,
  "charged_characters": 112,
  "dubs": [
    {
      "language": "en",
      "task_id": "a5c15c55-c861-4867-85ea-291789894745",
      "status": "pending",
      "progress_percent": 0.0,
      "voice_id": "pNInz6obpgDQGcFmaJgB"
    },
    {
      "language": "de",
      "task_id": "f91a2949-f4b8-49dc-9ec1-dbddd0828a82",
      "status": "pending",
      "progress_percent": 0.0,
      "voice_id": "EXAVITQu4vr4xnSDxMaL"
    }
  ]
}

Active tasks of this key

GET/v2/tasksToken in the Authorization header

Tasks created with this API key that are not finished yet: pending, processing, pending_confirmation. Other keys of the same user are not listed. An empty list if none are active.

Responseapplication/json

FieldTypeDescription
tasksarrayActive tasks of this key, oldest first. Same fields as GET /v2/tasks/{task_id}
tasks[].task_idstringTask ID
tasks[].statusstringpending, processing, pending_confirmation, completed, censored, failed, cancelled
tasks[].enginestring | nullTask engine
tasks[].progress_percentnumberReadiness 0-100
tasks[].chunks_completed, chunks_totalintegerSynthesized chunks out of the total
tasks[].split_outputbooleanThe result will be a ZIP
tasks[].created_at, started_at, completed_atdatetime | nullStage timestamps
tasks[].errorobject | null{type, message}; type: content_blocked, failover_confirmation_required, and others
tasks[].blocked_chunksarray | nullOn censored: [{index, text}] of blocked chunks
tasks[].dubsarray | nullOn a dubbing task: [{language, task_id, status, progress_percent, voice_id}] of its tracks. The original is synthesized first, then the languages one by one; all files become completed together once the track lengths are aligned
tasks[].dub_of, dub_languagestring | nullOn a dubbing track: the main task and the language

Errors

StatusCodeWhen
401Missing Authorization header or the token is invalid

Examples

Has active tasks
curl "https://voicer.mat3u.com/v2/tasks" \
  -H "Authorization: Bearer $VOICER_TOKEN"
Response 200
{
  "tasks": [
    {
      "task_id": "6b1d7e42-0c3a-4f8e-9a51-d2c47e8b9f03",
      "status": "processing",
      "engine": "original",
      "progress_percent": 40.0,
      "chunks_completed": 2,
      "chunks_total": 5,
      "split_output": false,
      "created_at": "2026-10-11T16:20:03Z",
      "started_at": "2026-10-11T16:20:05Z",
      "completed_at": null,
      "error": null,
      "blocked_chunks": null
    }
  ]
}
None active
curl "https://voicer.mat3u.com/v2/tasks" \
  -H "Authorization: Bearer $VOICER_TOKEN"
Response 200
{
  "tasks": []
}

Task status

GET/v2/tasks/{task_id}Token in the Authorization header

Status, progress, and error of your task. For censored, blocked_chunks holds the fragment texts you need to fix.

Path parameters

task_idstringrequired

Task ID

1-36

Responseapplication/json

FieldTypeDescription
task_idstringTask ID
statusstringpending, processing, pending_confirmation, completed, censored, failed, cancelled
enginestring | nullTask engine
progress_percentnumberReadiness 0-100
chunks_completed, chunks_totalintegerSynthesized chunks out of the total
split_outputbooleanThe result will be a ZIP
created_at, started_at, completed_atdatetime | nullStage timestamps
errorobject | null{type, message}; type: content_blocked, failover_confirmation_required, and others
blocked_chunksarray | nullOn censored: [{index, text}] of blocked chunks
dubsarray | nullOn a dubbing task: [{language, task_id, status, progress_percent, voice_id}] of its tracks. The original is synthesized first, then the languages one by one; all files become completed together once the track lengths are aligned
dub_of, dub_languagestring | nullOn a dubbing track: the main task and the language

Errors

StatusCodeWhen
401Missing Authorization header or the token is invalid
404task_not_foundThe task does not exist or belongs to another token

Examples

In progress
curl "https://voicer.mat3u.com/v2/tasks/6b1d7e42-0c3a-4f8e-9a51-d2c47e8b9f03" \
  -H "Authorization: Bearer $VOICER_TOKEN"
Response 200
{
  "task_id": "6b1d7e42-0c3a-4f8e-9a51-d2c47e8b9f03",
  "status": "processing",
  "engine": "original",
  "progress_percent": 40.0,
  "chunks_completed": 2,
  "chunks_total": 5,
  "split_output": false,
  "created_at": "2026-10-11T16:20:03Z",
  "started_at": "2026-10-11T16:20:05Z",
  "completed_at": null,
  "error": null,
  "blocked_chunks": null
}
Blocked by censorship
curl "https://voicer.mat3u.com/v2/tasks/6b1d7e42-0c3a-4f8e-9a51-d2c47e8b9f03" \
  -H "Authorization: Bearer $VOICER_TOKEN"
Response 200
{
  "task_id": "6b1d7e42-0c3a-4f8e-9a51-d2c47e8b9f03",
  "status": "censored",
  "engine": "original",
  "progress_percent": 80.0,
  "chunks_completed": 4,
  "chunks_total": 5,
  "split_output": false,
  "created_at": "2026-10-11T16:20:03Z",
  "started_at": "2026-10-11T16:20:05Z",
  "completed_at": "2026-10-11T16:21:12Z",
  "error": {
    "type": "content_blocked",
    "message": "1 чанк(ов) заблокировано цензурой ElevenLabs"
  },
  "blocked_chunks": [
    {
      "index": 3,
      "text": "Фрагмент, который не пропустил фильтр."
    }
  ]
}

Cancel a task

POST/v2/tasks/{task_id}/cancelToken in the Authorization header

Cancels a queued task and refunds charged characters. A task that is already synthesizing can be cancelled only on an unlimited subscription (nothing was charged, so there is nothing to refund).

Path parameters

task_idstringrequired

Task ID

1-36

Responseapplication/json

FieldTypeDescription
task_idstringTask ID
statusstringcancelled
previous_statusstringStatus before cancel
refunded_charactersintegerCharacters refunded

Errors

StatusCodeWhen
401Missing Authorization header or the token is invalid
404task_not_foundThe task does not exist or belongs to another token
409already_processingAlready synthesizing, and the subscription is not unlimited
409already_cancelledAlready cancelled
409not_cancellableThis status cannot be cancelled (status)
409dub_track_not_cancellableA dubbing track is cancelled only with its main task (dub_of)

Examples

Cancel from the queue
curl -X POST "https://voicer.mat3u.com/v2/tasks/6b1d7e42-0c3a-4f8e-9a51-d2c47e8b9f03/cancel" \
  -H "Authorization: Bearer $VOICER_TOKEN"
Response 200
{
  "task_id": "6b1d7e42-0c3a-4f8e-9a51-d2c47e8b9f03",
  "status": "cancelled",
  "previous_status": "pending",
  "refunded_characters": 28
}

Retry after censorship

POST/v2/tasks/{task_id}/retryToken in the Authorization header

Resynthesizes blocked fragments of a censored task with corrected text. Other chunks are not synthesized again. The task returns to pending.

Path parameters

task_idstringrequired

Task ID

1-36

Request bodyapplication/json

corrected_chunksarrayrequired

Corrected text for exactly each index from blocked_chunks, no more and no fewer

at least 1 item
indexintegerrequired

Index from blocked_chunks

textstringrequired

New fragment text

1-2500 characters, not whitespace only

Responseapplication/json

FieldTypeDescription
task_idstringTask ID
statusstringpending
retry_chunks_countintegerHow many fragments are resynthesized
charged_charactersintegerExtra charge for characters beyond the original fragments

Errors

StatusCodeWhen
401Missing Authorization header or the token is invalid
400retry_chunks_mismatchIndexes do not match (missing, unexpected)
400duplicate_chunk_indexAn index is repeated
400empty_textEmpty fragment text (indices)
402insufficient_charactersThe text is longer and there are no characters for the extra charge
403user_blockedThe user is blocked
404task_not_foundThe task does not exist or belongs to another token
409not_censoredThe task is not in censored
409retry_unavailableThere is no retry data, create a new task
503queue_unavailableThe queue is unavailable; the task stayed censored

Examples

Fix a dialogue part
curl -X POST "https://voicer.mat3u.com/v2/tasks/6b1d7e42-0c3a-4f8e-9a51-d2c47e8b9f03/retry" \
  -H "Authorization: Bearer $VOICER_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
  "corrected_chunks": [
    {
      "index": 0,
      "text": "[EXAVITQu4vr4xnSDxMaL]: Ты уже слышал про диалоги?\n[CwhRBWXzGAHq8TQ4Fs17]: Слышал. Сейчас и проверим."
    }
  ]
}'
Response 202
{
  "task_id": "6b1d7e42-0c3a-4f8e-9a51-d2c47e8b9f03",
  "status": "pending",
  "retry_chunks_count": 1,
  "charged_characters": 0
}
Fix one fragment
curl -X POST "https://voicer.mat3u.com/v2/tasks/6b1d7e42-0c3a-4f8e-9a51-d2c47e8b9f03/retry" \
  -H "Authorization: Bearer $VOICER_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
  "corrected_chunks": [
    {
      "index": 3,
      "text": "Фрагмент, переписанный мягче."
    }
  ]
}'
Response 202
{
  "task_id": "6b1d7e42-0c3a-4f8e-9a51-d2c47e8b9f03",
  "status": "pending",
  "retry_chunks_count": 1,
  "charged_characters": 0
}

Dialogues

A dialogue is a regular TTS task made of lines spoken by different voices. Status, download and cancellation go through the task endpoints, and characters are charged exactly like a regular task of the same length.

Engines and voices

labs synthesizes the dialogue through ElevenLabs text-to-dialogue. Voices are standard ElevenLabs voices, and audio tags such as [excited] and [laughs] are acted out by the model. The default model is eleven_v4; eleven_v4_turbo and eleven_v3 are also available.

cartesia synthesizes every line with its own Cartesia TTS request and concatenates them. A line voice is a clone ID, a Cartesia stock voice, or an ElevenLabs voice ID that already has a clone. The language picks the clone: if there is no clone in the text language, Slavic languages fall back to the voice's ru clone and everything else to the en clone, while the synthesis language stays the text language. Cartesia stock voices have a fixed language and read text in another language with an accent. Cartesia understands laughter and emotion tags; other tags are stripped.

A long dialogue is split into parts: up to 1000 characters per request for labs and 500 for cartesia, without breaking lines unnecessarily. A dialogue has at most 10 different voices. Dialogues have no captions: the provider response carries no timestamps.

Wait for the result
created = s.post(f"{BASE}/v2/dialogues", json=body, timeout=60).json()
task_id = created["task_id"]
while True:
    task = s.get(f"{BASE}/v2/tasks/{task_id}", timeout=30).json()
    if task["status"] in {"completed", "failed", "cancelled", "censored"}:
        break
    time.sleep(3)
file = s.get(f"{BASE}/v2/tasks/{task_id}/download", timeout=180)

How to write the text

In text a line starts with a row [voice_id]: text. The voice_id is the same voice ID as in a regular task. A row without a mark at the start continues the previous line, so a line can span several rows. Blank rows at the start of the text are skipped.

The whole dialogue text
[EXAVITQu4vr4xnSDxMaL]: [excited] Have you heard about dialogues?
[CwhRBWXzGAHq8TQ4Fs17]: [laughs] I have.
Let's check how it sounds.

Create a dialogue

POST/v2/dialoguesToken in the Authorization header

Lines of several voices in one file. Characters are charged immediately for the whole text; the response is 202 with task_id. Status, download and cancellation work like tasks: GET /v2/tasks/{task_id}, /download, /cancel.

Request bodyapplication/json

textstring

The whole dialogue as one text: each line starts on a new row with the voice in square brackets followed by a colon. A line spanning several rows continues without a voice mark. Send either text or inputs. What such text looks like: How to write the text

up to 1 000 000 characters
inputsarray

Lines in order, when text is not sent

1-2000 lines
textstringrequired

Line text

1-10 000 characters
voice_idstringrequired

Line voice

1-50 characters
enginestring

Engine

Default: "labs".
  • labsElevenLabs text-to-dialogue: a part of the dialogue is synthesized in one request
  • cartesiaEach line is a separate Cartesia TTS request, then concatenated
model_idstring

Model. For labs, omitted means eleven_v4; eleven_v4_turbo and eleven_v3 are also available. For cartesia, the same as Cartesia tasks, default sonic-3.6

up to 50 characters
language_codestring

Text language. For cartesia it picks the ElevenLabs voice clone in that language and the synthesis language; omitted: detected from the line text. For labs it is passed to ElevenLabs as is and may be omitted

2-8 characters, for example ru, en, de
settingsobject

ElevenLabs voice settings, labs only. Cartesia ignores them

stabilitynumber

Intonation stability

0-1
seedinteger

Result repeatability, labs only. Cartesia ignores it

0-4 294 967 295
split_outputboolean

true: a ZIP of per-part MP3 files instead of one MP3

Default: false.

Responseapplication/json

FieldTypeDescription
task_idstringTask ID
statusstringAlways pending
enginestringSelected engine
model_idstringSelected model
voicesarrayVoices after validation, in order of appearance (clone IDs for Cartesia)
language_codestringFor cartesia, the synthesis language: from the request or detected from the text; for labs, the request value, otherwise null
lines_countintegerNumber of lines
parts_countintegerRequests to the provider (task chunks)
charactersintegerLength of all lines
charged_charactersintegerCharacters charged; 0 on an unlimited plan

Errors

StatusCodeWhen
401Missing Authorization header or the token is invalid
400empty_textThe lines contain nothing that can be spoken
400too_many_voicesThe dialogue has more than 10 different voices
400too_many_chunksMore parts than the tariff max_chunks_per_task
400voice_not_foundThe voice was not found for this engine
400invalid_modelThe model is not supported
402subscription_requiredNo subscription or character package
402subscription_expiredThe unlimited subscription has expired
402insufficient_charactersNot enough characters
403dialogues_closedDialogues are in a closed test and the user is not on the list
403dialogues_disabledDialogues are disabled
403user_blockedThe user is blocked
403engine_not_allowedThe engine is closed by the tariff
429too_many_active_tasksThe license max_parallel_tasks is reached
503queue_unavailableThe queue is unavailable, characters were refunded
503elevenlabs_unavailableElevenLabs did not respond, retry later
503maintenanceMaintenance, new tasks are not accepted

Examples

Text with voices inline
curl -X POST "https://voicer.mat3u.com/v2/dialogues" \
  -H "Authorization: Bearer $VOICER_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
  "text": "[EXAVITQu4vr4xnSDxMaL]: [excited] Ты уже слышал про диалоги?\n[CwhRBWXzGAHq8TQ4Fs17]: [laughs] Слышал. Сейчас и проверим."
}'
Response 202
{
  "task_id": "6b1d7e42-0c3a-4f8e-9a51-d2c47e8b9f03",
  "status": "pending",
  "engine": "labs",
  "model_id": "eleven_v4",
  "voices": [
    "EXAVITQu4vr4xnSDxMaL",
    "CwhRBWXzGAHq8TQ4Fs17"
  ],
  "language_code": null,
  "lines_count": 2,
  "parts_count": 1,
  "characters": 72,
  "charged_characters": 72
}
Labs, lines one by one
curl -X POST "https://voicer.mat3u.com/v2/dialogues" \
  -H "Authorization: Bearer $VOICER_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
  "inputs": [
    {
      "text": "[excited] Ты уже слышал про диалоги?",
      "voice_id": "EXAVITQu4vr4xnSDxMaL"
    },
    {
      "text": "[laughs] Слышал. Сейчас и проверим.",
      "voice_id": "CwhRBWXzGAHq8TQ4Fs17"
    }
  ]
}'
Response 202
{
  "task_id": "6b1d7e42-0c3a-4f8e-9a51-d2c47e8b9f03",
  "status": "pending",
  "engine": "labs",
  "model_id": "eleven_v4",
  "voices": [
    "EXAVITQu4vr4xnSDxMaL",
    "CwhRBWXzGAHq8TQ4Fs17"
  ],
  "language_code": null,
  "lines_count": 2,
  "parts_count": 1,
  "characters": 62,
  "charged_characters": 62
}
Cartesia: the language is detected from the text
curl -X POST "https://voicer.mat3u.com/v2/dialogues" \
  -H "Authorization: Bearer $VOICER_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
  "engine": "cartesia",
  "inputs": [
    {
      "text": "Слушай, ты видел вчерашний отчёт?",
      "voice_id": "EXAVITQu4vr4xnSDxMaL"
    },
    {
      "text": "Видел. Половина посчитана по старой методике.",
      "voice_id": "CwhRBWXzGAHq8TQ4Fs17"
    }
  ]
}'
Response 202
{
  "task_id": "6b1d7e42-0c3a-4f8e-9a51-d2c47e8b9f03",
  "status": "pending",
  "engine": "cartesia",
  "model_id": "sonic-3.6",
  "voices": [
    "3a775eba-b397-4f6a-ad71-7e84163c3781",
    "d3419c87-ace1-4529-a916-dc1b4537d979"
  ],
  "language_code": "ru",
  "lines_count": 2,
  "parts_count": 1,
  "characters": 75,
  "charged_characters": 75
}

Result

You can download only a completed task. Audio is kept for a limited time after completion, then deleted, and /download responds 410 result_expired. Download as soon as the task is ready.

Download the result

GET/v2/tasks/{task_id}/downloadToken in the Authorization header

MP3 of a completed task, or a ZIP of per-chunk MP3 files if the task was created with split_output: true. The first download of a long task assembles the file from chunks and can take several minutes; later downloads are served immediately.

Path parameters

task_idstringrequired

Task ID

1-36

Responseaudio/mpeg | application/zip

FieldTypeDescription
Content-Dispositionheadervoice_<task_id>.mp3 or voice_<task_id>_chunks.zip
Content-LengthheaderFile size

Errors

StatusCodeWhen
401Missing Authorization header or the token is invalid
404task_not_foundThe task does not exist or belongs to another token
409task_not_completedThe task is not completed yet (status)
409task_incompleteNot all chunks are ready (ready, total, missing)
410result_expiredThe audio has already been deleted after retention

Examples

Save the MP3
curl "https://voicer.mat3u.com/v2/tasks/6b1d7e42-0c3a-4f8e-9a51-d2c47e8b9f03/download" \
  -H "Authorization: Bearer $VOICER_TOKEN" \
  -o voice.mp3
Response 200
MP3 (audio/mpeg)

Inside the ZIP files are named by chunk number with leading zeros: 0000.mp3, 0001.mp3, and so on. In ZIP mode an auto_pause pause sits at the end of the matching file.

Download via a signed link

GET/v2/tasks/{task_id}/fileSignature in the URL, no token

Same as /download, but instead of a token it uses the signature from /download-link. You usually do not build the URL by hand: take it whole from url.

Path parameters

task_idstringrequired

Task ID

1-36

Query parameters

expintegerrequired

Unix time when the link ends

sigstringrequired

HMAC signature

64 hexadecimal characters

Responseaudio/mpeg | application/zip

Errors

StatusCodeWhen
401invalid_download_linkThe signature is invalid or has expired
409task_not_completedThe task is not completed yet (status)
409task_incompleteNot all chunks are ready (ready, total, missing)
410result_expiredThe audio has already been deleted after retention

Examples

Download
curl -G "https://voicer.mat3u.com/v2/tasks/6b1d7e42-0c3a-4f8e-9a51-d2c47e8b9f03/file" \
  --data-urlencode "exp=1792327200" \
  --data-urlencode "sig=9f2c4e7a1b3d5f60718293a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4" \
  -o voice.mp3
Response 200
MP3 (audio/mpeg)

Captions

There are two ways to get captions for a TTS result. They differ in speed and in which engines they work for.

Which method to pick

A reliable strategy: try /subtitles first, and on 409 no_timestamps fall back to /v2/captions.

/v2/tasks/{task_id}/subtitles/v2/captions
SourceElevenLabs timestamps from synthesisSpeech recognition
Enginesoriginal onlyAny, including cartesia
SpeedInstantSeconds or minutes
Formatssrt, vtt, jsonsrt, vtt
Task statuscompleted or censoredFinished audio

Captions from ElevenLabs timestamps

GET/v2/tasks/{task_id}/subtitlesToken in the Authorization header

Instant captions from the timestamps ElevenLabs returns with the audio. Works for engine=original tasks in completed or censored. auto_pause pauses are included.

Path parameters

task_idstringrequired

Task ID

1-36

Query parameters

formatstring

File format

Default: "srt".
  • srtSubRip, application/x-subrip
  • vttWebVTT, text/vtt
  • jsonPer-character marks: characters, character_start_times_seconds, character_end_times_seconds

Responseapplication/x-subrip | text/vtt | application/json | application/zip

Errors

StatusCodeWhen
401Missing Authorization header or the token is invalid
404task_not_foundThe task does not exist or belongs to another token
409task_not_readySynthesis is not finished yet
409no_timestampsThe task has no timestamps (usually Cartesia)

Examples

SRT
curl -G "https://voicer.mat3u.com/v2/tasks/6b1d7e42-0c3a-4f8e-9a51-d2c47e8b9f03/subtitles" \
  --data-urlencode "format=srt" \
  -H "Authorization: Bearer $VOICER_TOKEN" \
  -o voice.srt
Response 200
1
00:00:00,000 --> 00:00:02,310
Привет! Это тест Voicer API.

With split_output: true you get a ZIP <task_id>_subtitles.zip with one file per chunk; timestamps in each file start at zero.

Timestamps are kept for an hour after the task finishes, then you get 409 no_timestamps. Request captions right after completed.

For dubbing, request captions by the task_id of each track in dubs: timestamps match the track's aligned audio.

Captions via speech recognition

POST/v2/captionsToken in the Authorization header

Captions for finished audio via speech recognition. Works for any engine, including Cartesia. Characters are not charged. The request is synchronous and takes from seconds to a couple of minutes.

Request bodyapplication/json

task_idstringrequired

Your task ID

1-36
formatstring

File format

Default: "srt".
  • srtSubRip
  • vttWebVTT
languagestring

Spoken language: ISO 639-1 (ru, en, uk, de...) or auto. Empty or auto: detect from audio among ru, en, uk

up to 16 characters

Responseapplication/x-subrip | text/vtt | application/zip

Errors

StatusCodeWhen
401Missing Authorization header or the token is invalid
404task_not_foundThe task does not exist or belongs to another token
409task_not_readySynthesis is not finished yet
422invalid_languageThe language code is not recognized
502transcription_failedSpeech could not be recognized
503transcription_timeoutRecognition took too long
503no_account_availableRecognition is temporarily unavailable

Examples

SRT in Russian
curl -X POST "https://voicer.mat3u.com/v2/captions" \
  -H "Authorization: Bearer $VOICER_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
  "task_id": "6b1d7e42-0c3a-4f8e-9a51-d2c47e8b9f03",
  "format": "srt",
  "language": "ru"
}' \
  -o voice.srt
Response 200
1
00:00:00,120 --> 00:00:02,280
Привет! Это тест Voicer API.

For a task with split_output: true you get a ZIP with 0000.srt, 0001.srt per chunk. For a task with auto_pause each chunk is recognized separately and shifted by the duration of the previous ones, so timestamps match the pauses in the MP3.

Captions for your own audio

POST/v2/captions/uploadToken in the Authorization header

Captions for any audio file without a TTS task. The result name is taken from the file name.

Form fieldsmultipart/form-data

filefilerequired

Audio file

formatstring

File format

Default: "srt".
  • srtSubRip
  • vttWebVTT
languagestring

Spoken language: ISO 639-1 or auto; empty: detect from audio

Responseapplication/x-subrip | text/vtt

Errors

StatusCodeWhen
401Missing Authorization header or the token is invalid
400empty_audioEmpty file
422invalid_languageThe language code is not recognized
502transcription_failedSpeech could not be recognized
503transcription_timeoutRecognition took too long
503no_account_availableRecognition is temporarily unavailable

Examples

VTT for a podcast
curl -X POST "https://voicer.mat3u.com/v2/captions/upload" \
  -H "Authorization: Bearer $VOICER_TOKEN" \
  -F "format=vtt" \
  -F "language=auto" \
  -F "file=@podcast.mp3" \
  -o podcast.vtt
Response 200
WEBVTT

00:00.000 --> 00:03.420
Добрый вечер, это выпуск номер сорок два.

Music and sound effects

Music and short sounds. Both methods are synchronous, with no task queue and no character charge. An active subscription is required.

Create music

POST/v2/musicToken in the Authorization header

A track from a text prompt. The call is synchronous and does not charge characters. An active subscription is required: unlimited still in term, or a character pack with a balance. download_url lasts about a day.

Request bodyapplication/json

promptstringrequired

Track description: genre, mood, era

1-2000 characters

Responseapplication/json

FieldTypeDescription
idstringProvider track ID
statusstringStatus, for example generating_music
progressnumberProgress from 0 to 1, when the provider sends it
promptstringPrompt that was sent
titlestringTrack title
descriptionstringShort description
genresstring[]Genres
bpmintegerTempo
duration_msintegerLength in milliseconds
download_urlstringFile URL, usually MP4
failure_reasonstringFailure reason, when present
waveforminteger[]Amplitudes for a waveform

Errors

StatusCodeWhen
401Missing Authorization header or the token is invalid
403user_blockedThe user is blocked
402subscription_requiredNo active subscription
402subscription_expiredThe unlimited subscription has ended
429too_many_generationsThe tariff thread limit on this key is reached
503no_proxyGeneration is temporarily unavailable
502generation_failedThe request was rejected or the response could not be parsed
503elevenlabs_unavailableElevenLabs did not respond, retry later

Examples

Dubstep
curl -X POST "https://voicer.mat3u.com/v2/music" \
  -H "Authorization: Bearer $VOICER_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
  "prompt": "dapstep 2012"
}'
Response 200
{
  "id": "b2WXbFqLJFRpGXFCxhil",
  "status": "generating_music",
  "progress": 1.0,
  "prompt": "dapstep 2012",
  "title": "Data Step",
  "description": "A dark electronic track.",
  "genres": [
    "electronic"
  ],
  "bpm": 140,
  "duration_ms": 30000,
  "download_url": "https://storage.googleapis.com/example/track.mp4",
  "failure_reason": null,
  "waveform": [
    871,
    -845
  ]
}

Until status is final, the file at download_url may still be written. Download it again after a few seconds.

Create a sound effect

POST/v2/sound-effectsToken in the Authorization header

Several takes of an effect from text. The call is synchronous and does not charge characters. An active subscription is required. audio_base64 is the audio itself (Opus), not a waveform picture.

Request bodyapplication/json

textstringrequired

What should be heard

1-500 characters
loopboolean

Loop the clip

Default: false.
output_formatstring

Provider audio format

Default: "opus_48000_128". 1-64 characters

Responseapplication/json

FieldTypeDescription
itemsobject[]Effect takes
items[].idstringTake ID
items[].textstringRequest text
items[].content_typestringMIME, usually audio/opus
items[].duration_secondsnumberLength in seconds
items[].audio_base64stringAudio as base64
items[].subgroup_idstringShared group of takes from one request
items[].subgroup_positionintegerTake number in the group

Errors

StatusCodeWhen
401Missing Authorization header or the token is invalid
403user_blockedThe user is blocked
402subscription_requiredNo active subscription
402subscription_expiredThe unlimited subscription has ended
429too_many_generationsThe tariff thread limit on this key is reached
503no_proxyGeneration is temporarily unavailable
502generation_failedThe request was rejected or the response could not be parsed
503elevenlabs_unavailableElevenLabs did not respond, retry later

Examples

Engine sound
curl -X POST "https://voicer.mat3u.com/v2/sound-effects" \
  -H "Authorization: Bearer $VOICER_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
  "text": "звук мотора",
  "loop": false,
  "output_format": "opus_48000_128"
}'
Response 200
{
  "items": [
    {
      "id": "2zjuIQWXhD2LY3KLArzI",
      "text": "звук мотора",
      "content_type": "audio/opus",
      "duration_seconds": 1.0,
      "audio_base64": "T2dnUw",
      "subgroup_id": "IXeCrNEYdt2fy8KmsRhN",
      "subgroup_position": 1
    }
  ]
}

Gotchas

Behaviour that is easy to miss when integrating. Each note is also linked at the end of its section.

A dialogue retry changes the text and keeps the voices

In a dialogue blocked_chunks, text is the whole part with all its lines. Send it to /retry as the same text with voices inline as on creation: the same voice_id values in the same order, only the line text changes. A different set of voices or text without marks yields 409 retry_unavailable.

Sections: Dialogues

Clones from ElevenLabs voices do not use slots

A clone created with elevenlabs_voice_id goes into the shared catalog and does not spend max_cartesia_clones. Only a clone from your own audio uses a slot. If a clone of that voice in that language already exists, /clone returns it with reused: true and creates nothing.

Only deletion frees a slot

clones_used counts your clones in any status. To free a slot, delete the clone with DELETE /v2/voices/cartesia/{voice_id}. Shared-catalog clones cannot be deleted and do not use slots.

Sections: Voice cloning

An ElevenLabs voice ID works in a Cartesia task

In a task with engine=cartesia you can pass an ElevenLabs voice ID that has a clone: the service takes yours first, then one from the shared catalog. If there is no clone, you get 400 voice_not_found: call /clone with elevenlabs_voice_id first.

A clone is created in the Cartesia language you set

A Sonic language code (ru, en, de, uk, ja, and the rest) is stored as the clone language. An empty code means ru. If Cartesia has no such code, an unsupported Slavic code (be, sr, bs, sl, mk, cnr) produces a ru clone and anything else an en clone. A task language selects the clone of the same ElevenLabs voice in that language when one exists, and is sent to synthesis.

ElevenLabs library voices are not for engine=original

A task with engine=original accepts only the standard ElevenLabs voices from the table. A voice from search or featured yields 400 voice_not_allowed. You can speak with it through a Cartesia clone.

Characters are charged on create

The cost is len(text), multiplied by (1 + 1.5 × number of languages) with dub_languages, and is charged immediately. Characters are refunded when you cancel a pending task, on 503 queue_unavailable, and when synthesis ends as failed. On censored they are not refunded: the spoken part is kept, and blocked fragments are resynthesized via /retry.

Sections: TTS tasks

Only unlimited can cancel a started task

A task in processing can be cancelled only on an unlimited subscription, where nothing was charged for it. On a character package you get 409 already_processing.

Sections: TTS tasks

Parallel task limit per license

A slot is taken by tasks in pending, processing, and pending_confirmation. Beyond the tariff max_parallel_tasks you get 429 too_many_active_tasks: wait for one task to finish. How fast a task itself runs is threads_per_task, how many chunks are synthesized in parallel.

Cartesia splits text at 500 characters

For engine=cartesia, max_chunk_length is capped at 500 in smart and max_length. In paragraphs the line is sent whole, up to 2500 characters.

Sections: TTS tasks

Chinese, Japanese, and Korean: up to 500 characters

If more than 20% of the text is CJK characters or Hangul, chunk length is capped at 500 for any engine.

Sections: TTS tasks

Quotes are normalized

Every kind of quote («», „“, “”, 「」) in chunks is replaced with ", and an unpaired quote at a chunk edge is removed so the model does not speak it. smart does not split text inside quotes.

Sections: TTS tasks

voice_settings is ElevenLabs only

Cartesia ignores voice_settings. An ElevenLabs model (eleven_*) in a Cartesia task is not an error: it is replaced with sonic-3.6.

Sections: TTS tasks

Pauses between chunks

auto_pause adds duration seconds of silence at the end of every frequency-th chunk; there is no pause after the last chunk. Pauses are included in captions from both methods. To place pauses between paragraphs, use split_type: paragraphs.

Sections: TTS tasks, Captions

split_output: ZIP per chunk

With split_output: true, /download returns a ZIP 0000.mp3, 0001.mp3, ... without concatenation, and captions come as a ZIP with one file per chunk and local timestamps. The mode is set when the task is created and cannot be changed later.

Sections: Result, Captions

The first download is slower

A single MP3 is assembled from chunks on the first /download and then cached. For long texts set a read timeout of at least 10 minutes. Audio is kept for a limited time, after which you get 410 result_expired.

Sections: Result

Audio and timestamps are kept for an hour

An hour after a task finishes, the service deletes its audio and ElevenLabs timestamps: /download responds 410 result_expired, /subtitles 409 no_timestamps, and /v2/captions has nothing left to recognize. Fetch files and captions right after completed. For dubbing, the original and every track are kept until the last language is ready and for an hour after it. A censored task keeps the spoken part while it waits for /retry; the task record itself is kept for 30 days.

Sections: Result, Captions

Retry after censorship

Only blocked_chunks are resynthesized. Indexes in corrected_chunks must match them exactly. If the new text is not longer, the retry is free and works even after the subscription ends. While the task is censored, captions via /subtitles are already available for the spoken part.

Sections: TTS tasks

pending_confirmation needs no action

The status appears when primary ElevenLabs synthesis has temporarily degraded. The task waits for an operator; you cannot cancel it from the API. Keep polling the status.

Sections: TTS tasks

Cartesia captions only via /v2/captions

Cartesia does not return timestamps, so /v2/tasks/{task_id}/subtitles yields 409 no_timestamps. Use POST /v2/captions with the same task_id. Recognition does not spend characters.

Sections: Captions

Maintenance

During maintenance POST /v2/tasks responds 503 maintenance. Other requests, including status and download, keep working.

Sections: TTS tasks

Music and effects need an active subscription

POST /v2/music and POST /v2/sound-effects do not charge characters and do not take a max_parallel_tasks slot. One API key runs at most threads_per_task generations at once; music and effects share that limit. An active subscription is required: unlimited still in term, or a character pack with a remaining balance.

Extra body fields are forbidden

Bodies of POST /v2/tasks and /retry are strict: an unknown field, a typo in a name, or a value out of range yields 422 with a list of errors in detail.

Sections: TTS tasks

Error codes

The detail.code value in an error response. Branch client logic on the code, not on the message text. 401 responses without a code and 422 validation are described in Error format.

Bad request

CodeStatusMeaningWhat to do
empty_text400The text has no letters or digitsCheck the text
text_too_long400The text is longer than max_chars_per_taskSplit into several tasks
too_many_chunks400More chunks than max_chunks_per_taskIncrease max_chunk_length or split the text
invalid_split400A paragraph is longer than 2500 characters in paragraphsSplit the paragraph or use smart
voice_not_found400, 404The voice was not found for the engineTake an ID from the voice lists
voice_not_allowed400The ElevenLabs voice is not in the standard listUse a standard voice or a Cartesia clone
invalid_model400The model is unsupported or retiredTake a model from /v2/voices/models
unsupported_dub_language400The dubbing language is unavailable for the engine or modelTake a language from available
dub_same_language400The dubbing language equals the text languageRemove that language
unknown_dub_voice400A dub_voices key is not in dub_languagesKeep only languages from dub_languages
too_many_voices400The dialogue has more than 10 different voicesReduce the number of voices
retry_chunks_mismatch400Indexes did not match blocked_chunksSend exactly missing with no unexpected
duplicate_chunk_index400An index is repeatedRemove duplicates
empty_audio400Empty fileCheck the file

Access and billing

CodeStatusMeaningWhat to do
subscription_required402No subscription or character packageBuy a package in the bot
subscription_expired402Unlimited has endedRenew the subscription in the bot
insufficient_characters402Not enough characters (required, available)Top up the balance or shorten the text
user_blocked403The user is blockedContact support
engine_not_allowed403The engine is closed by the tariffChange engine or the tariff
clone_limit_reached403All of your clone slots are usedDelete your clone or clone with elevenlabs_voice_id
shared_clone403A shared-catalog cloneNothing to delete, it does not use a slot
dialogues_closed403Dialogues are in a closed testWait for the public launch
dialogues_disabled403Dialogues are disabledRetry later
invalid_download_link401The signature is invalid or expiredGet a new link

Task and resource state

CodeStatusMeaningWhat to do
task_not_found404The task is missing or belongs to another tokenCheck task_id and the token
elevenlabs_voice_not_found404, 422The voice is not in ElevenLabsCheck the ID via search
preview_not_found404The voice has no sampleDo not show a player
task_not_completed409The task is not completed yetWait for the status
task_incomplete409Not all chunks are ready (missing)Retry in 10-60 seconds
task_not_ready409Synthesis is not finishedWait for the status
no_timestamps409The task has no ElevenLabs timestampsUse POST /v2/captions
already_processing409The task is already synthesizingWait for the result
already_cancelled409The task is already cancelledNothing
not_cancellable409The status does not allow cancelNothing
dub_track_not_cancellable409A dubbing track is cancelled with its main taskCancel the task from dub_of
not_censored409Retry is only for censoredCheck the status
retry_unavailable409There is no retry dataCreate a new task
result_expired410Audio was deleted after retentionSynthesize again

Files and validation

CodeStatusMeaningWhat to do
clip_too_large413The clip is larger than 16 MBCompress or trim
unsupported_format415The clip format is not supportedConvert to mp3 or wav
clip_required422No clip and no elevenlabs_voice_idSend one of the two
invalid_duration422The clip is not 10-60 secondsTrim or lengthen
invalid_audio422The file cannot be read as audioCheck the file
empty_clip422Empty clipCheck the file
cartesia_rejected422Cartesia did not accept the clipUse a cleaner clip, no music
invalid_language422The language code is not recognizedSend ISO 639-1 or auto

Limits and upstream

CodeStatusMeaningWhat to do
too_many_active_tasks429max_parallel_tasks is reachedWait for a task to finish
too_many_generations429The tariff thread limit on this API key is reachedWait for a generation to finish
transcription_failed502Speech was not recognizedCheck the audio, set language
cartesia_unavailable502Cartesia did not respondRetry in 10-60 seconds
transcription_timeout503Recognition did not finish in timeRetry in 10-60 seconds
queue_unavailable503The queue is unavailable, characters refundedRetry in 10-60 seconds
generation_failed502Generation was rejected or the response was invalidChange the request
no_proxy503No channel to generationRetry in 10-60 seconds
elevenlabs_unavailable503ElevenLabs did not respondRetry in 10-60 seconds
no_account_available503No free provider accountRetry in 10-60 seconds
download_signing_disabled503Signed links are disabledDownload via /download
maintenance503MaintenanceRetry later, the text is in message