# Как повысить точность (/accuracy)
Несколько простых способов повысить качество транскрибации от Nexara API.
## Качество аудио [#качество-аудио]
Чем чище исходный звук, тем точнее результат.
Записывайте на качественный микрофон, ближе к говорящему, в тихом помещении.
Отключайте нейросетевое шумоподавление и «улучшение» голоса на устройстве.
Такая обработка искажает речь и **ухудшает** распознавание. Модели нужен
естественный, необработанный звук.
Нейросетевые фильтры шума и «умное» улучшение звука вредят точности сильнее,
чем сам шум. Отправляйте аудио как есть, без предварительной обработки.
## Указывайте число говорящих при диаризации [#указывайте-число-говорящих-при-диаризации]
Если вы заранее знаете, сколько человек в записи, передайте `num_speakers` при диаризации (`task=diarize`). Это избавляет модель от необходимости угадывать количество говорящих и делает разделение точнее.
```python
from nexara import Nexara
client = Nexara() # ключ из переменной окружения NEXARA_API_KEY
result = client.transcriptions.create(
file="meeting.mp3",
task="diarize",
num_speakers=2,
)
```
```typescript
import { Nexara } from "nexara-sdk";
const client = new Nexara(); // ключ из переменной окружения NEXARA_API_KEY
const result = await client.transcriptions.create({
file: "meeting.mp3",
task: "diarize",
num_speakers: 2,
});
```
```bash
curl https://api.nexara.ru/v1/audio/transcriptions \
-H "Authorization: Bearer nx-XXXXXXXXXXXXXXXXXXXXXXXX" \
-F "file=@meeting.mp3" \
-F "model=whisper-1" \
-F "task=diarize" \
-F "num_speakers=2"
```
Указывайте `num_speakers`, только если уверены в числе говорящих. Если не
уверены — не передавайте параметр, и модель определит их количество
автоматически.
## Передавайте `diarization_setting` [#передавайте-diarization_setting]
`diarization_setting` — это настройка для модели диаризации, у нее есть два пресета — `general` и `telephonic`. `telephonic` лучше подходит для телефонных звонков, и в случаях, когда спикеры часто перебивают друг друга. Для всех остальных случаев `general` показывает более высокую точность. По умолчанию стоит `general`.
# Асинхронное распознавание (/async-transcription)
Асинхронный режим подходит для длинных записей и пакетной обработки: запрос не держит HTTP-соединение открытым всё время обработки. Вместо этого он сразу возвращает `job_id`, а готовый результат вы забираете отдельным запросом. Подробнее о разнице режимов — на странице [синхронная и асинхронная обработка](/sync-async).
## Шаг 1. Поставить задачу [#шаг-1-поставить-задачу]
Отправьте аудио на `POST /v1/audio/transcriptions/async`. Запрос принимает те же параметры, что и синхронный (можно передать локальный `file` или ссылку `url`), но возвращается сразу, не дожидаясь обработки.
В официальной библиотеке [`nexara`](/python-sdk) задачу ставит `create_job()` — он возвращает объект `Job` сразу, не дожидаясь обработки.
```python
from nexara import Nexara
client = Nexara() # ключ из переменной окружения NEXARA_API_KEY
job = client.transcriptions.create_job(file="long-audio.mp3")
print(job.job_id, job.status) # 5f1c2e3a-... in_progress
```
В официальной библиотеке [`nexara-sdk`](/typescript-sdk) задачу ставит `createJob()` — он возвращает объект `Job` сразу, не дожидаясь обработки.
```typescript
import { Nexara } from "nexara-sdk";
const client = new Nexara(); // ключ из переменной окружения NEXARA_API_KEY
const job = await client.transcriptions.createJob({ file: "long-audio.mp3" });
console.log(job.job_id, job.status); // 5f1c2e3a-... in_progress
```
```bash
curl https://api.nexara.ru/v1/audio/transcriptions/async \
-H "Authorization: Bearer nx-XXXXXXXXXXXXXXXXXXXXXXXX" \
-F "file=@long-audio.mp3" \
-F "model=whisper-1"
```
```python
import requests
with open("long-audio.mp3", "rb") as audio_file:
response = requests.post(
"https://api.nexara.ru/v1/audio/transcriptions/async",
headers={"Authorization": "Bearer nx-XXXXXXXXXXXXXXXXXXXXXXXX"},
files={"file": audio_file},
data={"model": "whisper-1"},
)
job_id = response.json()["job_id"]
print(job_id)
```
Node.js 18+:
```javascript
import fs from "fs";
const form = new FormData();
form.append("file", new Blob([fs.readFileSync("long-audio.mp3")]), "long-audio.mp3");
form.append("model", "whisper-1");
const response = await fetch(
"https://api.nexara.ru/v1/audio/transcriptions/async",
{
method: "POST",
headers: { Authorization: "Bearer nx-XXXXXXXXXXXXXXXXXXXXXXXX" },
body: form,
},
);
const { job_id } = await response.json();
console.log(job_id);
```
```go
package main
import (
"bytes"
"fmt"
"io"
"mime/multipart"
"net/http"
"os"
)
func main() {
file, _ := os.Open("long-audio.mp3")
defer file.Close()
var buf bytes.Buffer
writer := multipart.NewWriter(&buf)
part, _ := writer.CreateFormFile("file", "long-audio.mp3")
io.Copy(part, file)
writer.WriteField("model", "whisper-1")
writer.Close()
req, _ := http.NewRequest("POST", "https://api.nexara.ru/v1/audio/transcriptions/async", &buf)
req.Header.Set("Authorization", "Bearer nx-XXXXXXXXXXXXXXXXXXXXXXXX")
req.Header.Set("Content-Type", writer.FormDataContentType())
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
fmt.Println(string(body))
}
```
```ruby
require "net/http"
require "uri"
require "json"
uri = URI("https://api.nexara.ru/v1/audio/transcriptions/async")
request = Net::HTTP::Post.new(uri)
request["Authorization"] = "Bearer nx-XXXXXXXXXXXXXXXXXXXXXXXX"
request.set_form(
[
["file", File.open("long-audio.mp3")],
["model", "whisper-1"],
],
"multipart/form-data",
)
response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
http.request(request)
end
puts JSON.parse(response.body)["job_id"]
```
В ответ приходит идентификатор задачи и её статус:
```json
{
"job_id": "5f1c2e3a-...",
"status": "in_progress",
"created_at": "2026-07-13T10:00:00+00:00"
}
```
Сохраните `job_id` — по нему вы будете забирать результат.
## Шаг 2. Опросить статус [#шаг-2-опросить-статус]
Периодически запрашивайте `GET /v1/audio/transcriptions/async/{job_id}`, пока задача не завершится.
Обычно опрос не нужен: `job.wait()` сделает его за вас (см. [полный пример](#полный-пример)). Отдельный запрос статуса — в том числе из другого процесса — выглядит так:
```python
from nexara import Nexara
client = Nexara() # ключ из переменной окружения NEXARA_API_KEY
job = client.transcriptions.retrieve_job("5f1c2e3a-...")
print(job.status) # in_progress / complete / error
print(job.result) # None, пока задача не завершена
```
Обычно опрос не нужен: `job.wait()` сделает его за вас (см. [полный пример](#полный-пример)). Отдельный запрос статуса — в том числе из другого процесса — выглядит так:
```typescript
import { Nexara } from "nexara-sdk";
const client = new Nexara(); // ключ из переменной окружения NEXARA_API_KEY
const job = await client.transcriptions.retrieveJob("5f1c2e3a-...");
console.log(job.status); // in_progress / complete / error
console.log(job.result); // null, пока задача не завершена
```
```bash
curl https://api.nexara.ru/v1/audio/transcriptions/async/5f1c2e3a-... \
-H "Authorization: Bearer nx-XXXXXXXXXXXXXXXXXXXXXXXX"
```
```python
import requests
job_id = "5f1c2e3a-..."
response = requests.get(
f"https://api.nexara.ru/v1/audio/transcriptions/async/{job_id}",
headers={"Authorization": "Bearer nx-XXXXXXXXXXXXXXXXXXXXXXXX"},
)
print(response.json())
```
```javascript
const jobId = "5f1c2e3a-...";
const response = await fetch(
`https://api.nexara.ru/v1/audio/transcriptions/async/${jobId}`,
{ headers: { Authorization: "Bearer nx-XXXXXXXXXXXXXXXXXXXXXXXX" } },
);
console.log(await response.json());
```
```go
package main
import (
"fmt"
"io"
"net/http"
)
func main() {
jobID := "5f1c2e3a-..."
req, _ := http.NewRequest("GET", "https://api.nexara.ru/v1/audio/transcriptions/async/"+jobID, nil)
req.Header.Set("Authorization", "Bearer nx-XXXXXXXXXXXXXXXXXXXXXXXX")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
fmt.Println(string(body))
}
```
```ruby
require "net/http"
require "uri"
require "json"
job_id = "5f1c2e3a-..."
uri = URI("https://api.nexara.ru/v1/audio/transcriptions/async/#{job_id}")
request = Net::HTTP::Get.new(uri)
request["Authorization"] = "Bearer nx-XXXXXXXXXXXXXXXXXXXXXXXX"
response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
http.request(request)
end
puts response.body
```
Пока задача выполняется, поле `result` пустое:
```json
{
"job_id": "5f1c2e3a-...",
"status": "in_progress",
"created_at": "2026-07-13T10:00:00+00:00",
"completed_at": null,
"result": null,
"error": null
}
```
После завершения в поле `result` появляется готовая расшифровка:
```json
{
"job_id": "5f1c2e3a-...",
"status": "complete",
"created_at": "2026-07-13T10:00:00+00:00",
"completed_at": "2026-07-13T10:01:12+00:00",
"result": {
"text": "Привет! Это пример транскрибации."
},
"error": null
}
```
Поле `result` может содержать любой из [форматов ответа](/response-formats) — в зависимости от параметра `response_format`, переданного при постановке задачи.
### Статусы задачи [#статусы-задачи]
* `in_progress`: задача поставлена в очередь или обрабатывается. Поле `result` пустое;
* `complete`: обработка завершена успешно. Результат находится в поле `result`, а время завершения — в `completed_at`;
* `error`: обработка завершилась ошибкой. Описание причины находится в поле `error`.
## Полный пример [#полный-пример]
Постановка задачи и опрос до готового результата в одном скрипте:
`job.wait()` сам опрашивает статус с разумным интервалом и возвращает готовый результат. Если задача завершилась ошибкой, он бросает `JobFailedError` — такая задача не тарифицируется, и её можно бесплатно отправить заново.
```python
from nexara import Nexara
client = Nexara() # ключ из переменной окружения NEXARA_API_KEY
job = client.transcriptions.create_job(file="long-audio.mp3")
result = job.wait() # таймаут по умолчанию — 1800 секунд
print(result.text)
```
`job.wait()` сам опрашивает статус с разумным интервалом и возвращает готовый результат. Если задача завершилась ошибкой, он бросает `JobFailedError` — такая задача не тарифицируется, и её можно бесплатно отправить заново.
```typescript
import { Nexara } from "nexara-sdk";
const client = new Nexara(); // ключ из переменной окружения NEXARA_API_KEY
const job = await client.transcriptions.createJob({ file: "long-audio.mp3" });
const result = await job.wait(); // таймаут по умолчанию — 1_800_000 мс
console.log((result as { text: string }).text);
```
```python
import time
import requests
headers = {"Authorization": "Bearer nx-XXXXXXXXXXXXXXXXXXXXXXXX"}
# 1. Поставить задачу
with open("long-audio.mp3", "rb") as audio_file:
job = requests.post(
"https://api.nexara.ru/v1/audio/transcriptions/async",
headers=headers,
files={"file": audio_file},
data={"model": "whisper-1"},
).json()
job_id = job["job_id"]
# 2. Опрашивать статус, пока задача не завершится
while True:
status = requests.get(
f"https://api.nexara.ru/v1/audio/transcriptions/async/{job_id}",
headers=headers,
).json()
if status["status"] == "complete":
print(status["result"]["text"])
break
if status["status"] == "error":
raise RuntimeError(status["error"])
time.sleep(3)
```
## Что учитывать [#что-учитывать]
* **Хранение результата.** После завершения результат хранится на сервере **12 часов**, а затем удаляется безвозвратно.
* **Интервал опроса.** Опрашивайте статус с разумным интервалом (например, раз в несколько секунд), чтобы вы не уперлись в лимит частоты запросов.
* **Одновременные задачи.** На один API-ключ может выполняться не более **200** задач одновременно. Дождитесь завершения текущих, прежде чем ставить новые. Подробнее — на странице [лимитов](/limits).
* **Частота запросов.** Постановка задач и опрос статуса ограничены **10 запросами в секунду**. Подробнее — на странице [лимитов](/limits).
* **Форматы и размер.** Как и в синхронном режиме, принимается большинство [аудио- и видеоформатов](/supported-files), а максимальный размер файла — **3 ГБ**.
* **Хранение ключа.** Не вставляйте API-ключ прямо в код. Храните его в переменной окружения (например, `export NEXARA_API_KEY="nx-..."`) и ссылайтесь на неё. Подробнее — на странице [аутентификации](/authentication#curl).
# Аутентификация (/authentication)
Все запросы к Nexara API аутентифицируются с помощью **API-ключа**, который передаётся в заголовке `Authorization` по схеме `Bearer`.
## API-ключ [#api-ключ]
API-ключ выглядит так:
```
nx-XXXXXXXXXXXXXXXXXXXXXXXX
```
Ключ начинается с префикса `nx-`, за которым следуют 24 случайных символа (латинские буквы в верхнем и нижнем регистре и цифры).
API-ключ подобен паролю от аккаунта. Он даёт полный доступ к вашему аккаунту и
позволяет списывать средства с вашего баланса. Никогда не публикуйте ключи в
клиентском коде, репозиториях или сообщениях. Храните его в переменных
окружения или в защищённом хранилище секретов. Если думаете, что Ваш ключ утек
в сеть, немедленно замените его в [личном кабинете](https://app.nexara.ru).
## Получение ключа [#получение-ключа]
API-ключи создаются и управляются в личном кабинете — [app.nexara.ru](https://app.nexara.ru). Там же можно создавать новые ключи, задавать им имена и удалять (отзывать) ненужные.
## Использование ключа [#использование-ключа]
Передавайте ключ в HTTP-заголовке `Authorization` в формате `Bearer <ваш_ключ>`:
```
Authorization: Bearer nx-XXXXXXXXXXXXXXXXXXXXXXXX
```
### cURL [#curl]
```bash
curl https://api.nexara.ru/v1/audio/transcriptions \
-H "Authorization: Bearer nx-XXXXXXXXXXXXXXXXXXXXXXXX" \
-F "file=@audio.mp3" \
-F "model=whisper-1"
```
Чтобы не подставлять ключ в каждый запрос, сохраните его в переменную окружения `NEXARA_API_KEY`, а затем ссылайтесь на неё как `$NEXARA_API_KEY`:
```bash
export NEXARA_API_KEY="nx-XXXXXXXXXXXXXXXXXXXXXXXX"
curl https://api.nexara.ru/v1/audio/transcriptions \
-H "Authorization: Bearer $NEXARA_API_KEY" \
-F "file=@audio.mp3" \
-F "model=whisper-1"
```
Команда `export` действует только в текущей сессии терминала. Чтобы переменная
сохранялась между сессиями, добавьте строку `export
NEXARA_API_KEY="nx-..."` в файл конфигурации вашей оболочки (например,
`~/.bashrc` или `~/.zshrc`).
### Python SDK [#python-sdk]
Официальная библиотека [`nexara`](/python-sdk) добавляет заголовок сама. Если ключ не передан явно, он читается из переменной окружения `NEXARA_API_KEY`:
```python
from nexara import Nexara
client = Nexara() # ключ из переменной окружения NEXARA_API_KEY
```
Если ключ не найден ни в аргументе, ни в окружении, клиент сразу бросит понятную ошибку — до отправки запроса.
### TypeScript SDK [#typescript-sdk]
Официальная библиотека [`nexara-sdk`](/typescript-sdk) добавляет заголовок сама. Если ключ не передан явно полем `apiKey`, он читается из переменной окружения `NEXARA_API_KEY`:
```typescript
import { Nexara } from "nexara-sdk";
const client = new Nexara(); // ключ из переменной окружения NEXARA_API_KEY
```
Если ключ не найден ни в поле `apiKey`, ни в окружении, конструктор сразу бросит понятную ошибку — до отправки запроса.
### Python (OpenAI SDK) [#python-openai-sdk]
Nexara API совместим с OpenAI SDK — достаточно указать `base_url` и передать API-ключ Nexara. Обратите внимание, что в таком случае `Bearer` добавлять к ключу не нужно.
```python
from openai import OpenAI
client = OpenAI(
api_key="nx-XXXXXXXXXXXXXXXXXXXXXXXX",
base_url="https://api.nexara.ru/v1",
)
with open("audio.mp3", "rb") as audio_file:
transcript = client.audio.transcriptions.create(
model="whisper-1",
file=audio_file,
)
print(transcript.text)
```
Не храните ключ прямо в коде. Считывайте его из переменной окружения, например
`os.environ["NEXARA_API_KEY"]`.
## Ошибки аутентификации [#ошибки-аутентификации]
Если ключ отсутствует или недействителен, API возвращает статус **403 Forbidden** с описанием в поле `detail`:
| Сообщение | Причина | Как исправить |
| -------------------- | -------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------- |
| `No token specified` | Заголовок `Authorization` отсутствует, пуст или имеет неверный формат (ожидается `Bearer <ключ>`). | Добавьте заголовок `Authorization: Bearer <ваш_ключ>`. |
| `Invalid token` | Ключ не существует или был отозван (удалён). | Проверьте правильность ключа или создайте новый в [личном кабинете](https://app.nexara.ru). |
Пример тела ответа:
```json
{
"detail": "Invalid token"
}
```
Заголовок обязательно должен быть в формате `Bearer <ключ>`. Значение без префикса `Bearer`, лишние пробелы или `Bearer ` без самого ключа будут восприняты как отсутствие токена и приведут к ошибке `No token specified`.
## Отзыв ключа [#отзыв-ключа]
Если ключ скомпрометирован, удалите его в [личном кабинете](https://app.nexara.ru). Удалённый ключ немедленно перестаёт работать — все последующие запросы с ним будут отклонены с ошибкой `Invalid token`. При необходимости создайте новый ключ на замену.
# Диаризация (/diarization)
**Диаризация** — это разделение аудио по говорящим: API не только распознает речь, но и определяет, какой фрагмент речи кому принадлежит. Каждому сегменту присваивается метка говорящего (например, `speaker_0`, `speaker_1`), либо его роль в диалоге.
## Как включить [#как-включить]
Передайте параметр `task=diarize` в запросе на `POST /v1/audio/transcriptions`.
```python
from nexara import Nexara
client = Nexara() # ключ из переменной окружения NEXARA_API_KEY
result = client.transcriptions.create(
file="meeting.mp3",
task="diarize",
)
for segment in result.segments:
print(f"{segment.speaker}: {segment.text}")
```
```typescript
import { Nexara } from "nexara-sdk";
const client = new Nexara(); // ключ из переменной окружения NEXARA_API_KEY
const result = await client.transcriptions.create({
file: "meeting.mp3",
task: "diarize",
});
for (const segment of result.segments) {
console.log(`${segment.speaker}: ${segment.text}`);
}
```
```bash
curl https://api.nexara.ru/v1/audio/transcriptions \
-H "Authorization: Bearer nx-XXXXXXXXXXXXXXXXXXXXXXXX" \
-F "file=@meeting.mp3" \
-F "model=whisper-1" \
-F "task=diarize" \
-F "response_format=json"
```
Минимальная длительность аудио для диаризации — **3 секунды**. Более короткие
записи выдают ошибку `400`.
## Результат [#результат]
В ответе каждый сегмент содержит метку говорящего в поле `speaker`:
```json
{
"task": "diarize",
"language": "ru",
"duration": 12.4,
"text": "Здравствуйте! Чем могу помочь? Хочу оформить возврат.",
"segments": [
{
"start": 0.0,
"end": 2.1,
"text": "Здравствуйте! Чем могу помочь?",
"speaker": "speaker_0"
},
{
"start": 2.5,
"end": 5.0,
"text": "Хочу оформить возврат.",
"speaker": "speaker_1"
}
]
}
```
Метки говорящих сохраняются во всех форматах вывода. Например, при `response_format=text` результат выглядит так:
```
Speaker 0: Здравствуйте! Чем могу помочь?
Speaker 1: Хочу оформить возврат.
```
Диаризация также работает с форматами `srt`, `vtt` и `verbose_json`.
Вы можете передать `timestamp_granularities=['word']`, чтобы получить точные временные метки слов для каждого спикера. Более подробно об этом написано на странице про [временные метки](/timestamps).
## Полезные параметры [#полезные-параметры]
* `num_speakers`: если вы заранее знаете, сколько человек в записи, передайте это значение — это повысит качество диаризации. Этот параметр не гарантирует `num_speakers` спикеров в ответе. По умолчанию число определяется автоматически.
* `diarization_setting`: настройка для модели диаризации - `general` или `telephonic`. `telephonic` лучше подходит для телефонных звонков, и в случаях, когда спикеры часто перебивают друг друга. Для всех остальных случаев `general` показывает более высокую точность. По умолчанию стоит `general`.
## Многоканальное аудио [#многоканальное-аудио]
Если передавать `num_speakers=2`, то включается проверка похожести каналов. Если каналы разные, значит один спикер будет извлечен из одного канала, а другой — из другого. Данная настройка обеспечивает максимальное качество диаризации, но для этого нужно, чтобы телефония поддерживала режим записи звонка в два канала.
## Разметка ролей (role tagging) [#разметка-ролей-role-tagging]
По умолчанию говорящие обозначаются безликими метками `speaker_0`, `speaker_1`. **Разметка ролей** заменяет их на понятные роли — например, `Клиент` и `Агент`.
Разметка ролей включается параметром `roles` и доступна **только вместе с диаризацией** (`task=diarize`). Поддерживаются три режима:
`roles=auto` — модель сама определяет и назначает подходящие роли на основе содержания разговора.
JSON-массив строк, например `["Клиент", "Агент"]` — вы отправляете набор ролей, а модель распределяет их между говорящими.
JSON-объект `{"Клиент": "Клиент, который обратился в поддержку", "agent": "Оператор поддержки"}` — фиксированные роли с описаниями, которые помогают модели точнее их распознать.
Если модель определила больше говорящих, чем было передано в списке, остальные
спикеры будут помечены как неизвестные. Например, если в записи говорило 4
человека, а передано только 2 человека в списке, остальные будут помечены как
`unknown_1` и `unknown_2`.
Пример запроса с автоматической разметкой ролей:
```python
result = client.transcriptions.create(
file="call.mp3",
task="diarize",
roles="auto",
)
```
```typescript
const result = await client.transcriptions.create({
file: "call.mp3",
task: "diarize",
roles: "auto",
});
```
```bash
curl https://api.nexara.ru/v1/audio/transcriptions \
-H "Authorization: Bearer nx-XXXXXXXXXXXXXXXXXXXXXXXX" \
-F "file=@call.mp3" \
-F "model=whisper-1" \
-F "task=diarize" \
-F "roles=auto"
```
Пример с фиксированными ролями:
В [Python SDK](/python-sdk) роли передаются обычным списком или словарём — сериализация в JSON выполняется автоматически.
```python
result = client.transcriptions.create(
file="call.mp3",
task="diarize",
roles=["Клиент", "Агент"],
)
```
В [TypeScript SDK](/typescript-sdk) роли передаются обычным массивом или объектом — сериализация в JSON выполняется автоматически.
```typescript
const result = await client.transcriptions.create({
file: "call.mp3",
task: "diarize",
roles: ["Клиент", "Агент"],
});
```
```bash
curl https://api.nexara.ru/v1/audio/transcriptions \
-H "Authorization: Bearer nx-XXXXXXXXXXXXXXXXXXXXXXXX" \
-F "file=@call.mp3" \
-F "model=whisper-1" \
-F "task=diarize" \
-F 'roles=["Клиент", "Агент"]'
```
В результате метки `speaker_N` заменяются на назначенные роли:
```json
{
"task": "diarize",
"segments": [
{
"start": 0.0,
"end": 2.1,
"text": "Здравствуйте! Чем могу помочь?",
"speaker": "Агент"
},
{
"start": 2.5,
"end": 5.0,
"text": "Хочу оформить возврат.",
"speaker": "Клиент"
}
]
}
```
### Ограничения [#ограничения]
* В одном запросе можно задать не более **10** ролей.
* Каждое название роли — непустая строка длиной не более **64** символов, роли не должны повторяться.
* Описание роли (в режиме с описаниями) не должно превышать **500** символов.
Разметка ролей тарифицируется дополнительно к стоимости диаризации. Если
разметку не удалось применить (например, в записи не оказалось говорящих),
надбавка не списывается, а в ответе остаются исходные метки `speaker_N`. Стоимость услуги смотрите на странице [тарифов](/pricing).
# Распознавание эмоций (/emotions)
**Распознавание эмоций** оценивает, с какой эмоцией произнесён каждый фрагмент речи. К сегментам диаризации добавляется объект `emotion` с меткой (`angry`, `sad`, `neutral`, `positive`), уверенностью модели и распределением вероятностей по всем меткам.
Это удобно для аналитики звонков: видно не только *что* сказал клиент, но и *как* — например, в какой момент разговора он начал злиться.
Эмоцию определяет сама модель распознавания речи `nexara-ru` — отдельного
запроса и отдельной обработки не требуется, всё приходит в одном ответе.
## Что нужно для работы [#что-нужно-для-работы]
Эмоции считаются по сегментам речи, а сегменты появляются только при диаризации, поэтому у параметра три условия:
| Условие | Значение |
| ------------- | ------------------------- |
| Режим | `task=diarize` |
| Модель | `model=nexara-ru` |
| Формат ответа | `json` или `verbose_json` |
Форматы `text`, `srt` и `vtt` не подойдут: в них нет структуры, куда положить объект `emotion`. Любую другую комбинацию API отклоняет с ошибкой `400`, а [Python](/python-sdk) и [TypeScript](/typescript-sdk) SDK — ещё до отправки файла, чтобы запрос не тарифицировался впустую.
## Как включить [#как-включить]
Передайте `emotions=true` в запросе на `POST /v1/audio/transcriptions`. Замените `nx-XXXXXXXXXXXXXXXXXXXXXXXX` на свой [API-ключ](/authentication), а `call.mp3` — на путь к вашему файлу.
Официальная библиотека [`nexara`](/python-sdk) (`pip install nexara`):
```python
from nexara import Nexara
client = Nexara() # ключ из переменной окружения NEXARA_API_KEY
result = client.transcriptions.create(
file="call.mp3",
model="nexara-ru",
task="diarize",
emotions=True,
)
for segment in result.segments:
if segment.emotion:
print(f"{segment.speaker} [{segment.emotion.label}]: {segment.text}")
else:
print(f"{segment.speaker}: {segment.text}")
```
Официальная библиотека [`nexara-sdk`](/typescript-sdk) (`npm install nexara-sdk`):
```typescript
import { Nexara } from "nexara-sdk";
const client = new Nexara(); // ключ из переменной окружения NEXARA_API_KEY
const result = await client.transcriptions.create({
file: "call.mp3",
model: "nexara-ru",
task: "diarize",
emotions: true,
});
for (const segment of result.segments) {
if (segment.emotion) {
console.log(`${segment.speaker} [${segment.emotion.label}]: ${segment.text}`);
} else {
console.log(`${segment.speaker}: ${segment.text}`);
}
}
```
```bash
curl https://api.nexara.ru/v1/audio/transcriptions \
-H "Authorization: Bearer nx-XXXXXXXXXXXXXXXXXXXXXXXX" \
-F "file=@call.mp3" \
-F "model=nexara-ru" \
-F "task=diarize" \
-F "emotions=true"
```
```python
import requests
with open("call.mp3", "rb") as audio_file:
response = requests.post(
"https://api.nexara.ru/v1/audio/transcriptions",
headers={"Authorization": "Bearer nx-XXXXXXXXXXXXXXXXXXXXXXXX"},
files={"file": audio_file},
data={
"model": "nexara-ru",
"task": "diarize",
"emotions": "true",
},
)
for segment in response.json()["segments"]:
emotion = segment.get("emotion")
label = emotion["label"] if emotion else "—"
print(f"{segment['speaker']} [{label}]: {segment['text']}")
```
Node.js 18+:
```javascript
import fs from "fs";
const form = new FormData();
form.append("file", new Blob([fs.readFileSync("call.mp3")]), "call.mp3");
form.append("model", "nexara-ru");
form.append("task", "diarize");
form.append("emotions", "true");
const response = await fetch(
"https://api.nexara.ru/v1/audio/transcriptions",
{
method: "POST",
headers: { Authorization: "Bearer nx-XXXXXXXXXXXXXXXXXXXXXXXX" },
body: form,
},
);
const result = await response.json();
for (const segment of result.segments) {
console.log(`${segment.speaker} [${segment.emotion?.label ?? "—"}]: ${segment.text}`);
}
```
```go
package main
import (
"bytes"
"fmt"
"io"
"mime/multipart"
"net/http"
"os"
)
func main() {
file, _ := os.Open("call.mp3")
defer file.Close()
var buf bytes.Buffer
writer := multipart.NewWriter(&buf)
part, _ := writer.CreateFormFile("file", "call.mp3")
io.Copy(part, file)
writer.WriteField("model", "nexara-ru")
writer.WriteField("task", "diarize")
writer.WriteField("emotions", "true")
writer.Close()
req, _ := http.NewRequest("POST", "https://api.nexara.ru/v1/audio/transcriptions", &buf)
req.Header.Set("Authorization", "Bearer nx-XXXXXXXXXXXXXXXXXXXXXXXX")
req.Header.Set("Content-Type", writer.FormDataContentType())
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
fmt.Println(string(body))
}
```
```ruby
require "net/http"
require "uri"
require "json"
uri = URI("https://api.nexara.ru/v1/audio/transcriptions")
request = Net::HTTP::Post.new(uri)
request["Authorization"] = "Bearer nx-XXXXXXXXXXXXXXXXXXXXXXXX"
request.set_form(
[
["file", File.open("call.mp3")],
["model", "nexara-ru"],
["task", "diarize"],
["emotions", "true"],
],
"multipart/form-data",
)
response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
http.request(request)
end
JSON.parse(response.body)["segments"].each do |segment|
label = segment["emotion"] ? segment["emotion"]["label"] : "—"
puts "#{segment["speaker"]} [#{label}]: #{segment["text"]}"
end
```
## Результат [#результат]
У каждого оценённого сегмента появляется объект `emotion`:
```json
{
"task": "diarize",
"language": "ru",
"duration": 12.4,
"text": "Здравствуйте, чем могу помочь? Я третий раз звоню по одному и тому же вопросу!",
"segments": [
{
"start": 0.0,
"end": 3.21,
"text": "Здравствуйте, чем могу помочь?",
"speaker": "speaker_0",
"emotion": {
"label": "neutral",
"confidence": 0.87,
"probs": { "angry": 0.03, "sad": 0.04, "neutral": 0.87, "positive": 0.06 }
}
},
{
"start": 3.6,
"end": 8.9,
"text": "Я третий раз звоню по одному и тому же вопросу!",
"speaker": "speaker_1",
"emotion": {
"label": "angry",
"confidence": 0.91,
"probs": { "angry": 0.91, "sad": 0.05, "neutral": 0.03, "positive": 0.01 }
}
}
]
}
```
### Метки [#метки]
| Метка | Значение |
| ---------- | ------------------------------------ |
| `angry` | Злость, раздражение |
| `sad` | Грусть, расстроенность |
| `neutral` | Нейтральная, спокойная речь |
| `positive` | Позитив, радость, доброжелательность |
### Поля объекта [#поля-объекта]
* **`label`** — выбранная метка эмоции.
* **`confidence`** — уверенность модели в этой метке, от 0 до 1.
* **`probs`** — вероятности по всем меткам. Полезно, когда важна не только победившая эмоция: например, `angry` c `confidence` 0.45 и `neutral` 0.4 рядом — это скорее «лёгкое раздражение», чем настоящая злость.
## Ограничения [#ограничения]
* **Эмоция приходит не для каждого сегмента.** Если модель не смогла оценить фрагмент (слишком короткий, тишина, шум), поля `emotion` в сегменте просто не будет. Всегда проверяйте его наличие, а не обращайтесь к нему напрямую.
* **У слов эмоции нет.** Она считается по сегменту целиком, поэтому в массиве `words` объекта `emotion` не будет даже при `timestamp_granularities=["word"]`.
* **Только `nexara-ru` и только диаризация.** С `whisper-1` или с `task=transcribe` параметр вернёт ошибку `400`.
* **Только JSON-форматы ответа.** Для `text`, `srt` и `vtt` параметр вернёт ошибку `400`.
## Эмоции в LLM-анализе [#эмоции-в-llm-анализе]
Если в том же запросе передан `prompt`, метки эмоций уходят в языковую модель вместе с расшифровкой — анализ тональности опирается не только на слова, но и на то, как они сказаны. Модель видит примерно такой текст:
```text
Оператор [neutral]: Служба поддержки, здравствуйте.
Клиент [angry]: Я третий раз звоню по одному и тому же вопросу!
```
Ничего дополнительно включать не нужно: достаточно передать `emotions=true` и `prompt` в одном запросе. Подробнее про промпты и схемы — на странице [LLM-анализ](/llm-usage).
Что важно знать:
* **В модель уходит только метка.** `confidence` и `probs` остаются в ответе API, но в промпт не передаются.
* **Метка акустическая.** Она получена из тона голоса, а не из слов, поэтому может расходиться со смыслом сказанного — вежливая фраза, произнесённая раздражённо, получит метку `angry`. Модель об этом предупреждена отдельно.
Без `emotions=true` промпт не меняется: расшифровка уходит в модель ровно в том
же виде, что и раньше.
## Длинные записи [#длинные-записи]
Для длинных файлов параметр работает так же в [асинхронном режиме](/async-transcription) — передайте `emotions=true` при постановке задачи, и эмоции придут в готовом результате.
Распознавание эмоций тарифицируется дополнительно к стоимости диаризации. Если
ни один сегмент не удалось оценить, надбавка не списывается. Стоимость услуги
смотрите на странице [тарифов](/pricing).
# Ошибки (/errors)
При возникновении ошибки Nexara API возвращает соответствующий HTTP-код состояния и тело ответа в формате JSON с описанием проблемы в поле `detail`:
```json
{
"detail": "Недостаточно средств на аккаунте."
}
```
Ниже перечислены все коды ошибок, которые может вернуть API, с указанием причин и способов их устранения.
## 400 — Неверный запрос (Bad Request) [#400--неверный-запрос-bad-request]
Самая частая категория ошибок. Возникает при некорректных параметрах запроса. Проверка выполняется до обработки файла, поэтому такие ошибки возвращаются быстро.
Возможные причины:
* **Файл и ссылка одновременно** — указаны и `file`, и `url`. Нужно передать что-то одно.
* `Неверный запрос: укажите либо аудиофайл, либо ссылку, но не оба одновременно.`
* **Не указан ни файл, ни ссылка** — не передан ни `file`, ни `url`.
* `Неверный запрос: укажите либо аудиофайл, либо ссылку.`
* **Некорректная ссылка** — значение `url` не прошло валидацию.
* **Неподдерживаемая задача** — параметр `task` не входит в список поддерживаемых.
* `Задача '...' не поддерживается. Вот список поддерживаемых задач: ...`
* **Неподдерживаемый формат ответа** — параметр `response_format` не входит в список поддерживаемых.
* `Формат ответа '...' не поддерживается. Вот список поддерживаемых форматов: ...`
* **Неподдерживаемая модель** — параметр `model` не входит в список поддерживаемых.
* `Модель '...' не поддерживается. Поддерживаемые модели: ...`
* **Неподдерживаемый словарь** — параметр `dictionary` не входит в список поддерживаемых.
* `Словарь '...' не поддерживается. Поддерживаемые словари: ...`
* **Некорректные параметры генерации:**
* `temperature должна быть в диапазоне от 0.1 до 1.`
* `repetition_penalty должен быть в диапазоне от 0.1 до 2.`
* `length_penalty должен быть в диапазоне от 0.1 до 2.`
* **Неподдерживаемый язык** — параметр `language` не входит в список поддерживаемых.
* `Язык '...' не поддерживается. Список доступных языков - https://docs.nexara.ru/docs/languages.`
* **Файл слишком большой** — размер файла превышает максимально допустимый.
* `Неверный запрос: файл слишком большой. Максимальный размер - ... байт.`
* **Слишком короткое аудио:**
* `Минимальная длина аудио - 0.3 секунды.`
* `Минимальная длина аудио при диаризации - 3 секунды.` (для `task=diarize`)
* **Некорректная гранулярность таймкодов** — значение `timestamp_granularities` не поддерживается.
* `Гранулярность '...' не поддерживается. Поддерживаемые: ...`
* `Гранулярность 'sentence' доступна только для задачи 'transcribe'.`
* **Ошибки тегирования ролей (`roles`):**
* `Тегирование ролей (roles) доступно только с диаризацией (task=diarize).`
* `roles должен быть 'auto', JSON-списком строк или JSON-объектом {роль: описание}.`
* `Список ролей не может быть пустым.` / `Объект ролей не может быть пустым.`
* `Слишком много ролей. Максимум — ...`
* `Каждая роль должна быть непустой строкой не длиннее ... символов.`
* `Роли не должны повторяться.`
* `Описание роли должно быть строкой.` / `Описание роли не должно превышать ... символов.`
* **Ошибка обработки файла или ссылки:**
* `Error while processing the file metadata.`
* `Ошибка обработки ссылки на файл: ...`
**Как исправить:** внимательно проверьте параметры запроса и сообщение в поле `detail` — оно указывает на конкретную проблему.
## 402 — Требуется оплата (Payment Required) [#402--требуется-оплата-payment-required]
Недостаточно средств на балансе для выполнения запроса. Стоимость рассчитывается заранее по длительности аудио (с учётом надбавок за фильтр мата, тегирование ролей и LLM-обработку). Запрос отклоняется, если предполагаемая стоимость превышает баланс плюс лимит овердрафта.
* `Недостаточно средств на аккаунте.`
**Как исправить:** пополните баланс аккаунта.
## 403 — Доступ запрещён (Forbidden) [#403--доступ-запрещён-forbidden]
Проблема с аутентификацией по API-ключу. Это основной механизм авторизации для эндпоинтов транскрибации.
* `No token specified` — не передан API-ключ (заголовок `Authorization`).
* `Invalid token` — передан несуществующий или недействительный API-ключ.
**Как исправить:** убедитесь, что вы передаёте корректный API-ключ в заголовке `Authorization`.
## 404 — Не найдено (Not Found) [#404--не-найдено-not-found]
Запрашиваемый ресурс не существует.
* `Model not found` — запрошена несуществующая модель.
* `API key not found` — API-ключ не найден.
* `Job not found` — асинхронная задача с указанным `job_id` не найдена.
## 408 — Тайм-аут запроса (Request Timeout) [#408--тайм-аут-запроса-request-timeout]
Превышено время анализа медиафайла (`ffprobe`).
* `Media analysis timed out.` — при обработке загруженного файла.
* `Media analysis timed out (ffprobe). URL might be too slow or unresponsive.` — при обработке по ссылке (медленный или недоступный `url`).
**Как исправить:** проверьте доступность и скорость отдачи файла по ссылке, либо загрузите файл напрямую.
## 413 — LLM-обработка не успела завершиться (Payload Too Large) [#413--llm-обработка-не-успела-завершиться-payload-too-large]
Несмотря на название HTTP-кода, эта ошибка **не связана с размером файла**. Она возникает только на синхронном эндпоинте `/v1/audio/transcriptions`, когда в запросе указан `prompt` (LLM-обработка), а аудио длинное: сама транскрибация успешно завершилась, но пост-обработка языковой моделью не уложилась в синхронный лимит времени.
* `The synchronous LLM request timed out. Use async mode for long audio.`
**Как исправить:** не повторяйте тот же запрос синхронно — он снова упрётся в тайм-аут. Отправьте идентичный запрос через [асинхронный режим](/async-transcription) (`/v1/audio/transcriptions/async`), где на шаг LLM отводится значительно больший тайм-аут.
## 415 — Неподдерживаемый тип файла (Unsupported Media Type) [#415--неподдерживаемый-тип-файла-unsupported-media-type]
Формат файла или его MIME-тип не поддерживается. Полный список поддерживаемых форматов — на странице [Поддерживаемые файлы](/supported-files).
**Как исправить:** конвертируйте файл в один из поддерживаемых форматов и убедитесь, что указан правильный `Content-Type`.
## 429 — Слишком много запросов (Too Many Requests) [#429--слишком-много-запросов-too-many-requests]
Превышены лимиты частоты или конкурентности.
* Превышен лимит частоты запросов (rate limit).
* `Too many concurrent async jobs. Maximum is ... Please wait for existing jobs to complete before submitting new ones.` — превышено число одновременных асинхронных задач.
**Как исправить:** снизьте частоту запросов или дождитесь завершения текущих асинхронных задач перед отправкой новых.
## 500 — Внутренняя ошибка сервера (Internal Server Error) [#500--внутренняя-ошибка-сервера-internal-server-error]
Непредвиденная ошибка на стороне сервера.
* `Internal Server Error` — общая ошибка обработки.
* `Error retrieving user information.` — не удалось получить данные пользователя.
* `Ошибка при получении LLM-саммари.` — сбой при генерации LLM-обработки (при этом запрос не тарифицируется).
**Как исправить:** повторите запрос позже. Если ошибка повторяется — обратитесь в [Поддержку](https://t.me/RND_RandoM).
## 502 — Ошибка LLM-провайдера (Bad Gateway) [#502--ошибка-llm-провайдера-bad-gateway]
Редкая ошибка: при LLM-обработке провайдер языковой модели вернул пустой или некорректный ответ. Это обычная серверная ошибка — в отличие от `413`, где транскрибация уже готова и достаточно повторить запрос в асинхронном режиме.
**Как исправить:** повторите запрос позже или обратитесь в [Поддержку](https://t.me/RND_RandoM).
## Обработка ошибок в SDK [#обработка-ошибок-в-sdk]
В официальных библиотеках ([Python](/python-sdk) и [TypeScript](/typescript-sdk)) каждому коду соответствует типизированное исключение — `BadRequestError` (400), `InsufficientBalanceError` (402), `AuthenticationError` (403), `NotFoundError` (404), `SyncLLMTimeoutError` (413), `RateLimitError` (429), `InternalServerError` (500), `BadGatewayError` (502). Сообщение сервера доступно в поле `detail`, а ошибки `429` и сетевые сбои повторяются автоматически. Поймав `SyncLLMTimeoutError`, повторно отправьте тот же запрос через `create_job()` / `createJob()` — синхронный повтор снова упрётся в тайм-аут.
```python
from nexara import Nexara, InsufficientBalanceError
client = Nexara()
try:
result = client.transcriptions.create(file="audio.mp3")
except InsufficientBalanceError as e: # 402
print("Пополните баланс:", e.detail)
```
Подробнее — в разделе [ошибки Python SDK](/python-sdk#ошибки).
```typescript
import { Nexara, InsufficientBalanceError } from "nexara-sdk";
const client = new Nexara();
try {
await client.transcriptions.create({ file: "audio.mp3" });
} catch (e) {
if (e instanceof InsufficientBalanceError) {
console.log("Пополните баланс:", e.detail); // 402
} else {
throw e;
}
}
```
Подробнее — в разделе [ошибки TypeScript SDK](/typescript-sdk#ошибки).
Если вы столкнулись с ошибкой, причину которой не удаётся определить по
сообщению, напишите в [Поддержку](https://t.me/RND_RandoM) — приложите текст
ошибки и, по возможности, идентификатор запроса.
# Файл по ссылке (/file-url)
Вместо загрузки файла с диска можно передать прямую ссылку на аудио, и Nexara API скачает файл и вернёт текст из аудио. Вместо поля `file` передаётся поле `url` в том же запросе `POST /v1/audio/transcriptions`, а результат приходит в ответе.
## Транскрибация [#транскрибация]
Ниже представлен один и тот же запрос на разных языках программирования. Замените `nx-XXXXXXXXXXXXXXXXXXXXXXXX` на свой [API-ключ](/authentication), а `https://example.com/audio.mp3` — на прямую ссылку к вашему файлу.
Официальная библиотека [`nexara`](/python-sdk) (`pip install nexara`):
```python
from nexara import Nexara
client = Nexara() # ключ из переменной окружения NEXARA_API_KEY
result = client.transcriptions.create(url="https://example.com/audio.mp3")
print(result.text)
```
Официальная библиотека [`nexara-sdk`](/typescript-sdk) (`npm install nexara-sdk`):
```typescript
import { Nexara } from "nexara-sdk";
const client = new Nexara(); // ключ из переменной окружения NEXARA_API_KEY
const result = await client.transcriptions.create({ url: "https://example.com/audio.mp3" });
console.log(result.text);
```
```bash
curl https://api.nexara.ru/v1/audio/transcriptions \
-H "Authorization: Bearer nx-XXXXXXXXXXXXXXXXXXXXXXXX" \
-F "url=https://example.com/audio.mp3" \
-F "model=whisper-1"
```
```python
import requests
response = requests.post(
"https://api.nexara.ru/v1/audio/transcriptions",
headers={"Authorization": "Bearer nx-XXXXXXXXXXXXXXXXXXXXXXXX"},
data={
"url": "https://example.com/audio.mp3",
"model": "whisper-1",
},
)
print(response.json()["text"])
```
Node.js 18+:
```javascript
const form = new FormData();
form.append("url", "https://example.com/audio.mp3");
form.append("model", "whisper-1");
const response = await fetch(
"https://api.nexara.ru/v1/audio/transcriptions",
{
method: "POST",
headers: { Authorization: "Bearer nx-XXXXXXXXXXXXXXXXXXXXXXXX" },
body: form,
},
);
const result = await response.json();
console.log(result.text);
```
```go
package main
import (
"fmt"
"io"
"net/http"
"net/url"
"strings"
)
func main() {
form := url.Values{}
form.Set("url", "https://example.com/audio.mp3")
form.Set("model", "whisper-1")
req, _ := http.NewRequest("POST", "https://api.nexara.ru/v1/audio/transcriptions", strings.NewReader(form.Encode()))
req.Header.Set("Authorization", "Bearer nx-XXXXXXXXXXXXXXXXXXXXXXXX")
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
fmt.Println(string(body))
}
```
```ruby
require "net/http"
require "uri"
require "json"
uri = URI("https://api.nexara.ru/v1/audio/transcriptions")
request = Net::HTTP::Post.new(uri)
request["Authorization"] = "Bearer nx-XXXXXXXXXXXXXXXXXXXXXXXX"
request.set_form_data(
"url" => "https://example.com/audio.mp3",
"model" => "whisper-1",
)
response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
http.request(request)
end
puts JSON.parse(response.body)["text"]
```
## Результат [#результат]
По умолчанию (`response_format=json`) в ответе возвращается распознанный текст:
```json
{
"text": "Привет! Это пример транскрибации."
}
```
Формат ответа можно менять параметром `response_format` — `json` (по
умолчанию), `text`, `verbose_json`, `srt` или `vtt`. Подробнее — на странице
[форматы ответа](/response-formats).
## Что учитывать [#что-учитывать]
* **Ссылка должна быть публичной.** По ссылке должен скачиваться сам аудиофайл, без авторизации и редиректов на страницу. Если ссылка недоступна или отдаётся слишком медленно, запрос завершится ошибкой. В качестве ссылок лучше всего подходит pre-signed ссылка на S3 бакет. **Ссылки на YouTube, TikTok, VK и другие соцсети не поддерживаются.**
* **Форматы файлов.** Принимается большинство существующих аудио- и видеоформатов. Полный список — на странице [поддерживаемых файлов](/supported-files).
* **Размер файла.** Максимальный размер одного файла — **3 ГБ**. Подробнее — на странице [лимитов](/limits).
* **Частота запросов.** Синхронный эндпоинт ограничен **10 запросами в секунду**. Подробнее читайте здесь — [Лимиты](/limits#%D1%87%D0%B0%D1%81%D1%82%D0%BE%D1%82%D0%B0-%D0%B7%D0%B0%D0%BF%D1%80%D0%BE%D1%81%D0%BE%D0%B2).
* **Хранение ключа.** Не вставляйте API-ключ прямо в код. Храните его в переменной окружения (например, `export NEXARA_API_KEY="nx-..."`) и ссылайтесь на неё. Подробнее — на странице [аутентификации](/authentication#curl).
Для длинных записей вместо синхронного запроса используйте [асинхронный
режим](/async-transcription).
# Поддерживаемые языки (/languages)
Nexara API поддерживает автоматическое определение языка и транскрибацию для широкого спектра языков. Вы также можете явно указать язык, используя параметр `language` в ваших API-запросах. Для получения дополнительной информации обратитесь к [документации API](/api-reference/create_transcription_audio_transcriptions_post).
Ниже приведен полный список поддерживаемых языков с их кодами.
## Полный список поддерживаемых языков [#полный-список-поддерживаемых-языков]
| Код языка | Название языка |
| --------- | --------------------- |
| af | африкаанс |
| am | амхарский |
| ar | арабский |
| as | ассамский |
| az | азербайджанский |
| ba | башкирский |
| be | белорусский |
| bg | болгарский |
| bn | бенгальский |
| bo | тибетский |
| br | бретонский |
| bs | боснийский |
| ca | каталанский |
| cs | чешский |
| cy | валлийский |
| da | датский |
| de | немецкий |
| el | греческий |
| en | английский |
| es | испанский |
| et | эстонский |
| eu | баскский |
| fa | персидский |
| fi | финский |
| fo | фарерский |
| fr | французский |
| gl | галисийский |
| gu | гуджарати |
| ha | хауса |
| haw | гавайский |
| he | иврит |
| hi | хинди |
| hr | хорватский |
| ht | гаитянский креольский |
| hu | венгерский |
| hy | армянский |
| id | индонезийский |
| is | исландский |
| it | итальянский |
| ja | японский |
| jw | яванский |
| ka | грузинский |
| kk | казахский |
| km | кхмерский |
| kn | каннада |
| ko | корейский |
| la | латинский |
| lb | люксембургский |
| ln | лингала |
| lo | лаосский |
| lt | литовский |
| lv | латышский |
| mg | малагасийский |
| mi | маори |
| mk | македонский |
| ml | малаялам |
| mn | монгольский |
| mr | маратхи |
| ms | малайский |
| mt | мальтийский |
| my | бирманский |
| ne | непальский |
| nl | нидерландский |
| nn | нюнорск |
| no | норвежский |
| oc | окситанский |
| pa | панджаби |
| pl | польский |
| ps | пушту |
| pt | португальский |
| ro | румынский |
| ru | русский |
| sa | санскрит |
| sd | синдхи |
| si | сингальский |
| sk | словацкий |
| sl | словенский |
| sn | шона |
| so | сомалийский |
| sq | албанский |
| sr | сербский |
| su | сунданский |
| sv | шведский |
| sw | суахили |
| ta | тамильский |
| te | телугу |
| tg | таджикский |
| th | тайский |
| tk | туркменский |
| tl | тагальский |
| tr | турецкий |
| tt | татарский |
| uk | украинский |
| ur | урду |
| uz | узбекский |
| vi | вьетнамский |
| yi | идиш |
| yo | йоруба |
| yue | кантонский |
| zh | китайский |
Используйте эти коды языков, когда вам нужно явно указать язык для транскрибации с помощью параметра `language`. Если параметр не указан, API попытается определить язык автоматически.
# Лимиты (/limits)
На этой странице собраны все ограничения Nexara API. Их стоит учитывать при интеграции, чтобы запросы не отклонялись.
## Файлы и аудио [#файлы-и-аудио]
Файл больше 3 ГБ будет отклонён с ошибкой `400`. Ограничение действует как на загрузку файлом, так и на обработку по ссылке через параметр `url`.
Принимаются только аудио- и видеоформаты из списка на странице [Поддерживаемые файлы](/supported-files). Неподдерживаемый формат
вернёт ошибку `415`.
Аудио короче 0.3 секунды отклоняется с ошибкой `400`.
Для задачи `task=diarize` минимальная длительность аудио составляет 3
секунды.
## Частота запросов [#частота-запросов]
Эндпоинты транскрибации (`/audio/transcriptions` и
`/audio/transcriptions/async`) ограничены частотой **10 запросов в
секунду**. При превышении возвращается ошибка `429`.
Одновременно может выполняться не более **200** асинхронных задач
(`IN_PROGRESS`) на один API-ключ. Дождитесь завершения текущих задач перед
отправкой новых, иначе вам вернется ошибка `429`.
Если у Вас большие объемы аудио и Вам нужны увеличенные лимиты на частоту запросов или на максимальный размер файла, напишите нам в [поддержку](https://t.me/RND_RandoM).
## Аккаунт и ключи [#аккаунт-и-ключи]
Один аккаунт может иметь максимум **50** активных API-ключей. Попытка
создать больше вернёт ошибку `400`. Удалите неиспользуемые ключи в [личном
кабинете](https://app.nexara.ru).
Минимальная сумма пополнения баланса составляет 200 рублей.
## Тегирование ролей [#тегирование-ролей]
Ограничения параметра `roles` (доступен только с диаризацией, `task=diarize`):
В одном запросе можно задать не более **10** ролей.
Каждое название роли должно быть непустой строкой длиной не более **64** символов.
Описание роли (при передаче объектом `{роль: описание}`) не должно превышать **500** символов.
При превышении любого из лимитов API возвращает соответствующий код ошибки с
описанием причины в поле `detail`. Подробнее — на странице [ошибок](/errors).
# LLM анализ (/llm-usage)
Помимо транскрибации, Nexara API может сразу проанализировать её языковой моделью. Вы передаёте инструкцию в параметре `prompt`, и модель выполняет её над текстом расшифровки — например, делает краткое резюме, выделяет ключевые пункты, определяет тему или извлекает нужные поля.
Анализ включается, как только вы передаёте `prompt`. Возможны два режима:
Только `prompt` — модель возвращает ответ в свободной форме (текстом).
`prompt` + `json_schema` — модель возвращает JSON строго по заданной вами
схеме.
Когда включён анализ, ответ содержит и расшифровку, и результат работы модели:
```json
{
"transcription": { "task": "transcribe", "text": "...", "segments": [ ... ] },
"llm_output": "..."
}
```
Поле `transcription` — это полная расшифровка (в формате `verbose_json`), а `llm_output` — результат анализа: строка в свободном режиме или JSON-объект при использовании схемы.
`json_schema` учитывается только вместе с `prompt`. Отдельно, без `prompt`,
анализ не запускается.
На длинном аудио LLM-обработка может не уложиться в лимит времени
синхронного запроса — тогда `/v1/audio/transcriptions` вернёт ошибку
[`413`](/errors): транскрибация готова, но анализ не завершился. Повторять
запрос синхронно бесполезно — используйте [асинхронный режим](/async-transcription),
где на шаг LLM отводится значительно больший тайм-аут.
## Свободный анализ [#свободный-анализ]
Передайте `prompt` с инструкцией. Замените `nx-XXXXXXXXXXXXXXXXXXXXXXXX` на свой [API-ключ](/authentication), а `call.mp3` — на путь к вашему файлу.
Официальная библиотека [`nexara`](/python-sdk) (`pip install nexara`):
```python
from nexara import Nexara
client = Nexara() # ключ из переменной окружения NEXARA_API_KEY
result = client.transcriptions.create(
file="call.mp3",
prompt="Сделай краткое резюме разговора в трёх предложениях.",
)
print(result.llm_output) # ответ модели
print(result.transcription.text) # расшифровка, из которой он получен
```
Официальная библиотека [`nexara-sdk`](/typescript-sdk) (`npm install nexara-sdk`):
```typescript
import { Nexara } from "nexara-sdk";
const client = new Nexara(); // ключ из переменной окружения NEXARA_API_KEY
const result = await client.transcriptions.create({
file: "call.mp3",
prompt: "Сделай краткое резюме разговора в трёх предложениях.",
});
console.log(result.llm_output); // ответ модели
console.log(result.transcription.text); // расшифровка, из которой он получен
```
```bash
curl https://api.nexara.ru/v1/audio/transcriptions \
-H "Authorization: Bearer nx-XXXXXXXXXXXXXXXXXXXXXXXX" \
-F "file=@call.mp3" \
-F "model=whisper-1" \
-F "prompt=Сделай краткое резюме разговора в трёх предложениях."
```
```python
import requests
with open("call.mp3", "rb") as audio_file:
response = requests.post(
"https://api.nexara.ru/v1/audio/transcriptions",
headers={"Authorization": "Bearer nx-XXXXXXXXXXXXXXXXXXXXXXXX"},
files={"file": audio_file},
data={
"model": "whisper-1",
"prompt": "Сделай краткое резюме разговора в трёх предложениях.",
},
)
print(response.json()["llm_output"])
```
Node.js 18+:
```javascript
import fs from "fs";
const form = new FormData();
form.append("file", new Blob([fs.readFileSync("call.mp3")]), "call.mp3");
form.append("model", "whisper-1");
form.append("prompt", "Сделай краткое резюме разговора в трёх предложениях.");
const response = await fetch(
"https://api.nexara.ru/v1/audio/transcriptions",
{
method: "POST",
headers: { Authorization: "Bearer nx-XXXXXXXXXXXXXXXXXXXXXXXX" },
body: form,
},
);
const result = await response.json();
console.log(result.llm_output);
```
```go
package main
import (
"bytes"
"fmt"
"io"
"mime/multipart"
"net/http"
"os"
)
func main() {
file, _ := os.Open("call.mp3")
defer file.Close()
var buf bytes.Buffer
writer := multipart.NewWriter(&buf)
part, _ := writer.CreateFormFile("file", "call.mp3")
io.Copy(part, file)
writer.WriteField("model", "whisper-1")
writer.WriteField("prompt", "Сделай краткое резюме разговора в трёх предложениях.")
writer.Close()
req, _ := http.NewRequest("POST", "https://api.nexara.ru/v1/audio/transcriptions", &buf)
req.Header.Set("Authorization", "Bearer nx-XXXXXXXXXXXXXXXXXXXXXXXX")
req.Header.Set("Content-Type", writer.FormDataContentType())
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
fmt.Println(string(body))
}
```
```ruby
require "net/http"
require "uri"
require "json"
uri = URI("https://api.nexara.ru/v1/audio/transcriptions")
request = Net::HTTP::Post.new(uri)
request["Authorization"] = "Bearer nx-XXXXXXXXXXXXXXXXXXXXXXXX"
request.set_form(
[
["file", File.open("call.mp3")],
["model", "whisper-1"],
["prompt", "Сделай краткое резюме разговора в трёх предложениях."],
],
"multipart/form-data",
)
response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
http.request(request)
end
puts JSON.parse(response.body)["llm_output"]
```
В свободном режиме `llm_output` — это строка с ответом модели:
```json
{
"transcription": { "task": "transcribe", "text": "...", "segments": [ ... ] },
"llm_output": "Клиент обратился с вопросом о возврате товара. Оператор объяснил условия и оформил заявку. Стороны договорились о звонке на следующий день."
}
```
## Структурированный вывод [#структурированный-вывод]
Чтобы получить не текст, а данные с предсказуемой структурой, добавьте `json_schema` — [JSON Schema](https://json-schema.org/), описывающую форму ответа. Модель вернёт JSON, который строго ей соответствует, — такой ответ удобно сразу разбирать в коде без парсинга свободного текста.
### Как устроена схема [#как-устроена-схема]
`json_schema` — это обычная JSON Schema (черновик 2020-12). Обычно это объект (`"type": "object"`) с описанием полей в `properties`. Основные возможности:
* **Типы полей** — `string`, `number`, `integer`, `boolean`, `array`, `object`.
* **Обязательные поля** — перечислите их в массиве `required`, чтобы модель всегда возвращала эти ключи.
* **Фиксированный набор значений** — `enum` ограничивает поле списком допустимых вариантов (удобно для категорий и тональности).
* **Списки** — `"type": "array"` с `items`, описывающим тип элементов.
* **Вложенные объекты** — поле само может быть `"type": "object"` со своими `properties`.
Например, схема для анализа звонка в поддержку:
```json
{
"type": "object",
"properties": {
"topic": {
"type": "string",
"description": "Краткая тема обращения"
},
"sentiment": {
"type": "string",
"enum": ["положительная", "нейтральная", "отрицательная"]
},
"summary": {
"type": "string",
"description": "Резюме разговора в одном-двух предложениях"
},
"action_items": {
"type": "array",
"items": { "type": "string" }
}
},
"required": ["topic", "sentiment", "summary", "action_items"]
}
```
Схема проверяется при приёме запроса. Если `json_schema` — не корректная JSON
Schema или пустой объект, запрос отклоняется с ошибкой `400` ещё до
распознавания. Используйте `description` у полей, чтобы точнее подсказать
модели, что в них ожидается.
### Запрос [#запрос]
`json_schema` передаётся строкой с JSON. В `prompt` опишите задачу, а форму ответа задаст схема.
В SDK схема передаётся обычным словарём Python — без `json.dumps`. Поле `llm_output` возвращается уже разобранным объектом.
```python
from nexara import Nexara
client = Nexara() # ключ из переменной окружения NEXARA_API_KEY
result = client.transcriptions.create(
file="call.mp3",
prompt="Проанализируй разговор в поддержке.",
json_schema={
"type": "object",
"properties": {
"topic": {"type": "string"},
"sentiment": {
"type": "string",
"enum": ["положительная", "нейтральная", "отрицательная"],
},
"summary": {"type": "string"},
"action_items": {"type": "array", "items": {"type": "string"}},
},
"required": ["topic", "sentiment", "summary", "action_items"],
},
)
print(result.llm_output["summary"]) # dict по вашей схеме
```
В SDK схема передаётся обычным объектом — без `JSON.stringify`. Поле `llm_output` возвращается уже разобранным.
```typescript
import { Nexara } from "nexara-sdk";
const client = new Nexara(); // ключ из переменной окружения NEXARA_API_KEY
const result = await client.transcriptions.create({
file: "call.mp3",
prompt: "Проанализируй разговор в поддержке.",
json_schema: {
type: "object",
properties: {
topic: { type: "string" },
sentiment: {
type: "string",
enum: ["положительная", "нейтральная", "отрицательная"],
},
summary: { type: "string" },
action_items: { type: "array", items: { type: "string" } },
},
required: ["topic", "sentiment", "summary", "action_items"],
},
});
console.log(result.llm_output); // объект по вашей схеме
```
```bash
curl https://api.nexara.ru/v1/audio/transcriptions \
-H "Authorization: Bearer nx-XXXXXXXXXXXXXXXXXXXXXXXX" \
-F "file=@call.mp3" \
-F "model=whisper-1" \
-F "prompt=Проанализируй разговор в поддержке." \
-F 'json_schema={"type":"object","properties":{"topic":{"type":"string"},"sentiment":{"type":"string","enum":["положительная","нейтральная","отрицательная"]},"summary":{"type":"string"},"action_items":{"type":"array","items":{"type":"string"}}},"required":["topic","sentiment","summary","action_items"]}'
```
```python
import json
import requests
schema = {
"type": "object",
"properties": {
"topic": {"type": "string"},
"sentiment": {
"type": "string",
"enum": ["положительная", "нейтральная", "отрицательная"],
},
"summary": {"type": "string"},
"action_items": {"type": "array", "items": {"type": "string"}},
},
"required": ["topic", "sentiment", "summary", "action_items"],
}
with open("call.mp3", "rb") as audio_file:
response = requests.post(
"https://api.nexara.ru/v1/audio/transcriptions",
headers={"Authorization": "Bearer nx-XXXXXXXXXXXXXXXXXXXXXXXX"},
files={"file": audio_file},
data={
"model": "whisper-1",
"prompt": "Проанализируй разговор в поддержке.",
"json_schema": json.dumps(schema),
},
)
print(response.json()["llm_output"])
```
Node.js 18+:
```javascript
import fs from "fs";
const schema = {
type: "object",
properties: {
topic: { type: "string" },
sentiment: {
type: "string",
enum: ["положительная", "нейтральная", "отрицательная"],
},
summary: { type: "string" },
action_items: { type: "array", items: { type: "string" } },
},
required: ["topic", "sentiment", "summary", "action_items"],
};
const form = new FormData();
form.append("file", new Blob([fs.readFileSync("call.mp3")]), "call.mp3");
form.append("model", "whisper-1");
form.append("prompt", "Проанализируй разговор в поддержке.");
form.append("json_schema", JSON.stringify(schema));
const response = await fetch(
"https://api.nexara.ru/v1/audio/transcriptions",
{
method: "POST",
headers: { Authorization: "Bearer nx-XXXXXXXXXXXXXXXXXXXXXXXX" },
body: form,
},
);
const result = await response.json();
console.log(result.llm_output);
```
```go
package main
import (
"bytes"
"fmt"
"io"
"mime/multipart"
"net/http"
"os"
)
func main() {
schema := `{"type":"object","properties":{"topic":{"type":"string"},"sentiment":{"type":"string","enum":["положительная","нейтральная","отрицательная"]},"summary":{"type":"string"},"action_items":{"type":"array","items":{"type":"string"}}},"required":["topic","sentiment","summary","action_items"]}`
file, _ := os.Open("call.mp3")
defer file.Close()
var buf bytes.Buffer
writer := multipart.NewWriter(&buf)
part, _ := writer.CreateFormFile("file", "call.mp3")
io.Copy(part, file)
writer.WriteField("model", "whisper-1")
writer.WriteField("prompt", "Проанализируй разговор в поддержке.")
writer.WriteField("json_schema", schema)
writer.Close()
req, _ := http.NewRequest("POST", "https://api.nexara.ru/v1/audio/transcriptions", &buf)
req.Header.Set("Authorization", "Bearer nx-XXXXXXXXXXXXXXXXXXXXXXXX")
req.Header.Set("Content-Type", writer.FormDataContentType())
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
fmt.Println(string(body))
}
```
```ruby
require "net/http"
require "uri"
require "json"
schema = {
"type" => "object",
"properties" => {
"topic" => { "type" => "string" },
"sentiment" => {
"type" => "string",
"enum" => ["положительная", "нейтральная", "отрицательная"],
},
"summary" => { "type" => "string" },
"action_items" => { "type" => "array", "items" => { "type" => "string" } },
},
"required" => ["topic", "sentiment", "summary", "action_items"],
}
uri = URI("https://api.nexara.ru/v1/audio/transcriptions")
request = Net::HTTP::Post.new(uri)
request["Authorization"] = "Bearer nx-XXXXXXXXXXXXXXXXXXXXXXXX"
request.set_form(
[
["file", File.open("call.mp3")],
["model", "whisper-1"],
["prompt", "Проанализируй разговор в поддержке."],
["json_schema", schema.to_json],
],
"multipart/form-data",
)
response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
http.request(request)
end
puts JSON.parse(response.body)["llm_output"]
```
Теперь `llm_output` — это JSON-объект, соответствующий вашей схеме:
```json
{
"transcription": { "task": "transcribe", "text": "...", "segments": [ ... ] },
"llm_output": {
"topic": "Возврат товара",
"sentiment": "нейтральная",
"summary": "Клиент попросил оформить возврат, оператор объяснил условия и создал заявку.",
"action_items": [
"Отправить клиенту инструкцию по возврату",
"Перезвонить на следующий день"
]
}
}
```
LLM анализ тарифицируется дополнительно к стоимости распознавания.
Стоимость услуги смотрите на странице [тарифов](/pricing).
# Локальный файл (/local-file)
Самый частый сценарий — отправка аудиофайла с диска и получение его расшифровки. Файл передаётся напрямую в теле запроса `multipart/form-data` на эндпоинт `POST /v1/audio/transcriptions`, а результат приходит в ответе.
## Транскрибация [#транскрибация]
Ниже представлен один и тот же запрос на разных языках программирования. Замените `nx-XXXXXXXXXXXXXXXXXXXXXXXX` на свой [API-ключ](/authentication), а `audio.mp3` — на путь к вашему файлу.
Официальная библиотека [`nexara`](/python-sdk) (`pip install nexara`). Файл, переданный путём, стримится с диска и не загружается в память целиком.
```python
from nexara import Nexara
client = Nexara() # ключ из переменной окружения NEXARA_API_KEY
result = client.transcriptions.create(file="audio.mp3")
print(result.text)
```
Официальная библиотека [`nexara-sdk`](/typescript-sdk) (`npm install nexara-sdk`). Файл, переданный путём, стримится с диска и не загружается в память целиком.
```typescript
import { Nexara } from "nexara-sdk";
const client = new Nexara(); // ключ из переменной окружения NEXARA_API_KEY
const result = await client.transcriptions.create({ file: "audio.mp3" });
console.log(result.text);
```
```bash
curl https://api.nexara.ru/v1/audio/transcriptions \
-H "Authorization: Bearer nx-XXXXXXXXXXXXXXXXXXXXXXXX" \
-F "file=@audio.mp3" \
-F "model=whisper-1"
```
```python
import requests
with open("audio.mp3", "rb") as audio_file:
response = requests.post(
"https://api.nexara.ru/v1/audio/transcriptions",
headers={"Authorization": "Bearer nx-XXXXXXXXXXXXXXXXXXXXXXXX"},
files={"file": audio_file},
data={"model": "whisper-1"},
)
print(response.json()["text"])
```
Nexara API совместим с OpenAI SDK — достаточно указать `base_url`. Обратите внимание: `Bearer` к ключу добавлять не нужно.
```python
from openai import OpenAI
client = OpenAI(
api_key="nx-XXXXXXXXXXXXXXXXXXXXXXXX",
base_url="https://api.nexara.ru/v1",
)
with open("audio.mp3", "rb") as audio_file:
transcript = client.audio.transcriptions.create(
model="whisper-1",
file=audio_file,
)
print(transcript.text)
```
Node.js 18+:
```javascript
import fs from "fs";
const form = new FormData();
form.append("file", new Blob([fs.readFileSync("audio.mp3")]), "audio.mp3");
form.append("model", "whisper-1");
const response = await fetch(
"https://api.nexara.ru/v1/audio/transcriptions",
{
method: "POST",
headers: { Authorization: "Bearer nx-XXXXXXXXXXXXXXXXXXXXXXXX" },
body: form,
},
);
const result = await response.json();
console.log(result.text);
```
```go
package main
import (
"bytes"
"fmt"
"io"
"mime/multipart"
"net/http"
"os"
)
func main() {
file, _ := os.Open("audio.mp3")
defer file.Close()
var buf bytes.Buffer
writer := multipart.NewWriter(&buf)
part, _ := writer.CreateFormFile("file", "audio.mp3")
io.Copy(part, file)
writer.WriteField("model", "whisper-1")
writer.Close()
req, _ := http.NewRequest("POST", "https://api.nexara.ru/v1/audio/transcriptions", &buf)
req.Header.Set("Authorization", "Bearer nx-XXXXXXXXXXXXXXXXXXXXXXXX")
req.Header.Set("Content-Type", writer.FormDataContentType())
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
fmt.Println(string(body))
}
```
```ruby
require "net/http"
require "uri"
require "json"
uri = URI("https://api.nexara.ru/v1/audio/transcriptions")
request = Net::HTTP::Post.new(uri)
request["Authorization"] = "Bearer nx-XXXXXXXXXXXXXXXXXXXXXXXX"
request.set_form(
[
["file", File.open("audio.mp3")],
["model", "whisper-1"],
],
"multipart/form-data",
)
response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
http.request(request)
end
puts JSON.parse(response.body)["text"]
```
## Результат [#результат]
По умолчанию (`response_format=json`) в ответе возвращается распознанный текст:
```json
{
"text": "Привет! Это пример транскрибации."
}
```
Формат ответа можно менять параметром `response_format` — `json` (по
умолчанию), `text`, `verbose_json`, `srt` или `vtt`. Подробнее — на странице
[форматы ответа](/response-formats).
## Что учитывать [#что-учитывать]
* **Форматы файлов.** Принимается большинство существующих аудио- и видеоформатов. Полный список — на странице [поддерживаемых файлов](/supported-files).
* **Размер файла.** Максимальный размер одного файла — **3 ГБ**. Подробнее — на странице [лимитов](/limits).
* **Частота запросов.** Синхронный эндпоинт ограничен **10 запросами в секунду**. Подробнее читайте здесь — [Лимиты](/limits#%D1%87%D0%B0%D1%81%D1%82%D0%BE%D1%82%D0%B0-%D0%B7%D0%B0%D0%BF%D1%80%D0%BE%D1%81%D0%BE%D0%B2).
* **Хранение ключа.** Не вставляйте API-ключ прямо в код. Храните его в переменной окружения (например, `export NEXARA_API_KEY="nx-..."`) и ссылайтесь на неё. Подробнее — на странице [аутентификации](/authentication#curl).
Для длинных записей вместо синхронного запроса используйте [асинхронный
режим](/async-transcription).
# Ключевые пункты встречи (/meeting-highlights)
# Модели (/models)
Nexara предлагает две модели распознавания речи.
Модель указывается в параметре `model` при запросе. По умолчанию используется `whisper-1`.
Мультиязычная и устойчивая модель для большинства сценариев. Подходит,
когда аудио может быть на разных языках или когда нужен универсальный
вариант «по умолчанию».
Оптимизирована под русскую речь — телефонию и сложные записи (шум,
удалённый микрофон, плохое качество связи). Выбирайте её для русскоязычных
звонков и «далёких» записей.
## Какую модель выбрать [#какую-модель-выбрать]
* `whisper-1` — мультиязычные записи, подкасты, встречи, диктовка, любой сценарий «по умолчанию».
* `nexara-ru` — русскоязычная телефония, колл-центры, записи с плохим качеством связи и удалённым микрофоном.
## Как указать модель [#как-указать-модель]
Передайте нужный идентификатор в параметре `model`:
```python
from nexara import Nexara
client = Nexara() # ключ из переменной окружения NEXARA_API_KEY
result = client.transcriptions.create(
file="audio/example.mp3",
model="nexara-ru", # или "whisper-1"
)
print(result.text)
```
```typescript
import { Nexara } from "nexara-sdk";
const client = new Nexara(); // ключ из переменной окружения NEXARA_API_KEY
const result = await client.transcriptions.create({
file: "audio/example.mp3",
model: "nexara-ru", // или "whisper-1"
});
console.log(result.text);
```
```python
import requests
url = "https://api.nexara.ru/v1/audio/transcriptions"
api_key = "NEXARA_API_KEY"
with open("audio/example.mp3", "rb") as audio_file:
files = {"file": ("example.mp3", audio_file, "audio/mp3")}
data = {
"model": "nexara-ru", # или "whisper-1"
"response_format": "json",
}
response = requests.post(
url,
headers={"Authorization": f"Bearer {api_key}"},
files=files,
data=data,
)
print(response.json())
```
```bash
curl --request POST \
--url https://api.nexara.ru/v1/audio/transcriptions \
--header 'Authorization: Bearer NEXARA_API_KEY' \
--header 'Content-Type: multipart/form-data' \
--form model="nexara-ru" \
--form response_format="json" \
--form file="@example.mp3"
```
Стоимость дополнений (диаризация, определение ролей, LLM-обработка) не зависит от выбранной модели. См. [Тарифы](/pricing).
# Мультиканальное аудио (/multichannel)
# Гайд по n8n (/n8n)
[n8n](https://n8n.io) позволяет вызвать Nexara API без единой строки кода. Есть два способа:
* **Community-нода Nexara** (рекомендуется) — отдельный узел с готовыми операциями транскрибации и диаризации.
* **Узел HTTP Request** — ручная настройка запроса, если community-ноды недоступны.
## Способ 1: Community-нода Nexara (рекомендуется) [#способ-1-community-нода-nexara-рекомендуется]
Отдельный узел [`n8n-nodes-nexara`](https://www.npmjs.com/package/n8n-nodes-nexara) избавляет от ручной настройки запроса: транскрибация, диаризация, роли говорящих и структурированный вывод через LLM доступны как обычные операции узла.
### Установите ноду [#установите-ноду]
В n8n откройте **Settings → Community Nodes → Install** и введите:
```text
n8n-nodes-nexara
```
Подробнее — в [документации n8n по community-нодам](https://docs.n8n.io/integrations/community-nodes/installation/).
### Добавьте учётные данные (Credentials) [#добавьте-учётные-данные-credentials]
Создайте в [панели Nexara](https://nexara.ru) API-ключ. Затем в n8n добавьте учётные данные типа **Nexara API** и вставьте ключ — он будет отправляться как `Bearer`-токен в каждом запросе.
У учётных данных нет кнопки «Test»: в API нет отдельного health-эндпоинта. Чтобы проверить ключ, запустите операцию **Transcribe** на коротком аудио.
### Выберите операцию [#выберите-операцию]
Добавьте узел **Nexara** и выберите операцию:
| Операция | Что делает |
| -------------- | ----------------------------------------------------------------------------------------------------------- |
| **Transcribe** | Транскрибирует аудио в текст и дожидается результата. |
| **Diarize** | Транскрибирует и размечает говорящих, с опциональными ролями. |
| **Create Job** | Отправляет длинное аудио на отложенную обработку. Возвращает задачу сразу либо опрашивает её до завершения. |
| **Get Job** | Получает отложенную задачу по её ID. |
Аудио передаётся либо как **бинарный файл** из предыдущего узла, либо как **URL** — сервер скачает файл сам.
### Настройте параметры [#настройте-параметры]
Основные опции узла:
* **Response Format** — `JSON`, `Verbose JSON`, `Text`, `SRT` или `VTT`.
* **Language** — код [ISO-639-1](/languages) (например, `ru`, `en`) или пусто для автоопределения.
* **LLM Prompt** / **LLM JSON Schema** — прогнать расшифровку через [LLM](/llm-usage) и, при необходимости, получить строго структурированный JSON. Промпт автоматически включает `Verbose JSON`.
* **Timestamp Granularity**, **Profanity Filter**, **Dictionary**, **Model**.
* Для диаризации: **Number of Speakers**, **Diarization Setting** и **Roles** — `auto`, JSON-массив вида `["client","agent"]` или JSON-объект «метка → описание».
### Примеры [#примеры]
**Транскрибация загруженного файла**
1. Узел, отдающий бинарное аудио (например, *HTTP Request* или *Read Binary File*).
2. **Nexara → Transcribe**, Input Type — *Binary File*, Input Binary Field — `data`.
**Диаризация звонка с ролями**
1. **Nexara → Diarize**.
2. В *Diarization Options* задайте **Roles** — `["client","agent"]`.
**Длинное аудио (отложенная обработка)**
1. **Nexara → Create Job** с включённым **Wait for Completion** — узел сам опрашивает задачу и возвращает результат. За неуспешную задачу оплата не списывается.
2. Либо оставьте опрос выключенным и позже вызовите **Get Job** с полученным Job ID (в другом воркфлоу или запуске). Результаты хранятся 12 часов после создания.
## Способ 2: Узел HTTP Request (вручную) [#способ-2-узел-http-request-вручную]
Если community-ноды недоступны, Nexara API можно вызвать напрямую через встроенный узел **HTTP Request**. Ниже — пошаговая настройка транскрибации аудиофайла.
### Добавьте узел HTTP Request [#добавьте-узел-http-request]
Найдите и добавьте узел **HTTP Request** в ваш рабочий процесс n8n.
### Импортируйте команду cURL [#импортируйте-команду-curl]
Нажмите **Import cURL** в настройках узла HTTP Request и вставьте команду ниже.
```bash
curl --request POST \
--url https://api.nexara.ru/v1/audio/transcriptions \
--header 'Authorization: Bearer YOUR_NEXARA_API_KEY' \
--header 'Content-Type: multipart/form-data'
```
Не забудьте заменить `YOUR_NEXARA_API_KEY` на ваш реальный API-ключ Nexara.
### Настройте тело запроса (Body Parameters) [#настройте-тело-запроса-body-parameters]
* Прокрутите вниз до раздела **Body** в параметрах узла.
* Убедитесь, что **Body Content Type** установлен как `Form-Data`.
* В разделе **Body Parameters** нажмите **Add Parameter**.
* **Parameter Type** — `n8n Binary File`.
* **Name** — `file`.
* **Input Data Field Name** — `data`.
Поле `data` предполагает, что бинарный файл приходит из предыдущего узла (например, **Read Binary File**) и доступен под именем `data`. Если имя поля другое — укажите своё. А если у вас есть ссылка на файл, а не сам файл, передайте её напрямую через параметр `url`.
### Готово — при необходимости добавьте параметры [#готово--при-необходимости-добавьте-параметры]
Базовая настройка завершена: узел уже отправляет файл на транскрибацию.
Чтобы добавить другие параметры API (например, `language` или `response_format`), создайте новые параметры в **Body Parameters** — обычно с типом `String` в поле **Parameter Type**:
* Убедитесь, что перед узлом HTTP Request есть узел, который читает аудиофайл и делает его бинарные данные доступными (обычно под именем поля `data`). Либо передавайте ссылку на файл через параметр `url`.
* Чтобы выполнить диаризацию (разделение на говорящих), добавьте параметр `task` со значением `diarize`.
Подробнее о всех доступных параметрах API читайте в [Референсе API](/api-reference/create_transcription_audio_transcriptions_post).
# OpenAI SDK (/openai-sdk)
# Обзор (/overview)
# Телефонные звонки (/phone-call)
Телефонные звонки — самый частый сценарий для колл-центров и отделов продаж. Из одной записи можно получить сразу всё: расшифровку, разделение на оператора и клиента, эмоции говорящих и готовую аналитику звонка. Ниже разберём блок за блоком, а в конце соберём всё в один запрос и поделимся [лайфхаками](#лайфхаки).
## Разделение на говорящих [#разделение-на-говорящих]
Для звонков включите диаризацию (`task=diarize`) и передайте `num_speakers=2` — в разговоре обычно двое.
```python
from nexara import Nexara
client = Nexara() # ключ из переменной окружения NEXARA_API_KEY
result = client.transcriptions.create(
file="call.mp3",
task="diarize",
num_speakers=2,
)
```
```typescript
import { Nexara } from "nexara-sdk";
const client = new Nexara(); // ключ из переменной окружения NEXARA_API_KEY
const result = await client.transcriptions.create({
file: "call.mp3",
task: "diarize",
num_speakers: 2,
});
```
```bash
curl https://api.nexara.ru/v1/audio/transcriptions \
-H "Authorization: Bearer nx-XXXXXXXXXXXXXXXXXXXXXXXX" \
-F "file=@call.mp3" \
-F "model=whisper-1" \
-F "task=diarize" \
-F "num_speakers=2"
```
Если телефония умеет писать звонки в двухканальном (стерео) формате, где каждый
участник на своём канале, — используйте именно его. В связке с `num_speakers=2`
это даёт самое точное разделение.
## Роли вместо speaker\_0 и speaker\_1 [#роли-вместо-speaker_0-и-speaker_1]
Чтобы в расшифровке сразу были понятные роли, а не безликие `speaker_0`/`speaker_1`, передайте `roles` со своими метками. Для аналитики это гораздо удобнее.
```python
result = client.transcriptions.create(
file="call.mp3",
task="diarize",
num_speakers=2,
roles=["Оператор", "Клиент"],
)
```
```typescript
const result = await client.transcriptions.create({
file: "call.mp3",
task: "diarize",
num_speakers: 2,
roles: ["Оператор", "Клиент"],
});
```
```bash
curl https://api.nexara.ru/v1/audio/transcriptions \
-H "Authorization: Bearer nx-XXXXXXXXXXXXXXXXXXXXXXXX" \
-F "file=@call.mp3" \
-F "model=whisper-1" \
-F "task=diarize" \
-F "num_speakers=2" \
-F 'roles=["Оператор", "Клиент"]'
```
В сегментах вместо `speaker_N` появятся `Оператор` и `Клиент`. Подробнее — на странице [спикеры и роли](/speakers-roles).
## Русские звонки [#русские-звонки]
Если разговор на русском языке, используйте модель `nexara-ru` — она заточена под русскую речь и даёт более точную расшифровку, чем универсальная `whisper-1`.
```python
result = client.transcriptions.create(
file="call.mp3",
model="nexara-ru",
task="diarize",
num_speakers=2,
)
```
```typescript
const result = await client.transcriptions.create({
file: "call.mp3",
model: "nexara-ru",
task: "diarize",
num_speakers: 2,
});
```
```bash
curl https://api.nexara.ru/v1/audio/transcriptions \
-H "Authorization: Bearer nx-XXXXXXXXXXXXXXXXXXXXXXXX" \
-F "file=@call.mp3" \
-F "model=nexara-ru" \
-F "task=diarize" \
-F "num_speakers=2"
```
## Фильтрация мата [#фильтрация-мата]
В звонках нередко проскакивает нецензурная лексика. Чтобы замаскировать её в расшифровке (например, для отчётов и выгрузок), добавьте `profanity_filter=true`.
```python
result = client.transcriptions.create(
file="call.mp3",
model="nexara-ru",
task="diarize",
num_speakers=2,
profanity_filter=True,
)
```
```typescript
const result = await client.transcriptions.create({
file: "call.mp3",
model: "nexara-ru",
task: "diarize",
num_speakers: 2,
profanity_filter: true,
});
```
```bash
curl https://api.nexara.ru/v1/audio/transcriptions \
-H "Authorization: Bearer nx-XXXXXXXXXXXXXXXXXXXXXXXX" \
-F "file=@call.mp3" \
-F "model=nexara-ru" \
-F "task=diarize" \
-F "num_speakers=2" \
-F "profanity_filter=true"
```
## Эмоции в звонке [#эмоции-в-звонке]
Чтобы видеть не только *что* сказали, но и *как*, добавьте `emotions=true`. К каждому сегменту диаризации добавится объект `emotion` с меткой (`angry`, `sad`, `neutral`, `positive`) и уверенностью модели — сразу видно, на какой минуте клиент начал злиться.
Параметр работает только с моделью `nexara-ru` и диаризацией — то есть ровно в той конфигурации, которую мы собрали выше.
```python
result = client.transcriptions.create(
file="call.mp3",
model="nexara-ru",
task="diarize",
num_speakers=2,
roles=["Оператор", "Клиент"],
emotions=True,
)
for segment in result.segments:
if segment.emotion:
print(f"{segment.speaker} [{segment.emotion.label}]: {segment.text}")
else:
print(f"{segment.speaker}: {segment.text}")
```
```typescript
const result = await client.transcriptions.create({
file: "call.mp3",
model: "nexara-ru",
task: "diarize",
num_speakers: 2,
roles: ["Оператор", "Клиент"],
emotions: true,
});
for (const segment of result.segments) {
if (segment.emotion) {
console.log(`${segment.speaker} [${segment.emotion.label}]: ${segment.text}`);
} else {
console.log(`${segment.speaker}: ${segment.text}`);
}
}
```
```bash
curl https://api.nexara.ru/v1/audio/transcriptions \
-H "Authorization: Bearer nx-XXXXXXXXXXXXXXXXXXXXXXXX" \
-F "file=@call.mp3" \
-F "model=nexara-ru" \
-F "task=diarize" \
-F "num_speakers=2" \
-F 'roles=["Оператор", "Клиент"]' \
-F "emotions=true"
```
В сегментах появится эмоция рядом с ролью:
```json
{
"segments": [
{
"start": 0.0,
"end": 2.1,
"text": "Служба поддержки, здравствуйте.",
"speaker": "Оператор",
"emotion": { "label": "neutral", "confidence": 0.88 }
},
{
"start": 2.5,
"end": 6.4,
"text": "Я третий раз звоню по одному и тому же вопросу!",
"speaker": "Клиент",
"emotion": { "label": "angry", "confidence": 0.91 }
}
]
}
```
Эмоция приходит не для каждого сегмента — если модель не смогла оценить фрагмент, поля `emotion` в нём не будет, поэтому проверяйте его наличие. Подробнее — на странице [распознавание эмоций](/emotions).
Эмоции усиливают аналитику ниже. Если в том же запросе передан `prompt`,
языковая модель получает расшифровку уже с метками — реплики приходят к ней в
виде `Клиент [angry]: …`. Поэтому и `sentiment` в карточке звонка, и оценка
работы оператора опираются не только на слова, но и на тон голоса: вежливая по
тексту фраза, сказанная раздражённо, больше не выглядит нейтральной.
## Аналитика звонка [#аналитика-звонка]
Самое ценное — сразу получить из звонка структурированную аналитику. Для этого добавьте `prompt` (и, для строгой структуры, `json_schema`). Модель получает расшифровку с ролями — а если включены эмоции, то и с ними, — поэтому анализ учитывает, кто что сказал и с какой интонацией. Подробно про режимы — на странице [LLM-анализ](/llm-usage). Ниже — три самых частых сценария.
### Карточка звонка [#карточка-звонка]
Готовая карточка для записи в CRM: тема, тональность, резюме, решён ли вопрос и список задач.
```json
{
"type": "object",
"properties": {
"topic": { "type": "string", "description": "Причина обращения" },
"sentiment": {
"type": "string",
"enum": ["положительная", "нейтральная", "отрицательная"]
},
"summary": { "type": "string", "description": "Резюме разговора" },
"resolved": { "type": "boolean", "description": "Решён ли вопрос клиента" },
"action_items": { "type": "array", "items": { "type": "string" } }
},
"required": ["topic", "sentiment", "summary", "resolved", "action_items"]
}
```
Prompt: `Проанализируй телефонный разговор в поддержке и заполни карточку звонка.`
### Оценка качества (QA) [#оценка-качества-qa]
Проверка работы оператора по чек-листу для контроля качества.
```json
{
"type": "object",
"properties": {
"greeting": { "type": "boolean", "description": "Оператор поздоровался и представился" },
"polite": { "type": "boolean", "description": "Оператор был вежлив" },
"resolved": { "type": "boolean", "description": "Вопрос клиента решён" },
"score": { "type": "integer", "description": "Оценка звонка от 1 до 5" },
"comment": { "type": "string", "description": "Короткий комментарий к оценке" }
},
"required": ["greeting", "polite", "resolved", "score", "comment"]
}
```
Prompt: `Оцени работу оператора в этом звонке по чек-листу.`
### Извлечение данных в CRM [#извлечение-данных-в-crm]
Вытащить из разговора конкретные поля для автоматического заполнения карточки сделки.
```json
{
"type": "object",
"properties": {
"client_name": { "type": "string" },
"phone": { "type": "string" },
"product": { "type": "string", "description": "Обсуждаемый товар или услуга" },
"amount": { "type": "number", "description": "Сумма сделки, если названа" },
"next_step": { "type": "string", "description": "О чём договорились" }
},
"required": ["client_name", "product"]
}
```
Prompt: `Извлеки данные о клиенте и сделке из разговора.`
## Собираем всё вместе [#собираем-всё-вместе]
Один запрос для русского звонка: распознавание `nexara-ru`, диаризация с `num_speakers=2`, роли `Оператор`/`Клиент` и карточка звонка через `prompt` + `json_schema`.
В [Python SDK](/python-sdk) роли и схема передаются обычными списками и словарями — без ручной сериализации в JSON.
```python
from nexara import Nexara
client = Nexara() # ключ из переменной окружения NEXARA_API_KEY
result = client.transcriptions.create(
file="call.mp3",
model="nexara-ru",
task="diarize",
num_speakers=2,
roles=["Оператор", "Клиент"],
prompt="Проанализируй телефонный разговор в поддержке и заполни карточку звонка.",
json_schema={
"type": "object",
"properties": {
"topic": {"type": "string"},
"sentiment": {
"type": "string",
"enum": ["положительная", "нейтральная", "отрицательная"],
},
"summary": {"type": "string"},
"resolved": {"type": "boolean"},
"action_items": {"type": "array", "items": {"type": "string"}},
},
"required": ["topic", "sentiment", "summary", "resolved", "action_items"],
},
)
print(result.llm_output) # готовая карточка звонка (dict)
```
В [TypeScript SDK](/typescript-sdk) роли и схема передаются обычными массивами и объектами — без ручной сериализации в JSON.
```typescript
import { Nexara } from "nexara-sdk";
const client = new Nexara(); // ключ из переменной окружения NEXARA_API_KEY
const result = await client.transcriptions.create({
file: "call.mp3",
model: "nexara-ru",
task: "diarize",
num_speakers: 2,
roles: ["Оператор", "Клиент"],
prompt: "Проанализируй телефонный разговор в поддержке и заполни карточку звонка.",
json_schema: {
type: "object",
properties: {
topic: { type: "string" },
sentiment: {
type: "string",
enum: ["положительная", "нейтральная", "отрицательная"],
},
summary: { type: "string" },
resolved: { type: "boolean" },
action_items: { type: "array", items: { type: "string" } },
},
required: ["topic", "sentiment", "summary", "resolved", "action_items"],
},
});
console.log(result.llm_output); // готовая карточка звонка (объект)
```
```bash
curl https://api.nexara.ru/v1/audio/transcriptions \
-H "Authorization: Bearer nx-XXXXXXXXXXXXXXXXXXXXXXXX" \
-F "file=@call.mp3" \
-F "model=nexara-ru" \
-F "task=diarize" \
-F "num_speakers=2" \
-F 'roles=["Оператор", "Клиент"]' \
-F "prompt=Проанализируй телефонный разговор в поддержке и заполни карточку звонка." \
-F 'json_schema={"type":"object","properties":{"topic":{"type":"string"},"sentiment":{"type":"string","enum":["положительная","нейтральная","отрицательная"]},"summary":{"type":"string"},"resolved":{"type":"boolean"},"action_items":{"type":"array","items":{"type":"string"}}},"required":["topic","sentiment","summary","resolved","action_items"]}'
```
```python
import json
import requests
schema = {
"type": "object",
"properties": {
"topic": {"type": "string"},
"sentiment": {
"type": "string",
"enum": ["положительная", "нейтральная", "отрицательная"],
},
"summary": {"type": "string"},
"resolved": {"type": "boolean"},
"action_items": {"type": "array", "items": {"type": "string"}},
},
"required": ["topic", "sentiment", "summary", "resolved", "action_items"],
}
with open("call.mp3", "rb") as audio_file:
response = requests.post(
"https://api.nexara.ru/v1/audio/transcriptions",
headers={"Authorization": "Bearer nx-XXXXXXXXXXXXXXXXXXXXXXXX"},
files={"file": audio_file},
data={
"model": "nexara-ru",
"task": "diarize",
"num_speakers": "2",
"roles": json.dumps(["Оператор", "Клиент"]),
"prompt": "Проанализируй телефонный разговор в поддержке и заполни карточку звонка.",
"json_schema": json.dumps(schema),
},
)
result = response.json()
print(result["llm_output"])
```
Node.js 18+:
```javascript
import fs from "fs";
const schema = {
type: "object",
properties: {
topic: { type: "string" },
sentiment: {
type: "string",
enum: ["положительная", "нейтральная", "отрицательная"],
},
summary: { type: "string" },
resolved: { type: "boolean" },
action_items: { type: "array", items: { type: "string" } },
},
required: ["topic", "sentiment", "summary", "resolved", "action_items"],
};
const form = new FormData();
form.append("file", new Blob([fs.readFileSync("call.mp3")]), "call.mp3");
form.append("model", "nexara-ru");
form.append("task", "diarize");
form.append("num_speakers", "2");
form.append("roles", JSON.stringify(["Оператор", "Клиент"]));
form.append("prompt", "Проанализируй телефонный разговор в поддержке и заполни карточку звонка.");
form.append("json_schema", JSON.stringify(schema));
const response = await fetch(
"https://api.nexara.ru/v1/audio/transcriptions",
{
method: "POST",
headers: { Authorization: "Bearer nx-XXXXXXXXXXXXXXXXXXXXXXXX" },
body: form,
},
);
const result = await response.json();
console.log(result.llm_output);
```
```go
package main
import (
"bytes"
"fmt"
"io"
"mime/multipart"
"net/http"
"os"
)
func main() {
schema := `{"type":"object","properties":{"topic":{"type":"string"},"sentiment":{"type":"string","enum":["положительная","нейтральная","отрицательная"]},"summary":{"type":"string"},"resolved":{"type":"boolean"},"action_items":{"type":"array","items":{"type":"string"}}},"required":["topic","sentiment","summary","resolved","action_items"]}`
file, _ := os.Open("call.mp3")
defer file.Close()
var buf bytes.Buffer
writer := multipart.NewWriter(&buf)
part, _ := writer.CreateFormFile("file", "call.mp3")
io.Copy(part, file)
writer.WriteField("model", "nexara-ru")
writer.WriteField("task", "diarize")
writer.WriteField("num_speakers", "2")
writer.WriteField("roles", `["Оператор", "Клиент"]`)
writer.WriteField("prompt", "Проанализируй телефонный разговор в поддержке и заполни карточку звонка.")
writer.WriteField("json_schema", schema)
writer.Close()
req, _ := http.NewRequest("POST", "https://api.nexara.ru/v1/audio/transcriptions", &buf)
req.Header.Set("Authorization", "Bearer nx-XXXXXXXXXXXXXXXXXXXXXXXX")
req.Header.Set("Content-Type", writer.FormDataContentType())
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
fmt.Println(string(body))
}
```
```ruby
require "net/http"
require "uri"
require "json"
schema = {
"type" => "object",
"properties" => {
"topic" => { "type" => "string" },
"sentiment" => {
"type" => "string",
"enum" => ["положительная", "нейтральная", "отрицательная"],
},
"summary" => { "type" => "string" },
"resolved" => { "type" => "boolean" },
"action_items" => { "type" => "array", "items" => { "type" => "string" } },
},
"required" => ["topic", "sentiment", "summary", "resolved", "action_items"],
}
uri = URI("https://api.nexara.ru/v1/audio/transcriptions")
request = Net::HTTP::Post.new(uri)
request["Authorization"] = "Bearer nx-XXXXXXXXXXXXXXXXXXXXXXXX"
request.set_form(
[
["file", File.open("call.mp3")],
["model", "nexara-ru"],
["task", "diarize"],
["num_speakers", "2"],
["roles", ["Оператор", "Клиент"].to_json],
["prompt", "Проанализируй телефонный разговор в поддержке и заполни карточку звонка."],
["json_schema", schema.to_json],
],
"multipart/form-data",
)
response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
http.request(request)
end
puts JSON.parse(response.body)["llm_output"]
```
В ответе придёт расшифровка с ролями и готовая карточка звонка:
```json
{
"transcription": {
"task": "diarize",
"segments": [
{ "start": 0.0, "end": 2.1, "text": "Служба поддержки, здравствуйте.", "speaker": "Оператор" },
{ "start": 2.5, "end": 5.0, "text": "Здравствуйте, хочу оформить возврат.", "speaker": "Клиент" }
]
},
"llm_output": {
"topic": "Возврат товара",
"sentiment": "нейтральная",
"summary": "Клиент попросил оформить возврат, оператор объяснил условия и создал заявку.",
"resolved": true,
"action_items": ["Отправить клиенту инструкцию по возврату"]
}
}
```
## Лайфхаки [#лайфхаки]
* **Двухканальная запись.** Если телефония поддерживает раздельную запись каналов, включите её — вместе с `num_speakers=2` это даёт максимально точное разделение.
* **Точные роли.** В сложных звонках передавайте роли с описаниями (`{"Оператор": "сотрудник поддержки", "Клиент": "позвонивший"}`) — так модель распределяет их точнее.
* **Русская речь — `nexara-ru`.** Для звонков на русском эта модель точнее универсальной. Эмоциональный анализ поддерживается только на этой модели.
* **Передавайте большие файлы [по ссылке](/file-url).** Удобно отдавать запись по URL (например, pre-signed ссылке на хранилище), а не загружать файлом.
Диаризация, разметка ролей, распознавание эмоций и LLM-анализ тарифицируются
дополнительно к стоимости распознавания. Итоговую стоимость смотрите на
странице [тарифов](/pricing).
# Тарифы (/pricing)
Оплата рассчитывается по фактическому использованию.
Распознавание речи (транскрибация) и дополнения тарифицируются посекундно (например, если в запросе аудио длиной 15 секунд, то спишется 9 копеек). Округление секунд идет вверх.
# Фильтрация мата (/profanity-filtering)
Когда включён параметр `profanity_filter`, русский мат в расшифровке маскируется: первая и последняя буквы слова сохраняются, а середина заменяется звёздочками. Маскирование применяется ко всему ответу — к полю `text`, к сегментам и к словам с временными метками.
Фильтр рассчитан на русскую нецензурную лексику. Он определяет мат по форме
слова, а не по совпадению букв, поэтому безобидные похожие слова (`рубля`,
`страховка`, `ребаланс`) не затрагиваются.
## Как включить [#как-включить]
Передайте `profanity_filter=true` в запросе на `POST /v1/audio/transcriptions`. Замените `nx-XXXXXXXXXXXXXXXXXXXXXXXX` на свой [API-ключ](/authentication), а `audio.mp3` — на путь к вашему файлу.
Официальная библиотека [`nexara`](/python-sdk) (`pip install nexara`):
```python
from nexara import Nexara
client = Nexara() # ключ из переменной окружения NEXARA_API_KEY
result = client.transcriptions.create(
file="audio.mp3",
profanity_filter=True,
)
print(result.text)
```
Официальная библиотека [`nexara-sdk`](/typescript-sdk) (`npm install nexara-sdk`):
```typescript
import { Nexara } from "nexara-sdk";
const client = new Nexara(); // ключ из переменной окружения NEXARA_API_KEY
const result = await client.transcriptions.create({
file: "audio.mp3",
profanity_filter: true,
});
console.log(result.text);
```
```bash
curl https://api.nexara.ru/v1/audio/transcriptions \
-H "Authorization: Bearer nx-XXXXXXXXXXXXXXXXXXXXXXXX" \
-F "file=@audio.mp3" \
-F "model=whisper-1" \
-F "profanity_filter=true"
```
```python
import requests
with open("audio.mp3", "rb") as audio_file:
response = requests.post(
"https://api.nexara.ru/v1/audio/transcriptions",
headers={"Authorization": "Bearer nx-XXXXXXXXXXXXXXXXXXXXXXXX"},
files={"file": audio_file},
data={
"model": "whisper-1",
"profanity_filter": "true",
},
)
print(response.json()["text"])
```
Параметр `profanity_filter` не входит в стандартный набор OpenAI SDK — передайте его через `extra_body`.
```python
from openai import OpenAI
client = OpenAI(
api_key="nx-XXXXXXXXXXXXXXXXXXXXXXXX",
base_url="https://api.nexara.ru/v1",
)
with open("audio.mp3", "rb") as audio_file:
transcript = client.audio.transcriptions.create(
model="whisper-1",
file=audio_file,
extra_body={"profanity_filter": True},
)
print(transcript.text)
```
Node.js 18+:
```javascript
import fs from "fs";
const form = new FormData();
form.append("file", new Blob([fs.readFileSync("audio.mp3")]), "audio.mp3");
form.append("model", "whisper-1");
form.append("profanity_filter", "true");
const response = await fetch(
"https://api.nexara.ru/v1/audio/transcriptions",
{
method: "POST",
headers: { Authorization: "Bearer nx-XXXXXXXXXXXXXXXXXXXXXXXX" },
body: form,
},
);
const result = await response.json();
console.log(result.text);
```
```go
package main
import (
"bytes"
"fmt"
"io"
"mime/multipart"
"net/http"
"os"
)
func main() {
file, _ := os.Open("audio.mp3")
defer file.Close()
var buf bytes.Buffer
writer := multipart.NewWriter(&buf)
part, _ := writer.CreateFormFile("file", "audio.mp3")
io.Copy(part, file)
writer.WriteField("model", "whisper-1")
writer.WriteField("profanity_filter", "true")
writer.Close()
req, _ := http.NewRequest("POST", "https://api.nexara.ru/v1/audio/transcriptions", &buf)
req.Header.Set("Authorization", "Bearer nx-XXXXXXXXXXXXXXXXXXXXXXXX")
req.Header.Set("Content-Type", writer.FormDataContentType())
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
fmt.Println(string(body))
}
```
```ruby
require "net/http"
require "uri"
require "json"
uri = URI("https://api.nexara.ru/v1/audio/transcriptions")
request = Net::HTTP::Post.new(uri)
request["Authorization"] = "Bearer nx-XXXXXXXXXXXXXXXXXXXXXXXX"
request.set_form(
[
["file", File.open("audio.mp3")],
["model", "whisper-1"],
["profanity_filter", "true"],
],
"multipart/form-data",
)
response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
http.request(request)
end
puts JSON.parse(response.body)["text"]
```
## Результат [#результат]
В ответе нецензурные слова приходят уже замаскированными:
```json
{
"text": "Чтобы у тебя х*й во лбу вырос, у***к, б***ь! Переписываем. Бибикало е****е, б***ь."
}
```
Фильтрация мата тарифицируется дополнительно к стоимости распознавания.
Стоимость услуги смотрите на странице [тарифов](/pricing).
# Python SDK (/python-sdk)
**`nexara`** — официальная Python-библиотека для Nexara API. Она берёт на себя сборку запросов, разбор ответов в типизированные модели, обработку ошибок и повторные попытки, а асинхронные задачи превращает в один вызов `job.wait()`. Библиотека полностью типизирована (mypy strict) и работает на Python 3.10+.
```bash
pip install nexara
```
Сохраните API-ключ в переменной окружения `NEXARA_API_KEY`. SDK читает её автоматически при создании клиента, поэтому ключ не нужно указывать в коде — и он не попадёт в репозиторий:
```bash
export NEXARA_API_KEY=nx-...
```
Команда `export` задаёт переменную только для текущей сессии терминала. Чтобы
ключ сохранялся между сессиями, добавьте эту строку в файл конфигурации вашей
оболочки (например, `~/.zshrc` или `~/.bashrc`). На сервере переменную удобнее
задавать через настройки окружения или менеджер секретов.
## Быстрый старт [#быстрый-старт]
```python
from nexara import Nexara
client = Nexara() # ключ из переменной окружения NEXARA_API_KEY
text = client.transcriptions.create(file="audio.mp3").text
print(text)
```
Клиент берёт ключ из `NEXARA_API_KEY` — той самой переменной, что вы задали выше, — поэтому в коде его указывать не нужно. Передать ключ явным аргументом `api_key=` тоже можно, но не храните его в коде и не коммитьте в репозиторий.
## Клиент [#клиент]
```python
client = Nexara(
# api_key по умолчанию берётся из переменной окружения NEXARA_API_KEY,
# либо можете передать ключ сами: api_key="nx-..."
timeout=600.0, # таймаут запроса в секундах
max_retries=2, # повторы при 429 и сетевых сбоях
)
```
Клиент можно использовать как контекстный менеджер (`with Nexara() as client:`) — тогда HTTP-соединения закроются автоматически.
## Транскрибация [#транскрибация]
Передайте ровно один из параметров: `file` (путь, `bytes` или открытый бинарный файл) или `url` (прямая ссылка). Файл, переданный путём, стримится с диска и не загружается в память целиком.
```python
# Локальный файл
result = client.transcriptions.create(file="audio.mp3")
print(result.text)
# Файл по ссылке
result = client.transcriptions.create(url="https://example.com/audio.mp3")
print(result.text)
```
Параметры те же, что и у [API](/api-reference/create_transcription_audio_transcriptions_post): `model`, `language`, `response_format`, `timestamp_granularities`, `profanity_filter` и другие. Форматы `text`, `srt` и `vtt` возвращаются обычной строкой, `json` и `verbose_json` — типизированными объектами:
```python
srt = client.transcriptions.create(file="video.mp4", response_format="srt")
with open("video.srt", "w", encoding="utf-8") as f:
f.write(srt)
verbose = client.transcriptions.create(
file="audio.mp3",
response_format="verbose_json",
timestamp_granularities=["word"],
)
for word in verbose.words:
print(f"{word.start:.2f}–{word.end:.2f}: {word.word}")
```
## Диаризация и роли [#диаризация-и-роли]
Разделение по говорящим включается так же, как в API, — `task="diarize"`. Список ролей передаётся обычным списком или словарём Python: SDK сам сериализует его в JSON.
```python
call = client.transcriptions.create(
file="call.mp3",
task="diarize",
num_speakers=2,
roles=["Оператор", "Клиент"], # или "auto", или {"Роль": "описание"}
)
for segment in call.segments:
print(f"{segment.speaker}: {segment.text}")
```
Подробнее о режимах — на странице [спикеры и роли](/speakers-roles).
## Эмоции [#эмоции]
Флаг `emotions=True` добавляет к каждому сегменту диаризации объект `emotion`: метку `label` (`angry`, `sad`, `neutral` или `positive`), уверенность модели `confidence` и распределение вероятностей `probs`.
```python
call = client.transcriptions.create(
file="call.mp3",
task="diarize",
model="nexara-ru",
emotions=True,
)
for segment in call.segments:
if segment.emotion:
print(f"{segment.speaker}: {segment.emotion.label} "
f"({segment.emotion.confidence:.0%}) — {segment.text}")
else:
print(f"{segment.speaker}: эмоция не определена — {segment.text}")
```
Эмоция приходит не для каждого сегмента: если модель не смогла оценить фрагмент, поле `emotion` останется `None`. Проверяйте его перед чтением, как в примере выше. У слов (`words`) эмоции нет — она считается по сегменту целиком.
Полный набор меток доступен константой `EMOTION_LABELS`:
```python
from nexara import EMOTION_LABELS
print(EMOTION_LABELS) # ('angry', 'sad', 'neutral', 'positive')
```
Подробнее об условиях и ограничениях — на странице [распознавание эмоций](/emotions).
Распознавание эмоций тарифицируется дополнительно к стоимости диаризации.
Если ни один сегмент не удалось оценить, надбавка не списывается. Стоимость
услуги смотрите на странице [тарифов](/pricing).
## Асинхронные задачи [#асинхронные-задачи]
Для длинных записей используйте [асинхронный режим](/async-transcription): `create_job()` ставит задачу и сразу возвращает объект `Job`, а `job.wait()` опрашивает статус и возвращает готовый результат.
```python
job = client.transcriptions.create_job(file="long_recording.mp3")
print(job.job_id, job.status) # in_progress
result = job.wait() # опрашивает статус; таймаут по умолчанию — 1800 секунд
print(result.text)
```
Задачу можно забрать позже — в том числе из другого процесса:
```python
job = client.transcriptions.retrieve_job(job_id)
if job.status == "complete":
print(job.result)
```
Что важно знать:
* Результат хранится **12 часов** с момента создания задачи, затем удаляется — после этого `retrieve_job()` вернёт `NotFoundError`.
* Одновременно на один ключ может выполняться до **200** задач.
* Если задача завершилась ошибкой, `wait()` бросает `JobFailedError`. Неудавшаяся задача **не тарифицируется** — повторная отправка бесплатна.
* Если задача не успела за таймаут, `wait()` бросает `JobTimeoutError`; сама задача при этом не отменяется, её можно забрать позже через `retrieve_job()`.
«Асинхронный» здесь означает фоновую обработку на сервере, а не asyncio. Для
asyncio используйте [AsyncNexara](#asyncio) — оба клиента поддерживают и
`create()`, и `create_job()`.
## LLM-анализ [#llm-анализ]
Передайте `prompt`, чтобы прогнать расшифровку через языковую модель, и `json_schema` — чтобы получить [структурированный ответ](/llm-usage). Схема передаётся обычным словарём Python, а `llm_output` возвращается уже разобранным объектом — никакого ручного `json.dumps`/`json.loads`.
```python
result = client.transcriptions.create(
file="meeting.mp3",
prompt="Составь протокол встречи: резюме, решения и задачи.",
json_schema={
"type": "object",
"properties": {
"summary": {"type": "string"},
"decisions": {"type": "array", "items": {"type": "string"}},
"action_items": {"type": "array", "items": {"type": "string"}},
},
"required": ["summary", "decisions", "action_items"],
},
)
print(result.llm_output["summary"]) # dict по вашей схеме
print(result.transcription.text) # расшифровка, из которой он получен
```
Без `json_schema` поле `llm_output` — строка со свободным ответом модели.
## asyncio [#asyncio]
`AsyncNexara` — тот же интерфейс под `await`:
```python
import asyncio
from nexara import AsyncNexara
async def main():
async with AsyncNexara() as client:
result = await client.transcriptions.create(file="audio.mp3")
print(result.text)
asyncio.run(main())
```
## Ошибки [#ошибки]
Некорректные запросы (например, `file` и `url` одновременно, слишком много ролей) отклоняются на стороне клиента с `NexaraValidationError` — ещё до обращения к серверу. Ошибки сервера превращаются в типизированные исключения по коду статуса:
| Исключение | Код | Когда возникает |
| -------------------------- | --- | ---------------------------------------------------- |
| `BadRequestError` | 400 | Некорректные параметры запроса |
| `InsufficientBalanceError` | 402 | Недостаточно средств на балансе |
| `AuthenticationError` | 403 | Ключ не передан или недействителен |
| `NotFoundError` | 404 | Задача, модель или ключ не найдены |
| `RateLimitError` | 429 | Превышен лимит частоты или число одновременных задач |
| `InternalServerError` | 500 | Внутренняя ошибка сервера |
| `APIConnectionError` | — | Запрос не дошёл до сервера (сеть, таймаут) |
Все они наследуются от `NexaraError`, а у ошибок API есть поля `status_code` и `detail` с сообщением сервера. Полный список кодов — на странице [ошибок](/errors).
```python
from nexara import Nexara, InsufficientBalanceError, RateLimitError
client = Nexara()
try:
result = client.transcriptions.create(file="audio.mp3")
except InsufficientBalanceError as e: # 402
print("Пополните баланс:", e.detail)
except RateLimitError:
print("Слишком много запросов, попробуйте позже.")
```
## Ссылки [#ссылки]
* Пакет на PyPI: [pypi.org/project/nexara](https://pypi.org/project/nexara/)
* Справочник параметров API: [референс API](/api-reference/create_transcription_audio_transcriptions_post)
# Быстрый старт (/quickstart)
За пару минут вы получите API-ключ и сделаете первый запрос на транскрибацию.
### Получите API-ключ [#получите-api-ключ]
Создайте ключ в личном кабинете — [app.nexara.ru](https://app.nexara.ru). Ключ выглядит как `nx-XXXXXXXXXXXXXXXXXXXXXXXX` и передаётся в заголовке `Authorization`. Подробнее — на странице [аутентификация](/authentication).
Не вставляйте ключ прямо в код. Сохраните его в переменную окружения:
`export NEXARA_API_KEY="nx-..."`.
### Сделайте первый запрос [#сделайте-первый-запрос]
Отправьте аудиофайл на `POST /v1/audio/transcriptions`. Замените `nx-XXXXXXXXXXXXXXXXXXXXXXXX` на свой ключ, а `audio.mp3` — на путь к вашему файлу.
Самый быстрый способ — официальная библиотека [`nexara`](/python-sdk):
```bash
pip install nexara
```
```python
from nexara import Nexara
client = Nexara() # ключ из переменной окружения NEXARA_API_KEY
result = client.transcriptions.create(file="audio.mp3")
print(result.text)
```
Официальная библиотека [`nexara-sdk`](/typescript-sdk):
```bash
npm install nexara-sdk
```
```typescript
import { Nexara } from "nexara-sdk";
const client = new Nexara(); // ключ из переменной окружения NEXARA_API_KEY
const { text } = await client.transcriptions.create({ file: "audio.mp3" });
console.log(text);
```
```bash
curl https://api.nexara.ru/v1/audio/transcriptions \
-H "Authorization: Bearer nx-XXXXXXXXXXXXXXXXXXXXXXXX" \
-F "file=@audio.mp3" \
-F "model=whisper-1"
```
```python
import requests
with open("audio.mp3", "rb") as audio_file:
response = requests.post(
"https://api.nexara.ru/v1/audio/transcriptions",
headers={"Authorization": "Bearer nx-XXXXXXXXXXXXXXXXXXXXXXXX"},
files={"file": audio_file},
data={"model": "whisper-1"},
)
print(response.json()["text"])
```
Nexara API совместим с OpenAI SDK — достаточно указать `base_url`.
```python
from openai import OpenAI
client = OpenAI(
api_key="nx-XXXXXXXXXXXXXXXXXXXXXXXX",
base_url="https://api.nexara.ru/v1",
)
with open("audio.mp3", "rb") as audio_file:
transcript = client.audio.transcriptions.create(
model="whisper-1",
file=audio_file,
)
print(transcript.text)
```
Node.js 18+:
```javascript
import fs from "fs";
const form = new FormData();
form.append("file", new Blob([fs.readFileSync("audio.mp3")]), "audio.mp3");
form.append("model", "whisper-1");
const response = await fetch(
"https://api.nexara.ru/v1/audio/transcriptions",
{
method: "POST",
headers: { Authorization: "Bearer nx-XXXXXXXXXXXXXXXXXXXXXXXX" },
body: form,
},
);
const result = await response.json();
console.log(result.text);
```
```go
package main
import (
"bytes"
"fmt"
"io"
"mime/multipart"
"net/http"
"os"
)
func main() {
file, _ := os.Open("audio.mp3")
defer file.Close()
var buf bytes.Buffer
writer := multipart.NewWriter(&buf)
part, _ := writer.CreateFormFile("file", "audio.mp3")
io.Copy(part, file)
writer.WriteField("model", "whisper-1")
writer.Close()
req, _ := http.NewRequest("POST", "https://api.nexara.ru/v1/audio/transcriptions", &buf)
req.Header.Set("Authorization", "Bearer nx-XXXXXXXXXXXXXXXXXXXXXXXX")
req.Header.Set("Content-Type", writer.FormDataContentType())
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
fmt.Println(string(body))
}
```
```ruby
require "net/http"
require "uri"
require "json"
uri = URI("https://api.nexara.ru/v1/audio/transcriptions")
request = Net::HTTP::Post.new(uri)
request["Authorization"] = "Bearer nx-XXXXXXXXXXXXXXXXXXXXXXXX"
request.set_form(
[
["file", File.open("audio.mp3")],
["model", "whisper-1"],
],
"multipart/form-data",
)
response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
http.request(request)
end
puts JSON.parse(response.body)["text"]
```
В ответе придёт распознанный текст:
```json
{
"text": "Привет! Это пример транскрибации."
}
```
Готово — вы сделали первый запрос.
## Что дальше [#что-дальше]
Официальная библиотека: `pip install nexara` — и весь API в несколько строк.
Официальная библиотека: `npm install nexara-sdk` — типизированный клиент для Node.js.
Транскрибация файла с диска на разных языках программирования.
Разделение аудио по говорящим и разметка ролей.
`json`, `text`, `verbose_json`, а также субтитры `srt` и `vtt`.
Резюме, извлечение данных и структурированный вывод по расшифровке.
Фоновая обработка длинных записей по `job_id`.
Полное описание эндпоинтов и всех параметров.
# Форматы ответа (/response-formats)
Nexara API позволяет указать формат, в котором вы хотите получить результаты. Используя параметр `response_format` в вашем запросе, вы можете настроить вывод так, чтобы он наилучшим образом соответствовал вашим потребностям, будь то простой текст, структурированные данные или готовые к использованию файлы субтитров. Выбор `srt` или `vtt` дает не просто текст с таймингами; эти форматы предоставляют оформленные субтитры, автоматически оптимизированные для комфортного чтения и восприятия на экране. Примеры использования API смотрите в [документации API](/api-reference/create_transcription_audio_transcriptions_post).
API поддерживает следующие форматы вывода:
* **`json`:** Возвращает стандартный JSON-объект, содержащий транскрибированный текст.
* **`text`:** Возвращает транскрипцию в виде одной строки простого текста.
* **`verbose_json`:** Возвращает подробный JSON-объект, содержащий текст, язык, продолжительность, а также, возможно, временные метки на уровне сегментов и слов (если запрошено через `timestamp_granularities[]`).
* **`srt`:** Возвращает транскрипцию, отформатированную как файл субтитров SRT.
* **`vtt`:** Возвращает транскрипцию, отформатированную как файл субтитров WebVTT.
Чтобы выбрать формат, передайте параметр `response_format` в запросе:
```python
from nexara import Nexara
client = Nexara() # ключ из переменной окружения NEXARA_API_KEY
result = client.transcriptions.create(
file="audio.mp3",
response_format="verbose_json",
)
```
В [Python SDK](/python-sdk) форматы `json` и `verbose_json` возвращаются типизированными объектами, а `text`, `srt` и `vtt` — обычной строкой.
```typescript
import { Nexara } from "nexara-sdk";
const client = new Nexara(); // ключ из переменной окружения NEXARA_API_KEY
const result = await client.transcriptions.create({
file: "audio.mp3",
response_format: "verbose_json",
});
```
В [TypeScript SDK](/typescript-sdk) форматы `json` и `verbose_json` возвращаются типизированными объектами, а `text`, `srt` и `vtt` — обычной строкой.
```bash
curl https://api.nexara.ru/v1/audio/transcriptions \
-H "Authorization: Bearer nx-XXXXXXXXXXXXXXXXXXXXXXXX" \
-F "file=@audio.mp3" \
-F "model=whisper-1" \
-F "response_format=verbose_json"
```
## Примеры вывода [#примеры-вывода]
Для одной и той же записи разные форматы возвращают результат по-разному.
### `json` [#json]
```json
{
"text": "Привет! Это пример транскрибации."
}
```
### `text` [#text]
Ответ приходит текстом без JSON-обёртки:
```text
Привет! Это пример транскрибации.
```
### `verbose_json` [#verbose_json]
Подробный объект с текстом, языком, длительностью и сегментами. Массив `words` появляется, только если запрошены [временные метки слов](/timestamps) через `timestamp_granularities[]=word`. Ответ подобен ответу модели `whisper-1` на официальном API от OpenAI.
```json
{
"task": "transcribe",
"language": "ru",
"duration": 4.3,
"text": "Привет! Это пример транскрибации.",
"segments": [
{
"id": 0,
"seek": 0,
"start": 0.0,
"end": 2.1,
"text": "Привет! Это пример",
"tokens": [50150, 386, 2338, 2608, 1828, ...],
"temperature": 0.0,
"avg_logprob": 0.0,
"compression_ratio": 1.698224852071006,
"no_speech_prob": 0.0
},
{
"id": 1,
"seek": 2.1,
"start": 2.1,
"end": 4.3,
"text": "транскрибации.",
"tokens": [2338, 2608, 1828, ...],
"temperature": 0.0,
"avg_logprob": 0.0,
"compression_ratio": 1.5,
"no_speech_prob": 0.0
}
]
}
```
### `srt` [#srt]
```text
1
00:00:00,000 --> 00:00:02,100
Привет! Это пример
2
00:00:02,100 --> 00:00:04,300
транскрибации.
```
### `vtt` [#vtt]
```text
WEBVTT
1
00:00:00.000 --> 00:00:02.100
Привет! Это пример
2
00:00:02.100 --> 00:00:04.300
транскрибации.
```
Когда запрашиваете ответ в формате субтитров, (`srt` или `vtt`) **не**
используйте `response.text` если делаете запрос через `request` библиотеку в
Python. Используйте `response.json`, как и для остальных форматов ответа.
# Спикеры и роли (/speakers-roles)
**Диаризация** разделяет аудио по говорящим: API определяет, какой фрагмент речи кому принадлежит, и присваивает каждому сегменту метку говорящего (`speaker_0`, `speaker_1`) — или понятную роль (`Клиент`, `Агент`). Концептуально режим описан на странице [диаризация](/diarization).
## Разделение по говорящим [#разделение-по-говорящим]
Передайте `task=diarize` в запросе на `POST /v1/audio/transcriptions`. Замените `nx-XXXXXXXXXXXXXXXXXXXXXXXX` на свой [API-ключ](/authentication), а `meeting.mp3` — на путь к вашему файлу.
Официальная библиотека [`nexara`](/python-sdk) (`pip install nexara`):
```python
from nexara import Nexara
client = Nexara() # ключ из переменной окружения NEXARA_API_KEY
result = client.transcriptions.create(
file="meeting.mp3",
task="diarize",
)
for segment in result.segments:
print(f"{segment.speaker}: {segment.text}")
```
Официальная библиотека [`nexara-sdk`](/typescript-sdk) (`npm install nexara-sdk`):
```typescript
import { Nexara } from "nexara-sdk";
const client = new Nexara(); // ключ из переменной окружения NEXARA_API_KEY
const result = await client.transcriptions.create({
file: "meeting.mp3",
task: "diarize",
});
for (const segment of result.segments) {
console.log(`${segment.speaker}: ${segment.text}`);
}
```
```bash
curl https://api.nexara.ru/v1/audio/transcriptions \
-H "Authorization: Bearer nx-XXXXXXXXXXXXXXXXXXXXXXXX" \
-F "file=@meeting.mp3" \
-F "model=whisper-1" \
-F "task=diarize"
```
```python
import requests
with open("meeting.mp3", "rb") as audio_file:
response = requests.post(
"https://api.nexara.ru/v1/audio/transcriptions",
headers={"Authorization": "Bearer nx-XXXXXXXXXXXXXXXXXXXXXXXX"},
files={"file": audio_file},
data={
"model": "whisper-1",
"task": "diarize",
},
)
print(response.json())
```
Node.js 18+:
```javascript
import fs from "fs";
const form = new FormData();
form.append("file", new Blob([fs.readFileSync("meeting.mp3")]), "meeting.mp3");
form.append("model", "whisper-1");
form.append("task", "diarize");
const response = await fetch(
"https://api.nexara.ru/v1/audio/transcriptions",
{
method: "POST",
headers: { Authorization: "Bearer nx-XXXXXXXXXXXXXXXXXXXXXXXX" },
body: form,
},
);
console.log(await response.json());
```
```go
package main
import (
"bytes"
"fmt"
"io"
"mime/multipart"
"net/http"
"os"
)
func main() {
file, _ := os.Open("meeting.mp3")
defer file.Close()
var buf bytes.Buffer
writer := multipart.NewWriter(&buf)
part, _ := writer.CreateFormFile("file", "meeting.mp3")
io.Copy(part, file)
writer.WriteField("model", "whisper-1")
writer.WriteField("task", "diarize")
writer.Close()
req, _ := http.NewRequest("POST", "https://api.nexara.ru/v1/audio/transcriptions", &buf)
req.Header.Set("Authorization", "Bearer nx-XXXXXXXXXXXXXXXXXXXXXXXX")
req.Header.Set("Content-Type", writer.FormDataContentType())
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
fmt.Println(string(body))
}
```
```ruby
require "net/http"
require "uri"
require "json"
uri = URI("https://api.nexara.ru/v1/audio/transcriptions")
request = Net::HTTP::Post.new(uri)
request["Authorization"] = "Bearer nx-XXXXXXXXXXXXXXXXXXXXXXXX"
request.set_form(
[
["file", File.open("meeting.mp3")],
["model", "whisper-1"],
["task", "diarize"],
],
"multipart/form-data",
)
response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
http.request(request)
end
puts response.body
```
В ответе каждый сегмент содержит метку говорящего в поле `speaker`:
```json
{
"task": "diarize",
"language": "ru",
"duration": 12.4,
"text": "Здравствуйте! Чем могу помочь? Хочу оформить возврат.",
"segments": [
{
"start": 0.0,
"end": 2.1,
"text": "Здравствуйте! Чем могу помочь?",
"speaker": "speaker_0"
},
{
"start": 2.5,
"end": 5.0,
"text": "Хочу оформить возврат.",
"speaker": "speaker_1"
}
]
}
```
Метки говорящих сохраняются во всех [форматах ответа](/response-formats). Например, при `response_format=text`:
```
Speaker 0: Здравствуйте! Чем могу помочь?
Speaker 1: Хочу оформить возврат.
```
Минимальная длительность аудио для диаризации — **3 секунды**. Более короткие
записи выдают ошибку `400`. Если вы заранее знаете число говорящих, передайте
`num_speakers` — это повысит качество разделения.
## Разметка ролей [#разметка-ролей]
По умолчанию говорящие обозначаются безликими метками `speaker_0`, `speaker_1`. **Разметка ролей** заменяет их на понятные роли — например, `Клиент` и `Агент`. Она включается параметром `roles` и доступна **только вместе с диаризацией** (`task=diarize`). Поддерживаются три режима:
`roles=auto` — модель сама определяет и назначает подходящие роли на основе содержания разговора.
JSON-массив строк, например `["Клиент", "Агент"]` — вы отправляете набор ролей, а модель распределяет их между говорящими.
JSON-объект `{"Клиент": "Клиент, который обратился в поддержку", "Агент": "Оператор поддержки"}` — фиксированные роли с описаниями, которые помогают модели точнее их распознать.
Достаточно добавить поле `roles` к запросу на диаризацию. Значение — либо `auto`, либо JSON-строка со списком или объектом ролей.
В SDK роли передаются обычным списком или словарём Python — сериализация в JSON выполняется автоматически.
```python
from nexara import Nexara
client = Nexara() # ключ из переменной окружения NEXARA_API_KEY
result = client.transcriptions.create(
file="call.mp3",
task="diarize",
roles=["Клиент", "Агент"], # или "auto", или {"Роль": "описание"}
)
for segment in result.segments:
print(f"{segment.speaker}: {segment.text}")
```
В SDK роли передаются обычным массивом или объектом — сериализация в JSON выполняется автоматически.
```typescript
import { Nexara } from "nexara-sdk";
const client = new Nexara(); // ключ из переменной окружения NEXARA_API_KEY
const result = await client.transcriptions.create({
file: "call.mp3",
task: "diarize",
roles: ["Клиент", "Агент"], // или "auto", или { "Роль": "описание" }
});
for (const segment of result.segments) {
console.log(`${segment.speaker}: ${segment.text}`);
}
```
```bash
curl https://api.nexara.ru/v1/audio/transcriptions \
-H "Authorization: Bearer nx-XXXXXXXXXXXXXXXXXXXXXXXX" \
-F "file=@call.mp3" \
-F "model=whisper-1" \
-F "task=diarize" \
-F 'roles=["Клиент", "Агент"]'
```
```python
import requests
with open("call.mp3", "rb") as audio_file:
response = requests.post(
"https://api.nexara.ru/v1/audio/transcriptions",
headers={"Authorization": "Bearer nx-XXXXXXXXXXXXXXXXXXXXXXXX"},
files={"file": audio_file},
data={
"model": "whisper-1",
"task": "diarize",
"roles": '["Клиент", "Агент"]',
},
)
print(response.json())
```
```javascript
import fs from "fs";
const form = new FormData();
form.append("file", new Blob([fs.readFileSync("call.mp3")]), "call.mp3");
form.append("model", "whisper-1");
form.append("task", "diarize");
form.append("roles", JSON.stringify(["Клиент", "Агент"]));
const response = await fetch(
"https://api.nexara.ru/v1/audio/transcriptions",
{
method: "POST",
headers: { Authorization: "Bearer nx-XXXXXXXXXXXXXXXXXXXXXXXX" },
body: form,
},
);
console.log(await response.json());
```
```go
package main
import (
"bytes"
"fmt"
"io"
"mime/multipart"
"net/http"
"os"
)
func main() {
file, _ := os.Open("call.mp3")
defer file.Close()
var buf bytes.Buffer
writer := multipart.NewWriter(&buf)
part, _ := writer.CreateFormFile("file", "call.mp3")
io.Copy(part, file)
writer.WriteField("model", "whisper-1")
writer.WriteField("task", "diarize")
writer.WriteField("roles", `["Клиент", "Агент"]`)
writer.Close()
req, _ := http.NewRequest("POST", "https://api.nexara.ru/v1/audio/transcriptions", &buf)
req.Header.Set("Authorization", "Bearer nx-XXXXXXXXXXXXXXXXXXXXXXXX")
req.Header.Set("Content-Type", writer.FormDataContentType())
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
fmt.Println(string(body))
}
```
```ruby
require "net/http"
require "uri"
require "json"
uri = URI("https://api.nexara.ru/v1/audio/transcriptions")
request = Net::HTTP::Post.new(uri)
request["Authorization"] = "Bearer nx-XXXXXXXXXXXXXXXXXXXXXXXX"
request.set_form(
[
["file", File.open("call.mp3")],
["model", "whisper-1"],
["task", "diarize"],
["roles", ["Клиент", "Агент"].to_json],
],
"multipart/form-data",
)
response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
http.request(request)
end
puts response.body
```
В результате метки `speaker_N` заменяются на назначенные роли:
```json
{
"task": "diarize",
"segments": [
{
"start": 0.0,
"end": 2.1,
"text": "Здравствуйте! Чем могу помочь?",
"speaker": "Агент"
},
{
"start": 2.5,
"end": 5.0,
"text": "Хочу оформить возврат.",
"speaker": "Клиент"
}
]
}
```
Если модель определила больше говорящих, чем было передано в списке, остальные
спикеры будут помечены как неизвестные. Например, если в записи говорило 4
человека, а в списке передано только 2, остальные будут помечены как
`unknown_1` и `unknown_2`.
### Ограничения [#ограничения]
* В одном запросе можно задать не более **10** ролей.
* Каждое название роли — непустая строка длиной не более **64** символов, роли не должны повторяться.
* Описание роли (в режиме с описаниями) не должно превышать **500** символов.
Разметка ролей тарифицируется дополнительно к стоимости диаризации. Если
разметку не удалось применить (например, в записи не оказалось говорящих),
надбавка не списывается, а в ответе остаются исходные метки `speaker_N`.
Стоимость услуги смотрите на странице [тарифов](/pricing).
## Что учитывать [#что-учитывать]
* **Хранение ключа.** Не вставляйте API-ключ прямо в код. Храните его в переменной окружения (например, `export NEXARA_API_KEY="nx-..."`) и ссылайтесь на неё. Подробнее — на странице [аутентификации](/authentication#curl).
# Структурированный вывод (/structured-output)
# Субтитры (/subtitles)
Nexara умеет возвращать готовые файлы субтитров — `srt` или `vtt`. Их можно сразу подключить к видео в плеере или редакторе. Достаточно указать нужный формат в параметре `response_format`; временные метки при этом рассчитываются автоматически, отдельно запрашивать их не нужно.
## Создание субтитров [#создание-субтитров]
Передайте `response_format=srt` (или `vtt`) в запросе на `POST /v1/audio/transcriptions`. Замените `nx-XXXXXXXXXXXXXXXXXXXXXXXX` на свой [API-ключ](/authentication), а `video.mp4` — на путь к вашему файлу.
Официальная библиотека [`nexara`](/python-sdk) возвращает форматы `srt` и `vtt` обычной строкой — готовой к записи в файл.
```python
from nexara import Nexara
client = Nexara() # ключ из переменной окружения NEXARA_API_KEY
subtitles = client.transcriptions.create(
file="video.mp4",
response_format="srt", # или "vtt"
)
with open("video.srt", "w", encoding="utf-8") as f:
f.write(subtitles)
```
Официальная библиотека [`nexara-sdk`](/typescript-sdk) возвращает форматы `srt` и `vtt` обычной строкой — готовой к записи в файл.
```typescript
import { writeFile } from "node:fs/promises";
import { Nexara } from "nexara-sdk";
const client = new Nexara(); // ключ из переменной окружения NEXARA_API_KEY
const subtitles = await client.transcriptions.create({
file: "video.mp4",
response_format: "srt", // или "vtt"
});
await writeFile("video.srt", subtitles);
```
```bash
curl https://api.nexara.ru/v1/audio/transcriptions \
-H "Authorization: Bearer nx-XXXXXXXXXXXXXXXXXXXXXXXX" \
-F "file=@video.mp4" \
-F "model=whisper-1" \
-F "response_format=srt"
```
```python
import requests
with open("video.mp4", "rb") as audio_file:
response = requests.post(
"https://api.nexara.ru/v1/audio/transcriptions",
headers={"Authorization": "Bearer nx-XXXXXXXXXXXXXXXXXXXXXXXX"},
files={"file": audio_file},
data={
"model": "whisper-1",
"response_format": "srt",
},
)
subtitles = response.json()
with open("video.srt", "w", encoding="utf-8") as f:
f.write(subtitles)
```
```python
from openai import OpenAI
client = OpenAI(
api_key="nx-XXXXXXXXXXXXXXXXXXXXXXXX",
base_url="https://api.nexara.ru/v1",
)
with open("video.mp4", "rb") as audio_file:
subtitles = client.audio.transcriptions.create(
model="whisper-1",
file=audio_file,
response_format="srt",
)
print(subtitles)
```
Node.js 18+:
```javascript
import fs from "fs";
const form = new FormData();
form.append("file", new Blob([fs.readFileSync("video.mp4")]), "video.mp4");
form.append("model", "whisper-1");
form.append("response_format", "srt");
const response = await fetch(
"https://api.nexara.ru/v1/audio/transcriptions",
{
method: "POST",
headers: { Authorization: "Bearer nx-XXXXXXXXXXXXXXXXXXXXXXXX" },
body: form,
},
);
const subtitles = await response.json();
fs.writeFileSync("video.srt", subtitles);
```
```go
package main
import (
"bytes"
"fmt"
"io"
"mime/multipart"
"net/http"
"os"
)
func main() {
file, _ := os.Open("video.mp4")
defer file.Close()
var buf bytes.Buffer
writer := multipart.NewWriter(&buf)
part, _ := writer.CreateFormFile("file", "video.mp4")
io.Copy(part, file)
writer.WriteField("model", "whisper-1")
writer.WriteField("response_format", "srt")
writer.Close()
req, _ := http.NewRequest("POST", "https://api.nexara.ru/v1/audio/transcriptions", &buf)
req.Header.Set("Authorization", "Bearer nx-XXXXXXXXXXXXXXXXXXXXXXXX")
req.Header.Set("Content-Type", writer.FormDataContentType())
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
fmt.Println(string(body))
}
```
```ruby
require "net/http"
require "uri"
require "json"
uri = URI("https://api.nexara.ru/v1/audio/transcriptions")
request = Net::HTTP::Post.new(uri)
request["Authorization"] = "Bearer nx-XXXXXXXXXXXXXXXXXXXXXXXX"
request.set_form(
[
["file", File.open("video.mp4")],
["model", "whisper-1"],
["response_format", "srt"],
],
"multipart/form-data",
)
response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
http.request(request)
end
File.write("video.srt", JSON.parse(response.body))
```
В ответе приходит готовый файл субтитров:
```text
1
00:00:00,000 --> 00:00:02,100
Привет! Это пример
2
00:00:02,100 --> 00:00:04,300
транскрибации.
```
Чтобы получить формат WebVTT вместо SRT, замените `response_format=srt` на `response_format=vtt`.
Когда запрашиваете субтитры (`srt` или `vtt`) через библиотеку `requests` в
Python, читайте ответ через `response.json()`, а **не** `response.text` — как и
для остальных форматов ответа. Подробнее — на странице [форматы
ответа](/response-formats).
## Как формируются субтитры [#как-формируются-субтитры]
Nexara API формирует субтитры, удобные для чтения на экране:
* длинные фразы автоматически разбиваются на короткие реплики подходящей длины;
* время показа каждой реплики подбирается так, чтобы субтитры не мелькали слишком быстро и не задерживались слишком долго;
* соседние субтитры не накладываются друг на друга.
Поэтому разбивка на реплики в `srt`/`vtt` может отличаться от сегментов в `verbose_json` — это ожидаемо и сделано ради читаемости.
## Субтитры с диаризацией [#субтитры-с-диаризацией]
Если добавить `task=diarize`, в субтитрах перед каждой репликой появится метка говорящего (`Speaker 0`, `Speaker 1`) — или [роль](/speakers-roles), если включена разметка ролей.
```python
subtitles = client.transcriptions.create(
file="meeting.mp4",
task="diarize",
response_format="srt",
)
```
```typescript
const subtitles = await client.transcriptions.create({
file: "meeting.mp4",
task: "diarize",
response_format: "srt",
});
```
```bash
curl https://api.nexara.ru/v1/audio/transcriptions \
-H "Authorization: Bearer nx-XXXXXXXXXXXXXXXXXXXXXXXX" \
-F "file=@meeting.mp4" \
-F "model=whisper-1" \
-F "task=diarize" \
-F "response_format=srt"
```
Каждая реплика в субтитрах подписана говорящим:
```text
1
00:00:00,000 --> 00:00:02,100
Speaker 0: Здравствуйте! Чем могу помочь?
2
00:00:02,500 --> 00:00:05,000
Speaker 1: Хочу оформить возврат.
```
## Что учитывать [#что-учитывать]
* **Хранение ключа.** Не вставляйте API-ключ прямо в код. Храните его в переменной окружения (например, `export NEXARA_API_KEY="nx-..."`) и ссылайтесь на неё. Подробнее — на странице [аутентификации](/authentication#curl).
# Поддерживаемые файлы (/supported-files)
Nexara API для, в частности `/audio/transcriptions`, принимает файлы с различными MIME типами, представляющими аудио- и видеоформаты.
При отправке файла через `multipart/form-data` убедитесь, что `Content-Type`, связанный с частью файла, содержит правильный формат.
Ниже приведен список MIME типов, поддерживаемых в настоящее время:
**Аудиоформаты:**
* **WAV:**
* `audio/wav`
* `audio/x-wav`
* `audio/wave`
* **MP3:**
* `audio/mp3`
* `audio/mpeg`
* `audio/mpg`
* `audio/x-mpeg`
* **M4A / AAC:**
* `audio/x-m4a`
* `audio/mp4`
* `audio/mp4a-latm`
* `audio/mpeg4`
* `audio/aac`
* `audio/vnd.dlna.adts`
* `audio/x-aac`
* **FLAC:**
* `audio/flac`
* **OGG (Vorbis/Opus):**
* `audio/ogg`
* `application/ogg`
* `audio/oga`
* **Opus:**
* `audio/opus`
* **AIFF:**
* `audio/aiff`
* `audio/x-aiff`
* **ASF:**
* `audio/asf`
* **WebM:**
* `audio/webm`
**Видеоформаты (аудио будет извлечено):**
* **MP4:**
* `video/mp4`
* **MOV:**
* `video/quicktime`
* **AVI:**
* `video/x-msvideo`
* **MKV:**
* `video/x-matroska`
* **WebM:**
* `video/webm`
Если есть конкретный MIME тип, поддержку которого вы хотели бы видеть, пожалуйста, напишите в [Поддержку](https://t.me/RND_RandoM).
# Синхронная и асинхронная обработка (/sync-async)
Nexara API предлагает два режима обработки аудио. Они принимают одни и те же параметры, но по-разному возвращают результат: синхронный режим возвращает готовую транскрипцию прямо в ответе, держа соединение открытым все время обработки, а асинхронный — сразу возвращает идентификатор задачи, результат которой вы забираете позже.
`POST /v1/audio/transcriptions` — соединение остаётся открытым, пока
файл обрабатывается, и результат приходит в теле ответа. Подходит для
коротких аудио.
`POST /v1/audio/transcriptions/async` — запрос сразу возвращает
`job_id`, а обработка идёт в фоне. Готовый результат забирается опросом по
`job_id`. Подходит для длинных файлов и пакетной обработки.
## Синхронная обработка [#синхронная-обработка]
Отправьте аудио на `POST /v1/audio/transcriptions`. Соединение удерживается до конца обработки, после чего в ответе возвращается готовый текст.
```python
from nexara import Nexara
client = Nexara() # ключ из переменной окружения NEXARA_API_KEY
result = client.transcriptions.create(file="audio.mp3")
print(result.text)
```
```typescript
import { Nexara } from "nexara-sdk";
const client = new Nexara(); // ключ из переменной окружения NEXARA_API_KEY
const result = await client.transcriptions.create({ file: "audio.mp3" });
console.log(result.text);
```
```bash
curl https://api.nexara.ru/v1/audio/transcriptions \
-H "Authorization: Bearer nx-XXXXXXXXXXXXXXXXXXXXXXXX" \
-F "file=@audio.mp3" \
-F "model=whisper-1"
```
Ответ содержит результат сразу:
```json
{
"text": "Привет! Это пример транскрибации."
}
```
Синхронный режим — это самый простой способ использовать API: один запрос, один ответ. Он оптимален для коротких записей, где время обработки невелико.
Для длинных файлов синхронный запрос держит HTTP-соединение открытым всё время
обработки, что чувствительно к сетевым таймаутам на стороне клиента и прокси.
Для продолжительного аудио рекомендуется использовать асинхронный режим.
## Асинхронная обработка [#асинхронная-обработка]
Асинхронный режим разбивает работу на два шага: постановку задачи и получение результата.
### Шаг 1. Поставить задачу [#шаг-1-поставить-задачу]
Отправьте аудио на `POST /v1/audio/transcriptions/async`. Запрос принимает те же параметры, что и синхронный, но возвращается сразу, не дожидаясь обработки.
```python
job = client.transcriptions.create_job(file="long-audio.mp3")
print(job.job_id, job.status) # 5f1c2e3a-... in_progress
```
```typescript
const job = await client.transcriptions.createJob({ file: "long-audio.mp3" });
console.log(job.job_id, job.status); // 5f1c2e3a-... in_progress
```
```bash
curl https://api.nexara.ru/v1/audio/transcriptions/async \
-H "Authorization: Bearer nx-XXXXXXXXXXXXXXXXXXXXXXXX" \
-F "file=@long-audio.mp3" \
-F "model=whisper-1"
```
В ответ приходит идентификатор задачи и её статус:
```json
{
"job_id": "5f1c2e3a-...",
"status": "in_progress",
"created_at": "2026-07-13T10:00:00+00:00"
}
```
Сохраните `job_id` — по нему вы будете забирать результат.
### Шаг 2. Опросить статус [#шаг-2-опросить-статус]
Отправляйте запросы на `GET /v1/audio/transcriptions/async/{job_id}`, пока задача не завершится.
В [Python SDK](/python-sdk) опрос делает `job.wait()` — он возвращает готовый результат, когда задача завершится. Проверить статус вручную можно через `retrieve_job()`.
```python
result = job.wait()
print(result.text)
```
В [TypeScript SDK](/typescript-sdk) опрос делает `job.wait()` — он возвращает готовый результат, когда задача завершится. Проверить статус вручную можно через `retrieveJob()`.
```typescript
const result = await job.wait();
console.log((result as { text: string }).text);
```
```bash
curl https://api.nexara.ru/v1/audio/transcriptions/async/5f1c2e3a-... \
-H "Authorization: Bearer nx-XXXXXXXXXXXXXXXXXXXXXXXX"
```
Так выглядит ответ API пока задача выполняется:
```json
{
"job_id": "5f1c2e3a-...",
"status": "in_progress",
"created_at": "2026-07-13T10:00:00+00:00",
"completed_at": null,
"result": null,
"error": null
}
```
А так — когда ответ получен:
```json
{
"job_id": "5f1c2e3a-...",
"status": "complete",
"created_at": "2026-07-13T10:00:00+00:00",
"completed_at": "2026-07-13T10:01:12+00:00",
"result": {
"text": "Привет! Это пример транскрибации."
},
"error": null
}
```
После завершения в поле `result` появляется готовый текст расшифровки. Поле `result` может содержать любые [форматы ответа](/response-formats).
После генерации ответа, результат хранится на сервере в течение 12 часов, а
потом удаляется безвозвратно.
### Статусы задачи [#статусы-задачи]
* `in_progress`: задача поставлена в очередь или обрабатывается. Поле `result` пустое;
* `complete`: обработка завершена успешно. Результат находится в поле `result`, а время завершения — в `completed_at`.
* `error`: обработка завершилась ошибкой. Описание причины находится в поле `error`.
## Что выбрать [#что-выбрать]
для коротких записей, интерактивных сценариев и быстрой интеграции, когда
важно получить результат одним запросом.
для длинных файлов и пакетной обработки, чтобы не держать открытым
HTTP-соединение и не упираться в клиентские таймауты.
Оба режима ограничены частотой **10 запросов в секунду**. Для асинхронного
режима действует дополнительный лимит — не более **200** одновременно
выполняющихся задач на один API-ключ. Подробнее читайте на странице
[лимитов](/docs/limits).
# Постобработка текста (/text-postprocessing)
# Временные метки (/timestamps)
По умолчанию API возвращает временные метки на уровне **сегментов** — крупных фрагментов речи. Но можно запросить метки на уровне каждого слова: тогда для любого слова будет известно точное время начала и конца. Это удобно для субтитров, поиска по аудио и синхронизации текста со звуком.
## Как получить временные метки слов [#как-получить-временные-метки-слов]
Передайте два параметра:
* `timestamp_granularities[]=word` — уровень детализации меток;
* `response_format=verbose_json` — подробный формат ответа, в котором содержится массив слов.
```python
from nexara import Nexara
client = Nexara() # ключ из переменной окружения NEXARA_API_KEY
result = client.transcriptions.create(
file="audio.mp3",
response_format="verbose_json",
timestamp_granularities=["word"],
)
for word in result.words:
print(f"{word.start:.2f}–{word.end:.2f}: {word.word}")
```
```typescript
import { Nexara } from "nexara-sdk";
const client = new Nexara(); // ключ из переменной окружения NEXARA_API_KEY
const result = await client.transcriptions.create({
file: "audio.mp3",
response_format: "verbose_json",
timestamp_granularities: ["word"],
});
for (const word of result.words ?? []) {
console.log(`${word.start.toFixed(2)}–${word.end.toFixed(2)}: ${word.word}`);
}
```
```bash
curl https://api.nexara.ru/v1/audio/transcriptions \
-H "Authorization: Bearer nx-XXXXXXXXXXXXXXXXXXXXXXXX" \
-F "file=@audio.mp3" \
-F "model=whisper-1" \
-F "response_format=verbose_json" \
-F "timestamp_granularities[]=word"
```
Временные метки слов возвращаются только в формате `verbose_json`. В форматах
`json` и `text` массив слов не передаётся.
## Структура ответа [#структура-ответа]
В ответе появляется массив `words`, где для каждого слова указаны время начала (`start`), время конца (`end`) в секундах и уверенность модели (`prob`):
```json
{
"task": "transcribe",
"text": "Привет мир",
"words": [
{ "word": "Привет", "start": 0.0, "end": 0.42, "prob": 0.99 },
{ "word": "мир", "start": 0.42, "end": 0.88, "prob": 0.98 }
],
...
}
```
## Уровни детализации [#уровни-детализации]
Метки на уровне крупных фрагментов речи. Подходит для большинства сценариев
и работает без дополнительных параметров.
Временные метки для каждого отдельного слова. Полезно для генерации
субтитров.
## Временные метки и диаризация [#временные-метки-и-диаризация]
При диаризации (`task=diarize`) временные метки слов дополнительно содержат идентификатор или роль говорящего в поле `speaker`.
Пример вывода:
```json
{
"task": "diarize",
"words": [
{
"word": "Здравствуйте",
"start": 0.0,
"end": 0.8,
"prob": 0.99,
"speaker": "speaker_0"
},
{
"word": "Слушаю",
"start": 1.2,
"end": 1.7,
"prob": 0.97,
"speaker": "speaker_1"
}
],
...
}
```
Если включена [разметка
ролей](/diarization#%D1%80%D0%B0%D0%B7%D0%BC%D0%B5%D1%82%D0%BA%D0%B0-%D1%80%D0%BE%D0%BB%D0%B5%D0%B9-role-tagging),
метки `speaker_N` в словах заменяются на назначенные роли (например, `Клиент`,
`Агент`).
# TypeScript SDK (/typescript-sdk)
**`nexara-sdk`** — официальная TypeScript/JavaScript-библиотека для Nexara API. Она берёт на себя сборку запросов, разбор ответов в типизированные объекты, обработку ошибок и повторные попытки, а асинхронные задачи превращает в один вызов `job.wait()`. Библиотека полностью типизирована и поставляет собственные `.d.ts`, тип результата сужается автоматически по `task` и `response_format`. Требуется Node.js 20+.
```bash
npm install nexara-sdk
```
Сохраните API-ключ в переменной окружения `NEXARA_API_KEY`. SDK читает её автоматически при создании клиента, поэтому ключ не нужно указывать в коде — и он не попадёт в репозиторий:
```bash
export NEXARA_API_KEY=nx-...
```
Команда `export` задаёт переменную только для текущей сессии терминала. Чтобы
ключ сохранялся между сессиями, добавьте эту строку в файл конфигурации вашей
оболочки (например, `~/.zshrc` или `~/.bashrc`). На сервере переменную удобнее
задавать через настройки окружения или менеджер секретов.
## Быстрый старт [#быстрый-старт]
```typescript
import { Nexara } from "nexara-sdk";
const client = new Nexara(); // ключ из переменной окружения NEXARA_API_KEY
const { text } = await client.transcriptions.create({ file: "audio.mp3" });
console.log(text);
```
Клиент берёт ключ из `NEXARA_API_KEY` — той самой переменной, что вы задали выше, — поэтому в коде его указывать не нужно. Передать ключ явным полем `apiKey` тоже можно, но не храните его в коде и не коммитьте в репозиторий.
## Клиент [#клиент]
```typescript
const client = new Nexara({
// apiKey по умолчанию берётся из переменной окружения NEXARA_API_KEY,
// либо можете передать ключ сами: apiKey: "nx-..."
timeoutMs: 600_000, // таймаут запроса в миллисекундах
maxRetries: 2, // повторы при 429 и сетевых сбоях
});
```
Клиент асинхронный целиком — отдельного синхронного варианта нет (в отличие от `Nexara`/`AsyncNexara` в [Python SDK](/python-sdk)). Все методы возвращают промисы.
## Транскрибация [#транскрибация]
Передайте ровно один из параметров: `file` (путь, `Uint8Array` или `Blob`) или `url` (прямая ссылка). Файл, переданный путём, стримится с диска и не загружается в память целиком.
```typescript
// Локальный файл
const local = await client.transcriptions.create({ file: "audio.mp3" });
console.log(local.text);
// Файл по ссылке
const remote = await client.transcriptions.create({
url: "https://example.com/audio.mp3",
});
console.log(remote.text);
```
Параметры те же, что и у [API](/api-reference/create_transcription_audio_transcriptions_post): `model`, `language`, `response_format`, `timestamp_granularities`, `profanity_filter` и другие. Тип результата сужается автоматически: `text`, `srt` и `vtt` возвращаются строкой, `json` и `verbose_json` — типизированными объектами:
```typescript
import { writeFile } from "node:fs/promises";
const srt = await client.transcriptions.create({
file: "video.mp4",
response_format: "srt",
});
await writeFile("video.srt", srt);
const verbose = await client.transcriptions.create({
file: "audio.mp3",
response_format: "verbose_json",
timestamp_granularities: ["word"],
});
for (const word of verbose.words ?? []) {
console.log(`${word.start.toFixed(2)}–${word.end.toFixed(2)}: ${word.word}`);
}
```
## Диаризация и роли [#диаризация-и-роли]
Разделение по говорящим включается так же, как в API, — `task: "diarize"`. Роли передаются обычным массивом или объектом: SDK сам сериализует их в JSON.
```typescript
const call = await client.transcriptions.create({
file: "call.mp3",
task: "diarize",
num_speakers: 2,
roles: ["Оператор", "Клиент"], // или "auto", или { "Роль": "описание" }
});
for (const segment of call.segments) {
console.log(`${segment.speaker}: ${segment.text}`);
}
```
Подробнее о режимах — на странице [спикеры и роли](/speakers-roles).
## Эмоции [#эмоции]
Флаг `emotions: true` добавляет к каждому сегменту диаризации объект `emotion`: метку `label` (`angry`, `sad`, `neutral` или `positive`), уверенность модели `confidence` и распределение вероятностей `probs`.
```typescript
const call = await client.transcriptions.create({
file: "call.mp3",
task: "diarize",
model: "nexara-ru",
emotions: true,
});
for (const segment of call.segments) {
if (segment.emotion) {
const confidence = Math.round(segment.emotion.confidence * 100);
console.log(`${segment.speaker}: ${segment.emotion.label} (${confidence}%) — ${segment.text}`);
} else {
console.log(`${segment.speaker}: эмоция не определена — ${segment.text}`);
}
}
```
Эмоция приходит не для каждого сегмента: если модель не смогла оценить фрагмент, поле `emotion` будет `undefined`. Проверяйте его перед чтением, как в примере выше. У слов (`words`) эмоции нет — она считается по сегменту целиком.
Метки доступны типом `EmotionLabel`, а сам объект — типом `Emotion`:
```typescript
import type { Emotion, EmotionLabel } from "nexara-sdk";
```
Подробнее об условиях и ограничениях — на странице [распознавание эмоций](/emotions).
Распознавание эмоций тарифицируется дополнительно к стоимости диаризации.
Если ни один сегмент не удалось оценить, надбавка не списывается. Стоимость
услуги смотрите на странице [тарифов](/pricing).
## Асинхронные задачи [#асинхронные-задачи]
Для длинных записей используйте [асинхронный режим](/async-transcription): `createJob()` ставит задачу и сразу возвращает объект `Job`, а `job.wait()` опрашивает статус и возвращает готовый результат.
```typescript
const job = await client.transcriptions.createJob({
file: "long_recording.mp3",
});
console.log(job.job_id, job.status); // in_progress
const result = await job.wait(); // опрашивает статус; таймаут по умолчанию — 1_800_000 мс
console.log((result as { text: string }).text);
```
Задачу можно забрать позже — в том числе из другого процесса:
```typescript
const job = await client.transcriptions.retrieveJob(jobId);
if (job.status === "complete") {
console.log(job.result);
}
```
Что важно знать:
* Результат хранится **12 часов** с момента создания задачи, затем удаляется — после этого `retrieveJob()` бросит `NotFoundError`.
* Одновременно на один ключ может выполняться до **200** задач.
* Если задача завершилась ошибкой, `wait()` бросает `JobFailedError`. Неудавшаяся задача **не тарифицируется** — повторная отправка бесплатна.
* Если задача не успела за таймаут, `wait()` бросает `JobTimeoutError`; сама задача при этом не отменяется, её можно забрать позже через `retrieveJob()`.
## LLM-анализ [#llm-анализ]
Передайте `prompt`, чтобы прогнать расшифровку через языковую модель, и `json_schema` — чтобы получить [структурированный ответ](/llm-usage). Схема передаётся обычным объектом, а `llm_output` возвращается уже разобранным — никакого ручного `JSON.parse`.
```typescript
const result = await client.transcriptions.create({
file: "meeting.mp3",
prompt: "Составь протокол встречи: резюме, решения и задачи.",
json_schema: {
type: "object",
properties: {
summary: { type: "string" },
decisions: { type: "array", items: { type: "string" } },
action_items: { type: "array", items: { type: "string" } },
},
required: ["summary", "decisions", "action_items"],
},
});
console.log(result.llm_output); // объект по вашей схеме
console.log(result.transcription.text); // расшифровка, из которой он получен
```
Без `json_schema` поле `llm_output` — строка со свободным ответом модели.
## Параллельные запросы [#параллельные-запросы]
Клиент асинхронный целиком — «async» здесь означает промисы. Отдельного синхронного клиента нет, поэтому несколько запросов запускаются обычным `Promise.all`:
```typescript
const urls = [
"https://example.com/a.mp3",
"https://example.com/b.mp3",
"https://example.com/c.mp3",
];
const results = await Promise.all(
urls.map((url) => client.transcriptions.create({ url })),
);
for (const result of results) {
console.log(result.text);
}
```
## Ошибки [#ошибки]
Некорректные запросы (например, `file` и `url` одновременно, слишком много ролей) отклоняются на стороне клиента с `NexaraValidationError` — ещё до обращения к серверу. Ошибки сервера превращаются в типизированные классы по коду статуса:
| Класс | Код | Когда возникает |
| -------------------------- | --- | ---------------------------------------------------- |
| `BadRequestError` | 400 | Некорректные параметры запроса |
| `InsufficientBalanceError` | 402 | Недостаточно средств на балансе |
| `AuthenticationError` | 403 | Ключ не передан или недействителен |
| `NotFoundError` | 404 | Задача, модель или ключ не найдены |
| `RateLimitError` | 429 | Превышен лимит частоты или число одновременных задач |
| `InternalServerError` | 500 | Внутренняя ошибка сервера |
| `APIConnectionError` | — | Запрос не дошёл до сервера (сеть, таймаут) |
Все они наследуются от `NexaraError`, а у ошибок API есть поля `status_code` и `detail` с сообщением сервера. Тип ошибки проверяется через `instanceof`. Полный список кодов — на странице [ошибок](/errors).
```typescript
import { Nexara, InsufficientBalanceError, RateLimitError } from "nexara-sdk";
const client = new Nexara();
try {
await client.transcriptions.create({ file: "audio.mp3" });
} catch (e) {
if (e instanceof InsufficientBalanceError) {
console.log("Пополните баланс:", e.detail); // 402
} else if (e instanceof RateLimitError) {
console.log("Слишком много запросов, попробуйте позже.");
} else {
throw e;
}
}
```
## Ссылки [#ссылки]
* Пакет на npm: [npmjs.com/package/nexara-sdk](https://www.npmjs.com/package/nexara-sdk)
* Справочник параметров API: [референс API](/api-reference/create_transcription_audio_transcriptions_post)
# Учёт расходов (/usage-tracking)
# Zoom встречи (/zoom)
Записи Zoom-встреч и онлайн-переговоров — частый сценарий: из одной записи можно получить расшифровку, разделение по участникам и готовый протокол встречи. Ниже разберём блок за блоком, а в конце соберём всё в один запрос и поделимся [лайфхаками](#лайфхаки).
## Разделение на говорящих [#разделение-на-говорящих]
Включите диаризацию (`task=diarize`) и укажите `diarization_setting=telephonic`. Этот режим хорошо работает, где участники часто перебивают друг друга и говорят одновременно, как это обычно и бывает на встречах.
```python
from nexara import Nexara
client = Nexara() # ключ из переменной окружения NEXARA_API_KEY
result = client.transcriptions.create(
file="meeting.mp3",
task="diarize",
diarization_setting="telephonic",
)
```
```typescript
import { Nexara } from "nexara-sdk";
const client = new Nexara(); // ключ из переменной окружения NEXARA_API_KEY
const result = await client.transcriptions.create({
file: "meeting.mp3",
task: "diarize",
diarization_setting: "telephonic",
});
```
```bash
curl https://api.nexara.ru/v1/audio/transcriptions \
-H "Authorization: Bearer nx-XXXXXXXXXXXXXXXXXXXXXXXX" \
-F "file=@meeting.mp3" \
-F "model=whisper-1" \
-F "task=diarize" \
-F "diarization_setting=telephonic"
```
Модель `whisper-1` — универсальная и хорошо подходит для встреч на разных языках. Число участников определяется автоматически.
## Роли участников [#роли-участников]
Чтобы вместо безликих `speaker_0`/`speaker_1` в расшифровке были понятные роли, добавьте `roles=auto` — модель сама подберёт подписи по содержанию разговора (например, `Ведущий`, `Участник`). Если роли известны заранее, можно передать их списком.
```python
result = client.transcriptions.create(
file="meeting.mp3",
task="diarize",
diarization_setting="telephonic",
roles="auto",
)
```
```typescript
const result = await client.transcriptions.create({
file: "meeting.mp3",
task: "diarize",
diarization_setting: "telephonic",
roles: "auto",
});
```
```bash
curl https://api.nexara.ru/v1/audio/transcriptions \
-H "Authorization: Bearer nx-XXXXXXXXXXXXXXXXXXXXXXXX" \
-F "file=@meeting.mp3" \
-F "model=whisper-1" \
-F "task=diarize" \
-F "diarization_setting=telephonic" \
-F "roles=auto"
```
Подробнее о режимах разметки — на странице [спикеры и роли](/speakers-roles).
## Аналитика встречи [#аналитика-встречи]
Самое ценное — сразу получить из встречи структурированный результат. Добавьте `prompt` (и, для строгой структуры, `json_schema`). Модель получает расшифровку с ролями, поэтому учитывает, кто что говорил. Подробно про режимы — на странице [LLM-анализ](/llm-usage). Ниже — три частых сценария.
### Протокол встречи [#протокол-встречи]
Готовый протокол, в котором будут указаны краткое резюме, принятые решения и задачи с ответственными.
```json
{
"type": "object",
"properties": {
"summary": { "type": "string", "description": "Краткое резюме встречи" },
"decisions": {
"type": "array",
"items": { "type": "string" },
"description": "Принятые решения"
},
"action_items": {
"type": "array",
"items": {
"type": "object",
"properties": {
"task": { "type": "string" },
"owner": {
"type": "string",
"description": "Ответственный, если назван"
}
},
"required": ["task"]
}
}
},
"required": ["summary", "decisions", "action_items"]
}
```
Prompt: `Составь протокол встречи: резюме, решения и задачи с ответственными.`
### Краткое резюме [#краткое-резюме]
Самый простой вариант — без схемы, ответ придёт текстом.
Prompt: `Сделай краткое резюме встречи в пяти предложениях.`
### Список задач [#список-задач]
Только задачи, поставленные на встрече, — для трекера или таск-менеджера.
```json
{
"type": "object",
"properties": {
"action_items": {
"type": "array",
"items": {
"type": "object",
"properties": {
"task": { "type": "string" },
"owner": { "type": "string" },
"deadline": { "type": "string", "description": "Срок, если назван" }
},
"required": ["task"]
}
}
},
"required": ["action_items"]
}
```
Prompt: `Выпиши все задачи, поставленные на встрече, с ответственными и сроками.`
## Собираем всё вместе [#собираем-всё-вместе]
Один запрос: распознавание `whisper-1`, диаризация в режиме `telephonic`, авто-роли и протокол встречи через `prompt` + `json_schema`.
В [Python SDK](/python-sdk) схема передаётся обычным словарём — без ручной сериализации в JSON.
```python
from nexara import Nexara
client = Nexara() # ключ из переменной окружения NEXARA_API_KEY
result = client.transcriptions.create(
file="meeting.mp3",
task="diarize",
diarization_setting="telephonic",
roles="auto",
prompt="Составь протокол встречи: резюме, решения и задачи с ответственными.",
json_schema={
"type": "object",
"properties": {
"summary": {"type": "string"},
"decisions": {"type": "array", "items": {"type": "string"}},
"action_items": {
"type": "array",
"items": {
"type": "object",
"properties": {
"task": {"type": "string"},
"owner": {"type": "string"},
},
"required": ["task"],
},
},
},
"required": ["summary", "decisions", "action_items"],
},
)
print(result.llm_output) # готовый протокол встречи (dict)
```
В [TypeScript SDK](/typescript-sdk) схема передаётся обычным объектом — без ручной сериализации в JSON.
```typescript
import { Nexara } from "nexara-sdk";
const client = new Nexara(); // ключ из переменной окружения NEXARA_API_KEY
const result = await client.transcriptions.create({
file: "meeting.mp3",
task: "diarize",
diarization_setting: "telephonic",
roles: "auto",
prompt: "Составь протокол встречи: резюме, решения и задачи с ответственными.",
json_schema: {
type: "object",
properties: {
summary: { type: "string" },
decisions: { type: "array", items: { type: "string" } },
action_items: {
type: "array",
items: {
type: "object",
properties: {
task: { type: "string" },
owner: { type: "string" },
},
required: ["task"],
},
},
},
required: ["summary", "decisions", "action_items"],
},
});
console.log(result.llm_output); // готовый протокол встречи (объект)
```
```bash
curl https://api.nexara.ru/v1/audio/transcriptions \
-H "Authorization: Bearer nx-XXXXXXXXXXXXXXXXXXXXXXXX" \
-F "file=@meeting.mp3" \
-F "model=whisper-1" \
-F "task=diarize" \
-F "diarization_setting=telephonic" \
-F "roles=auto" \
-F "prompt=Составь протокол встречи: резюме, решения и задачи с ответственными." \
-F 'json_schema={"type":"object","properties":{"summary":{"type":"string"},"decisions":{"type":"array","items":{"type":"string"}},"action_items":{"type":"array","items":{"type":"object","properties":{"task":{"type":"string"},"owner":{"type":"string"}},"required":["task"]}}},"required":["summary","decisions","action_items"]}'
```
```python
import json
import requests
schema = {
"type": "object",
"properties": {
"summary": {"type": "string"},
"decisions": {"type": "array", "items": {"type": "string"}},
"action_items": {
"type": "array",
"items": {
"type": "object",
"properties": {
"task": {"type": "string"},
"owner": {"type": "string"},
},
"required": ["task"],
},
},
},
"required": ["summary", "decisions", "action_items"],
}
with open("meeting.mp3", "rb") as audio_file:
response = requests.post(
"https://api.nexara.ru/v1/audio/transcriptions",
headers={"Authorization": "Bearer nx-XXXXXXXXXXXXXXXXXXXXXXXX"},
files={"file": audio_file},
data={
"model": "whisper-1",
"task": "diarize",
"diarization_setting": "telephonic",
"roles": "auto",
"prompt": "Составь протокол встречи: резюме, решения и задачи с ответственными.",
"json_schema": json.dumps(schema),
},
)
result = response.json()
print(result["llm_output"])
```
Node.js 18+:
```javascript
import fs from "fs";
const schema = {
type: "object",
properties: {
summary: { type: "string" },
decisions: { type: "array", items: { type: "string" } },
action_items: {
type: "array",
items: {
type: "object",
properties: {
task: { type: "string" },
owner: { type: "string" },
},
required: ["task"],
},
},
},
required: ["summary", "decisions", "action_items"],
};
const form = new FormData();
form.append("file", new Blob([fs.readFileSync("meeting.mp3")]), "meeting.mp3");
form.append("model", "whisper-1");
form.append("task", "diarize");
form.append("diarization_setting", "telephonic");
form.append("roles", "auto");
form.append("prompt", "Составь протокол встречи: резюме, решения и задачи с ответственными.");
form.append("json_schema", JSON.stringify(schema));
const response = await fetch(
"https://api.nexara.ru/v1/audio/transcriptions",
{
method: "POST",
headers: { Authorization: "Bearer nx-XXXXXXXXXXXXXXXXXXXXXXXX" },
body: form,
},
);
const result = await response.json();
console.log(result.llm_output);
```
```go
package main
import (
"bytes"
"fmt"
"io"
"mime/multipart"
"net/http"
"os"
)
func main() {
schema := `{"type":"object","properties":{"summary":{"type":"string"},"decisions":{"type":"array","items":{"type":"string"}},"action_items":{"type":"array","items":{"type":"object","properties":{"task":{"type":"string"},"owner":{"type":"string"}},"required":["task"]}}},"required":["summary","decisions","action_items"]}`
file, _ := os.Open("meeting.mp3")
defer file.Close()
var buf bytes.Buffer
writer := multipart.NewWriter(&buf)
part, _ := writer.CreateFormFile("file", "meeting.mp3")
io.Copy(part, file)
writer.WriteField("model", "whisper-1")
writer.WriteField("task", "diarize")
writer.WriteField("diarization_setting", "telephonic")
writer.WriteField("roles", "auto")
writer.WriteField("prompt", "Составь протокол встречи: резюме, решения и задачи с ответственными.")
writer.WriteField("json_schema", schema)
writer.Close()
req, _ := http.NewRequest("POST", "https://api.nexara.ru/v1/audio/transcriptions", &buf)
req.Header.Set("Authorization", "Bearer nx-XXXXXXXXXXXXXXXXXXXXXXXX")
req.Header.Set("Content-Type", writer.FormDataContentType())
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
fmt.Println(string(body))
}
```
```ruby
require "net/http"
require "uri"
require "json"
schema = {
"type" => "object",
"properties" => {
"summary" => { "type" => "string" },
"decisions" => { "type" => "array", "items" => { "type" => "string" } },
"action_items" => {
"type" => "array",
"items" => {
"type" => "object",
"properties" => {
"task" => { "type" => "string" },
"owner" => { "type" => "string" },
},
"required" => ["task"],
},
},
},
"required" => ["summary", "decisions", "action_items"],
}
uri = URI("https://api.nexara.ru/v1/audio/transcriptions")
request = Net::HTTP::Post.new(uri)
request["Authorization"] = "Bearer nx-XXXXXXXXXXXXXXXXXXXXXXXX"
request.set_form(
[
["file", File.open("meeting.mp3")],
["model", "whisper-1"],
["task", "diarize"],
["diarization_setting", "telephonic"],
["roles", "auto"],
["prompt", "Составь протокол встречи: резюме, решения и задачи с ответственными."],
["json_schema", schema.to_json],
],
"multipart/form-data",
)
response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
http.request(request)
end
puts JSON.parse(response.body)["llm_output"]
```
В ответе придёт расшифровка с ролями и готовый протокол встречи:
```json
{
"transcription": {
"task": "diarize",
"segments": [
{
"start": 0.0,
"end": 3.4,
"text": "Давайте начнём. Сегодня обсудим релиз.",
"speaker": "Ведущий"
},
{
"start": 3.8,
"end": 7.2,
"text": "Я подготовлю тестовую сборку к пятнице.",
"speaker": "Участник"
}
]
},
"llm_output": {
"summary": "Команда обсудила предстоящий релиз и распределила задачи.",
"decisions": ["Выпустить релиз в конце недели"],
"action_items": [
{ "task": "Подготовить тестовую сборку", "owner": "Участник" }
]
}
}
```
## Лайфхаки [#лайфхаки]
* **Режим `telephonic`.** На встречах участники часто перебивают друг друга — этот режим справляется с такими записями лучше стандартного.
* **Авто-роли.** На встречах состав участников заранее не известен, поэтому `roles=auto` обычно удобнее фиксированного списка, но если у вас есть список, лучше его передать для лучшего качества.
* **Длинные встречи — [асинхронный режим](/async-transcription).** Записи совещаний бывают долгими, не держите HTTP-соединение открытым всё это время.
* **Большие файлы — [по ссылке](/file-url).** Запись встречи удобно отдавать по URL (например, pre-signed ссылке на хранилище), а не загружать файлом.
* **Вложенные схемы.** Для задач с ответственными используйте массив объектов в `json_schema` — так каждую задачу можно сразу разложить на поля.
Диаризация, разметка ролей и LLM-анализ тарифицируются дополнительно к
стоимости распознавания. Итоговую стоимость смотрите на странице
[тарифов](/pricing).
# Создать асинхронную задачу транскрибации (/api-reference/createAsyncTranscription)
{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */}
# Транскрибировать аудио (/api-reference/create_transcription_audio_transcriptions_post)
{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */}
# Получить статус асинхронной задачи (/api-reference/getAsyncTranscription)
{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */}
# Получить баланс (/api-reference/getBalance)
{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */}
# Получить информацию о модели (/api-reference/getModel)
{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */}
# Список доступных моделей (/api-reference/listModels)
{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */}