Documentação da API e do MCP
Transcreva áudio a partir do seu código ou de um assistente de IA. Uma API REST (versionada em /api/v1) e um servidor MCP usam a mesma chave, os mesmos limites e o mesmo saldo. Incluída nos planos Pro e Studio.
Primeiros passos
Crie uma chave em Minha conta, depois crie um trabalho, consulte o andamento e leia o resultado:
export TLTL_API_KEY=tltl_xxxxxxxxxxxx_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
# 1. create the job
curl -X POST https://toolongtolisten.com/api/v1/transcriptions \
-H "Authorization: Bearer $TLTL_API_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{"url":"https://example.com/meeting.mp3","language":"auto","name":"Reunião semanal"}'
# 2. poll until "status" is "completed"
curl https://toolongtolisten.com/api/v1/transcriptions/$ID -H "Authorization: Bearer $TLTL_API_KEY"
# 3. read the result: json | text | srt | vtt
curl "https://toolongtolisten.com/api/v1/transcriptions/$ID/transcript?format=srt" -H "Authorization: Bearer $TLTL_API_KEY"const BASE = 'https://toolongtolisten.com';
const headers = { Authorization: `Bearer ${process.env.TLTL_API_KEY}`, 'Content-Type': 'application/json' };
const created = await fetch(`${BASE}/api/v1/transcriptions`, {
method: 'POST',
headers: { ...headers, 'Idempotency-Key': crypto.randomUUID() },
body: JSON.stringify({ url: 'https://example.com/meeting.mp3', language: 'auto' }),
}).then((r) => r.json());
let job = created;
while (job.status === 'queued' || job.status === 'processing') {
await new Promise((r) => setTimeout(r, 5000));
job = await fetch(`${BASE}/api/v1/transcriptions/${created.id}`, { headers }).then((r) => r.json());
}
if (job.status !== 'completed') throw new Error(job.error?.message ?? job.status);
const transcript = await fetch(`${BASE}/api/v1/transcriptions/${created.id}/transcript`, { headers }).then((r) => r.json());
console.log(transcript.text, transcript.segments.length);import os, time, uuid, requests
BASE = "https://toolongtolisten.com"
H = {"Authorization": f"Bearer {os.environ['TLTL_API_KEY']}"}
job = requests.post(f"{BASE}/api/v1/transcriptions", headers={**H, "Idempotency-Key": str(uuid.uuid4())},
json={"url": "https://example.com/meeting.mp3", "language": "auto"}).json()
while job["status"] in ("queued", "processing"):
time.sleep(5)
job = requests.get(f"{BASE}/api/v1/transcriptions/{job['id']}", headers=H).json()
if job["status"] != "completed":
raise RuntimeError(job.get("error") or job["status"])
transcript = requests.get(f"{BASE}/api/v1/transcriptions/{job['id']}/transcript", headers=H).json()
print(transcript["text"])Autenticação
Envie Authorization: Bearer <chave> (o cabeçalho X-API-Key também funciona). As chaves têm o formato tltl_<id>_<segredo>, aparecem uma única vez ao criar e só um hash é guardado. Você pode ter até 5 chaves ativas e revogá-las a qualquer momento em Minha conta. Chave revogada ou desconhecida retorna 401 invalid_api_key; conta sem Pro ou Studio retorna 403 plan_required.
Criar uma transcrição
A partir de uma URL https pública (arquivo de mídia direto de até 100 MB, baixado pelo nosso servidor) ou enviando o arquivo. Formatos: mp3, m4a, ogg, opus, wav, webm, flac e mp4. Links de páginas do YouTube e do Google Drive não são aceitos, apenas URLs diretas de mídia. A resposta é imediata (202) e o trabalho roda em fila. Campos opcionais em toda criação: name, language (código ISO como pt, ou auto), word_timestamps, metadata (até 10 pares de texto devolvidos como estão) e webhook_url.
Enviar um arquivo local
Arquivos pequenos (até 30 MB) podem ir como multipart no mesmo endpoint. Para qualquer arquivo até 100 MB, peça uma URL de upload assinada, faça PUT do arquivo direto no armazenamento e depois inicie o trabalho. Com "resumable": true a URL é uma sessão de upload retomável.
# 1. reserve a slot (file_name decides the format)
curl -X POST https://toolongtolisten.com/api/v1/uploads \
-H "Authorization: Bearer $TLTL_API_KEY" -H "Content-Type: application/json" \
-d '{"file_name":"meeting.m4a","content_type":"audio/mp4","language":"auto"}'
# -> { "id": "...", "upload": { "method": "PUT", "url": "https://storage.googleapis.com/...", "headers": {...} }, "complete_url": "..." }
# 2. PUT the file to upload.url with exactly the returned headers
curl -X PUT "$UPLOAD_URL" -H "Content-Type: audio/mp4" -H "x-goog-content-length-range: 0,104857600" --data-binary @meeting.m4a
# 3. start the job, then poll as usual
curl -X POST https://toolongtolisten.com/api/v1/uploads/$ID/complete -H "Authorization: Bearer $TLTL_API_KEY"
# small files (up to 30 MB) can go straight to the create endpoint
curl -X POST https://toolongtolisten.com/api/v1/transcriptions -H "Authorization: Bearer $TLTL_API_KEY" -F "file=@meeting.m4a" -F "name=Reunião semanal"Status e resultado
O status é awaiting_upload, queued, processing, completed, failed ou canceled. A transcrição pronta vem em JSON (texto, idioma, trechos com tempos, falantes), texto simples, SRT ou VTT. Use words=1 para tempos por palavra (trabalhos criados com word_timestamps), speakers=0 para omitir os nomes dos falantes e lang=en para uma tradução feita no app. Outras chamadas: listar (GET /api/v1/transcriptions, com cursor e status), DELETE do trabalho e do áudio, POST .../cancel, POST .../summary (modos short, topics, minutes, chapters; o campo opcional language, como pt-BR ou en, define o idioma da saída, o padrão é o idioma detectado na transcrição) e GET /api/v1/usage.
Webhooks
Com webhook_url (somente https, hosts públicos), enviamos um POST JSON com o evento transcription.completed ou transcription.failed, em até 3 tentativas. O corpo traz transcript_url, não o texto. Cabeçalhos: X-TLTL-Signature: t=<unix>,v1=<hmac>, em que o HMAC é o SHA-256 de "<t>.<corpo bruto>" com o segredo mostrado ao criar a chave, X-TLTL-Event e X-TLTL-Delivery (estável entre tentativas, use para deduplicar). Verifique sobre o corpo bruto e rejeite carimbos com mais de 5 minutos. Para reenviar: POST /api/v1/transcriptions/{id}/webhook.
{
"event": "transcription.completed",
"id": "9fKd3mQ1xZ",
"status": "completed",
"language": "en",
"duration_seconds": 312.4,
"error": null,
"metadata": { "task": "ST-123" },
"transcript_url": "https://toolongtolisten.com/api/v1/transcriptions/9fKd3mQ1xZ/transcript",
"created_at": "2026-10-22T18:30:00.000Z"
}import { createHmac, timingSafeEqual } from 'node:crypto';
export function verify(secret, rawBody, header) {
const parts = Object.fromEntries(header.split(',').map((p) => p.split('=')));
if (Math.abs(Date.now() / 1000 - Number(parts.t)) > 300) return false;
const expected = createHmac('sha256', secret).update(parts.t + '.' + rawBody).digest();
const given = Buffer.from(parts.v1 || '', 'hex');
return given.length === expected.length && timingSafeEqual(given, expected);
}import hmac, hashlib, time
def verify(secret: str, raw_body: bytes, header: str) -> bool:
parts = dict(p.split("=", 1) for p in header.split(","))
if abs(time.time() - int(parts["t"])) > 300:
return False
expected = hmac.new(secret.encode(), parts["t"].encode() + b"." + raw_body, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, parts.get("v1", ""))Idempotência
Envie o cabeçalho Idempotency-Key (até 255 caracteres) em POST /api/v1/transcriptions e POST /api/v1/uploads. Uma nova tentativa com a mesma chave e a mesma requisição devolve a resposta original com Idempotent-Replayed: true em vez de criar um segundo trabalho; a mesma chave com outra requisição é um 422 idempotency_key_reused. As chaves valem por 24 horas.
Limites e cotas
- Por chave: 60 requisições por minuto, 60 novas transcrições por hora, 3000 por mês e 30 resumos por hora. Toda resposta traz
X-RateLimit-Limit,X-RateLimit-RemainingeX-RateLimit-Reset; ao exceder, a resposta é 429 comRetry-After(rate_limitedouquota_exceeded). - O uso é cobrado em segundos de áudio do mesmo saldo do app. Sem saldo a resposta é 402
insufficient_credits.GET /api/v1/usagemostra limites, uso e saldo. - O áudio é mantido conforme a retenção do plano; as transcrições ficam até você apagar.
Tenants (várias organizações, uma chave)
Se seu produto atende várias organizações com uma única chave, informe a qual delas cada chamada se refere. Envie X-Tenant-Id (necessário para a atribuição: 1 a 64 caracteres, letras, dígitos, _ . : @ -, começando por letra ou dígito) e, opcionalmente, X-Tenant-Name (nome de exibição, até 80 caracteres, aceita percent encoding) em qualquer chamada REST e no endpoint MCP. As ferramentas MCP que criam trabalhos também aceitam os argumentos tenant_id e tenant_name, que valem mais que os cabeçalhos. Um id malformado é um 400. Chamadas sem o cabeçalho são atribuídas somente à chave.
O tenant é guardado no trabalho e devolvido como tenant: { id, name } em toda transcrição e no corpo do webhook. Requisições, erros, latência, webhooks, minutos de áudio e custo estimado são reportados por chave e por tenant ao operador da conta.
Uma chave pode ter um limite mensal de minutos por tenant (um padrão para tenants sem entrada própria e exceções; um tenant pode ser ilimitado e ainda assim acompanhado). Quando um tenant usa seus minutos no mês UTC corrente, criar um trabalho responde 402 tenant_limit_reached com Retry-After apontando para o início do próximo mês. Os minutos contam o áudio concluído mais o áudio ainda na fila. GET /api/v1/usage?tenant=<id> (ou o cabeçalho) devolve tenant.monthly_minutes_limit, minutes_used, minutes_in_progress, minutes_remaining, limit_reached e resets_at.
curl -X POST https://toolongtolisten.com/api/v1/transcriptions \
-H "Authorization: Bearer $TLTL_API_KEY" \
-H "X-Tenant-Id: org_acme" \
-H "X-Tenant-Name: Acme Ltda" \
-H "Content-Type: application/json" \
-d '{"url": "https://example.com/meeting.m4a"}'
# 402 when org_acme used its minutes this month
# {"error": {"code": "tenant_limit_reached", "status": 402, "message": "...", "request_id": "req_..."}}
curl "https://toolongtolisten.com/api/v1/usage?tenant=org_acme" -H "Authorization: Bearer $TLTL_API_KEY"Erros
Todo erro tem o mesmo formato e o cabeçalho X-Request-Id. Decida pelo code, nunca pela mensagem. Informe o request_id ao falar com o suporte.
HTTP/1.1 429 Too Many Requests
Retry-After: 21
X-Request-Id: req_Qm3b0u9vTq1k
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 0
{ "error": { "code": "rate_limited", "status": 429, "message": "Rate limit exceeded. Retry later.", "request_id": "req_Qm3b0u9vTq1k" } }Códigos
invalid_requestinvalid_api_keyplan_requiredforbiddennot_foundconflictnot_finishedstill_processingupload_missinginsufficient_creditspayload_too_largeunsupported_media_typedownload_failedrate_limitedquota_exceededtenant_limit_reachedidempotency_key_reusedidempotency_in_progressservice_unavailableinternal_error
Servidor MCP
O mesmo backend fala o Model Context Protocol por Streamable HTTP em https://toolongtolisten.com/api/mcp. Ele não guarda estado, autentica com a mesma chave de API e atende a revisão 2026-07-28 e também clientes 2025-11-25, 2025-06-18 e 2025-03-26. Para adicionar ao Claude Code:
claude mcp add --transport http tltl https://toolongtolisten.com/api/mcp \
--header "Authorization: Bearer $TLTL_API_KEY"Outros clientes que aceitam configuração em JSON (Cursor, Claude Desktop via mcp-remote e outros):
{
"mcpServers": {
"tltl": {
"url": "https://toolongtolisten.com/api/mcp",
"headers": { "Authorization": "Bearer tltl_xxxxxxxxxxxx_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" }
}
}
}
// Claude Desktop (stdio bridge)
{
"mcpServers": {
"tltl": {
"command": "npx",
"args": ["-y", "mcp-remote", "https://toolongtolisten.com/api/mcp", "--header", "Authorization: Bearer ${TLTL_API_KEY}"],
"env": { "TLTL_API_KEY": "tltl_xxxxxxxxxxxx_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" }
}
}
}Ferramentas
transcribe_urlInicia uma transcrição a partir de uma URL pública de mídia.transcribe_uploadDevolve uma URL de upload assinada para um arquivo local; faça PUT do arquivo e chame complete_upload.complete_uploadInicia o trabalho depois do upload.get_transcriptionStatus e, quando pronta, o texto ou os trechos com tempos.list_transcriptionsLista os trabalhos com status e paginação.export_subtitlesTexto SRT ou VTT de um trabalho pronto.summarizeResumo curto, tópicos, ata de reunião ou capítulos.cancel_transcription / delete_transcriptionInterrompe um trabalho na fila ou remove um já finalizado.
Um servidor remoto não lê arquivos da sua máquina; para um arquivo local o assistente chama transcribe_upload, envia o arquivo (por exemplo com curl) e então chama complete_upload.
npx @modelcontextprotocol/inspector --cli https://toolongtolisten.com/api/mcp --transport http \
--header "Authorization: Bearer $TLTL_API_KEY" --method tools/listOpenAPI
A descrição OpenAPI 3.1 da API REST está em /api/v1/openapi.json. Importe no Postman, no Insomnia ou em um gerador de código.