Всё, что умеет сайт, — из вашего кода: конвертация, сжатие, нарезка, распознавание речи, импорт по ссылке. JSON по HTTPS, ключ в заголовке, вебхук по готовности.
Базовый адрес — https://convr.app/api/v1. Запросы и ответы — JSON в UTF-8, у запросов с телом нужен заголовок Content-Type: application/json. Время — RFC 3339 в UTC, размеры — в байтах, длительности — в секундах.
Обычный сценарий:
создать задание (POST /jobs) — с именем и размером файла или со ссылкой;
загрузить файл напрямую в хранилище по выданной ссылке и сообщить POST /jobs/{id}/uploaded;
дождаться state: "done" — вебхуком или опросом GET /jobs/{id};
получить ссылку на результат: GET /jobs/{id}/download.
Файлы не проходят через API-сервер: загрузка и скачивание идут напрямую в хранилище и CDN, поэтому размер файла ограничен только тарифом.
Быстрый старт
export CONVR_KEY=cvk_…
# 1. create a job: what into what, and the file's name and size
curl -s -X POST https://convr.app/api/v1/jobs -H "Authorization: Bearer $CONVR_KEY" -H "Content-Type: application/json" \
-d '{"converter":"mov-mp4","options":{"quality":"medium"},
"file":{"name":"clip.mov","size":52428800}}'
# → {"batch_id":"…","job":{"id":"JOB","state":"awaiting_input",…},
# "upload":{"put_url":"PUT_URL","headers":{"Content-Type":"video/quicktime"}}}
# 2. upload the file straight to storage (no API key here)
curl -X PUT "PUT_URL" -H "Content-Type: video/quicktime" --data-binary @clip.mov
# 3. start it
curl -s -X POST https://convr.app/api/v1/jobs/JOB/uploaded -H "Authorization: Bearer $CONVR_KEY"
# 4. poll until "state":"done", then take the result
curl -s https://convr.app/api/v1/jobs/JOB -H "Authorization: Bearer $CONVR_KEY"
curl -s https://convr.app/api/v1/jobs/JOB/download -H "Authorization: Bearer $CONVR_KEY"
# → {"url":"https://…","expires_at":"…"}
Ключ и доступ
Создайте ключ в личном кабинете и передавайте его в каждом запросе:
Authorization: Bearer cvk_…
ключ действует от имени аккаунта: те же лимиты тарифа, та же история файлов;
работает только на тарифах с API; если тариф закончился — 403 plan_required;
ключ показывается один раз, у нас хранится только его хеш; потеряли — отзовите и создайте новый;
до 10 ключей на аккаунт, отзыв действует мгновенно;
ключом нельзя управлять аккаунтом: вход, пароль, оплата, сами ключи — 403 not_for_keys.
Никогда не вставляйте ключ в код страницы или мобильного приложения: его оттуда достанут. Вызывайте API со своего сервера.
Лимиты
Частота: 120 запросов в минуту на ключ (с запасом на короткие всплески). Сверх — 429 rate_limited и заголовок Retry-After в секундах.
Тариф: размер файла, задания в сутки, одновременные задания, длительность видео, минуты распознавания речи и срок хранения результатов — как у вашего тарифа. Точные числа отдаёт GET /limits.
Ссылки на загрузку живут 15 минут, ссылки на результат — 30 минут; новую ссылку на результат можно получить в любой момент, пока файл хранится (expires_at задания).
Опрашивайте статус не чаще раза в 2–3 секунды или используйте вебхуки.
Объект задания
Задание — один файл, превращаемый в один результат. Его возвращают почти все методы.
Этап в работе: fetching, probing, converting, uploading.
progress
int
Прогресс, 0–100.
converter
string
Конвертер, например mov-mp4.
input_name
string
Имя исходного файла.
input_size
int
Размер исходника, байт (у ссылки — после скачивания).
output_name
string
Имя результата.
output_size
int
Размер результата, байт (когда готов).
error
string
Код причины при failed.
expires_at
time
Когда файлы будут удалены.
deleted
bool
Файлы уже удалены.
result
object
Данные анализа или распознавания (только у scan и transcribe).
input_ready
bool
Исходник сохранён, по нему можно запускать clips и transcribe.
Код
HTTP
Описание
awaiting_input
Ждёт загрузки файла.
queued
В очереди.
leased
Обрабатывается.
done
✓
Готово, можно скачать.
failed
✓
Не удалось, причина в error.
canceled
✓
Отменено.
expired
✓
Срок хранения истёк или файл так и не загрузили.
Конечные состояния отмечены ✓ — после них задание не меняется.
Конвертеры и настройки
Конвертер называется <из>-<в>: mov-mp4, mp4-mp3, png-webp, docx-pdf, pdf-jpg. Полный список с настройками каждого — GET /registry (сотни пар форматов видео, аудио, изображений и документов). Выбирайте конвертер по расширению исходного файла.
options — объект «ключ: значение». Всё необязательно, пропущенное берёт значение по умолчанию; неизвестный ключ или значение вне списка — 400 invalid_options. Типы настроек в /registry:
Тип
Описание
enum
string
Одно из values, например "1080".
int
number
Целое от min до max.
bool
boolean
true или false.
time
number | string
Секунды (12.5) или "мм:сс" / "чч:мм:сс".
text
string
Текст до max символов.
ranges
string
Отрезки "начало-конец" в секундах через запятую: "12-27,40.5-70".
asset
string
id картинки из POST /assets (водяной знак).
options
Тип
Описание
quality
enum
Видео: high · medium · small.
target_mb
enum
Видео: сжать до размера, МБ — 8, 10, 16, 25, 50…
resolution
enum
Видео: высота кадра — keep, 2160 … 360.
aspect / fit
enum
Видео: 9:16, 4:5, 1:1, 16:9; поля blur, обрезка crop или smart (по лицам).
trim_start / trim_end
time
Видео и аудио: обрезать начало и конец.
vcodec / fps
enum
Видео: h264 или h265; частота кадров.
no_audio / volume
bool / int
Убрать звук; громкость 0–300 %.
auto_subs / sub_lang / sub_style
bool / enum
Видео: распознать речь и вжечь субтитры; язык; стиль (tiktok, karaoke, beast, lyrics…).
title / wm_text / wm_image
text / asset
Видео: заголовок, текстовый или картинка-водяной знак.
bitrate / sample_rate / channels
enum
Аудио: 128–320 кбит/с; 44100/48000; mono/stereo.
resize / image_quality / strip
enum / int / bool
Изображения: ширина; качество 1–100; убрать EXIF.
pages / dpi
enum
PDF в картинки: all или first; 72/150/300.
gif_width / gif_fps
enum
GIF: ширина и частота кадров.
Задания
POST/jobs
Создать задание
Передайте либо file (затем загрузите его по upload), либо url — тогда файл скачаем мы и задание сразу встанет в очередь.
В ответе upload — одно из двух:
put_url и headers — файл до 64 МБ загружается одним PUT с этими заголовками;
part_size, part_count, parts — файл больше загружается частями по part_size байт (последняя короче); в parts первые 50 ссылок, остальные — через POST /jobs/{id}/parts.
Для url блок upload пуст.
Тело запроса (JSON)
Тип
Описание
converterобязательно
string
Конвертер из /registry, например mov-mp4.
options
object
Настройки конвертера.
file.name
string
Имя файла (до 1024 символов). С file.size — вместо url.
file.size
int
Точный размер файла в байтах.
url
string
Ссылка вместо файла: прямая, Google Drive, Dropbox, YouTube, TikTok, Instagram, Vimeo, X.
webhook
string
Куда прислать POST, когда задание завершится.
save_to_drive
bool
Положить результат в облако аккаунта, когда он будет готов.
drive_folder
string
Папка облака для результата (по умолчанию — верхний уровень).
batch_id
string
Добавить задание в существующий пакет (для ZIP и событий).
unknown_converter400 Нет такого конвертера — смотрите GET /registry.
invalid_options400 Неизвестная настройка или недопустимое значение.
invalid_file400 Не указаны имя или размер файла.
invalid_url400 Ссылка не http(s) или вместе с ней передан файл.
invalid_webhook400 Адрес вебхука не http(s).
file_too_large413 Файл больше, чем позволяет тариф (limit — в байтах).
limit_active429 Слишком много заданий выполняется одновременно.
limit_daily429 Исчерпан суточный лимит заданий.
speech_limit429 Исчерпаны минуты распознавания речи за 30 дней.
links_unavailable503 Скачивание с видеосайтов временно недоступно.
PUT{put_url}
Загрузить файл
Запрос идёт напрямую в хранилище — без ключа API, с заголовками из upload.headers. Тело — сам файл, ровно file.size байт. Ссылка действует 15 минут.
При загрузке частями: PUT каждой части по её url, тело — part_size байт (последняя — остаток). Части можно грузить параллельно и в любом порядке; собирать их не нужно.
Пример
curl -X PUT "$PUT_URL" -H "Content-Type: video/mp4" --data-binary @video.mp4
# parts: split -b 16777216 big.mov part_ && upload part_aa to parts[0].url, …
curl -X PUT "$PART_1_URL" --data-binary @part_aa
POST/jobs/{id}/parts
Ещё ссылки на части
Подписанные ссылки для частей с from по to включительно, не больше 50 за раз.
Каждое задание входит в пакет (batch_id в ответе на создание). Передайте тот же batch_id в следующие POST /jobs, чтобы следить за ними одним потоком событий или упаковать в ZIP.
Server-Sent Events: сначала snapshot со всеми заданиями, затем job при каждом изменении. Соединение держится открытым; после обрыва переподключайтесь — снова придёт snapshot.
Создаёт задание-архив из всех готовых результатов пакета (от двух). Повторный вызов для того же набора вернёт тот же архив.
Ответ
{ "id": "…", "state": "…", … } // the job object
Ошибки
nothing_to_zip400 В пакете меньше двух готовых результатов.
zip_too_large413 Архив вышел бы слишком большим.
Облако
Файлы аккаунта без срока хранения, в папках, в пределах места тарифа. Загрузка — как у заданий: ссылка на PUT или части. Файл из облака конвертируется без повторной загрузки, результат ложится в ту же папку.
GET/drive
Содержимое папки
Папки и файлы, путь к папке, занятое место и квота.
file_too_large413 Файл больше, чем позволяет тариф (limit — в байтах).
POST/drive/files/{id}/convert
Конвертировать из облака
Создаёт задание, которое читает файл прямо из облака; результат кладётся в папку файла (save: false — не класть).
Тело запроса (JSON)
Тип
Описание
converterобязательно
string
Конвертер для формата файла.
options
object
Настройки.
folder
string
Другая папка для результата.
Ответ
{ "id": "…", "state": "…", … } // the job object
POST/jobs/{id}/drive
Результат — в облако
Копирует готовый результат задания в облако (внутри хранилища, без скачивания).
Тело запроса (JSON)
Тип
Описание
folder
string
Папка.
Ошибки
drive_full
not_ready409 Результата ещё нет: задание не в состоянии done.
GET/drive/files/{id}/url
Ссылка на скачивание
Подписанная ссылка CDN на время.
Ответ
{ "url": "https://…", "expires_at": "…" }
PATCH/drive/files/{id}
Переименовать или переместить
name и/или folder ("" — верхний уровень). Папки — PATCH /drive/folders/{id} с name и parent.
DELETE/drive/files/{id}
Удалить файл
Сразу освобождает место. Папка удаляется вместе с содержимым: DELETE /drive/folders/{id}.
Клипы и речь
Видео загружается один раз как задание-анализ <формат>-scan, после чего из него можно сколько угодно раз резать клипы и распознавать речь — без повторной загрузки, пока задание хранится.
POST/jobs {"converter":"mp4-scan"}
Анализ видео
Обычное создание задания с конвертером mp4-scan (mov-scan, mkv-scan…). Результат анализа — в поле result готового задания: громкость, смены сцен и лучшие моменты для клипов по 15, 30, 60 и 90 секунд с оценкой 0–100. Анализ загруженного файла не расходует суточный лимит заданий.
{id} — задание scan. Один отрезок даёт MP4, несколько — ZIP с клипами. Доступны настройки видео: aspect (по умолчанию 9:16), fit, quality, заголовок, водяной знак, фильтры, музыка.
Тело запроса (JSON)
Тип
Описание
options.rangesобязательно
ranges
До 20 отрезков, каждый 1–600 с.
options.*
Остальные настройки конвертера <формат>-clips из /registry.
у job.done есть download_url — ссылка на результат на 30 минут; позже берите новую через /download;
ответьте любым 2xx за 10 секунд. Иначе повторим: через 10 с, 40 с, ~3 мин, ~11 мин и ~43 мин — всего 6 попыток;
адрес должен быть публичным: внутренние сети и редиректы не поддерживаются;
одно событие может прийти дважды (например, при сетевом сбое после вашего ответа) — проверяйте job.id.
Каждый вызов подписан: X-Convr-Signature: sha256=<hex> — HMAC-SHA256 сырого тела запроса с секретом whsec_… из кабинета. Проверяйте подпись до разбора JSON и сравнивайте за постоянное время.
import hashlib, hmac, os
from flask import Flask, abort, request
SECRET = os.environ["CONVR_WEBHOOK_SECRET"].encode()
app = Flask(__name__)
@app.post("/hooks/convr")
def convr_hook():
sig = "sha256=" + hmac.new(SECRET, request.get_data(), hashlib.sha256).hexdigest()
if not hmac.compare_digest(sig, request.headers.get("X-Convr-Signature", "")):
abort(401)
e = request.get_json()
if e["event"] == "job.done":
print(e["job"]["id"], e["download_url"])
return "", 204
Ошибки
Ошибка запроса — HTTP-статус 4xx/5xx и тело {"error": "код"}. У ошибок лимитов есть ещё limit — число, в которое вы упёрлись. Опирайтесь на код, а не на текст: коды не меняются.
HTTP/1.1 429 Too Many Requests
{"error": "limit_active", "limit": 10}
Код
HTTP
Описание
invalid_key
401
Ключ не найден, отозван или аккаунт отключён.
plan_required
403
Тариф аккаунта без API.
not_for_keys
403
Этот метод доступен только на сайте.
rate_limited
429
Больше 120 запросов в минуту. Подождите Retry-After секунд.
limit_active
429
Слишком много заданий выполняется одновременно.
limit_daily
429
Исчерпан суточный лимит заданий.
speech_limit
429
Исчерпаны минуты распознавания речи за 30 дней.
file_too_large
413
Файл больше, чем позволяет тариф (limit — в байтах).
json_required
415
Нет заголовка Content-Type: application/json.
bad_json
400
Тело — не JSON или поля не того типа.
unknown_converter
400
Нет такого конвертера — смотрите GET /registry.
invalid_options
400
Неизвестная настройка или недопустимое значение.
invalid_file
400
Не указаны имя или размер файла.
invalid_url
400
Ссылка не http(s) или вместе с ней передан файл.
invalid_webhook
400
Адрес вебхука не http(s).
not_found
404
Задания нет или оно не ваше.
already_uploaded
409
Задание уже запущено.
upload_incomplete
400
Файл (или часть) не загружен целиком.
upload_size_mismatch
400
Загружено не столько байт, сколько указано в file.size.
not_ready
409
Результата ещё нет: задание не в состоянии done.
links_unavailable
503
Скачивание с видеосайтов временно недоступно.
speech_unavailable
503
Распознавание речи временно недоступно.
internal
500
Сбой на нашей стороне. Повторите позже.
Если задание завершилось с state: "failed", причина — в поле error задания:
Код
Описание
input_invalid
Файл повреждён или не читается.
input_format_mismatch
Внутри файла другой формат, чем у конвертера.
input_no_stream
Нет нужной дорожки (например, звука для mp4-mp3).
input_encrypted
Архив защищён паролем.
input_too_long
Видео длиннее, чем позволяет тариф.
input_too_large
Скачанный по ссылке файл больше лимита тарифа.
input_resolution
Слишком большое разрешение.
target_too_small
Видео не уместить в target_mb даже с минимальным качеством.
output_too_large
Результат вышел больше лимита.
convert_failed
Конвертация не удалась (после повторов на разных серверах).
timeout
Обработка заняла слишком долго.
url_unreachable
Ссылка не открывается.
url_blocked
Ссылка ведёт во внутреннюю сеть — запрещено.
no_video
По ссылке нет видео (пост с фото).
url_private
Видео приватное, удалено, с возрастным или региональным ограничением.
site_blocked
Сайт временно не отдаёт видео. Повторите позже.
fetch_failed
Не удалось получить загруженный файл.
speech_too_long
Больше 20 минут речи в одном задании.
worker_lost
Сервер обработки пропал; задание обычно перезапускается само.