Referência da API
Endpoints, campos, respostas, créditos, limites e erros.
Atualizado em
Como funciona#
Cada transcrição é uma tarefa. Você envia uma fonte, a tarefa roda em segundo plano e, quando ela termina com sucesso, aponta para uma transcrição que você pode ler ou exportar quantas vezes quiser. URL base: https://www.transcriptdock.com. Todas as requisições e respostas são em JSON.
# 1. Submitcurl -X POST https://www.transcriptdock.com/v1/jobs \-H "Authorization: Bearer td_live_YOUR_KEY" \-H "Idempotency-Key: video-7680721699171601694" \-H "Content-Type: application/json" \-d '{ "source": { "url": "https://www.tiktok.com/@tiktok/video/7680721699171601694" }, "mode": "captions_only" }'# 2. Wait (returns as soon as the job finishes, up to 25 s per call)curl "https://www.transcriptdock.com/v1/jobs/11111111-1111-4111-8111-111111111111?wait=25" \-H "Authorization: Bearer td_live_YOUR_KEY"# 3. Exportcurl "https://www.transcriptdock.com/v1/transcripts/22222222-2222-4222-8222-222222222222/export?format=srt" \-H "Authorization: Bearer td_live_YOUR_KEY"
Autenticação#
Crie chaves na página Chaves de API. Uma chave é mostrada uma única vez, tem o formato td_live_... e vai no cabeçalho Authorization de cada requisição. As chaves têm escopos escolhidos na criação: jobs:read, jobs:write, transcripts:read, uploads:write. Crie uma chave por integração, para poder revogar uma delas sozinha.
Authorization: Bearer td_live_YOUR_KEY
Idempotency-Key#
Obrigatório em POST /v1/jobs, /v1/batches, /v1/jobs/{id}/retry, /v1/video-discoveries e /v1/language-discoveries: qualquer texto de 8 a 128 caracteres ASCII imprimíveis. Enviar a mesma chave com o mesmo corpo em até 48 horas devolve o objeto original, então uma requisição repetida nunca cria nem cobra duas vezes. A mesma chave com um corpo diferente retorna 409 IDEMPOTENCY_CONFLICT. Uma boa chave é o seu próprio id para o item que você está transcrevendo.
X-Request-Id#
Toda resposta traz um. Informe-o ao entrar em contato com o suporte.
Endpoints#
| Método | Caminho | O que faz | Créditos |
|---|---|---|---|
| POST | /v1/jobs | Transcreve um vídeo, arquivo ou link | 1 por transcrição de legendas, 2 por minuto de transcrição por IA |
| GET | /v1/jobs/{id} | Status da tarefa, espera até 25 s | Grátis |
| GET | /v1/jobs | Histórico de tarefas | Grátis |
| POST | /v1/jobs/{id}/cancel | Cancela uma tarefa na fila | Grátis |
| POST | /v1/jobs/{id}/retry | Tenta de novo uma tarefa com falha | Como uma nova tarefa |
| POST | /v1/batches | Transcreve vários vídeos (tamanho do lote do plano) | Por item, como acima |
| GET | /v1/batches/{id} | Status do lote | Grátis |
| GET | /v1/transcripts/{id} | Transcrição em JSON | Grátis |
| GET | /v1/transcripts/{id}/export | Arquivo txt, srt, vtt ou json | Grátis |
| DELETE | /v1/transcripts/{id} | Exclui uma transcrição | Grátis |
| POST | /v1/uploads | Obtém uma URL para enviar um arquivo | Grátis |
| POST | /v1/uploads/{id}/complete | Marca o envio como concluído | Grátis |
| POST | /v1/video-discoveries | Pesquisa no YouTube, lista um canal, uma playlist ou um perfil do TikTok | 1 por página |
| GET | /v1/video-discoveries/{id} | Resultado da descoberta | Grátis |
| POST | /v1/language-discoveries | Lista os idiomas de legenda de um vídeo do YouTube | 1 |
| GET | /v1/language-discoveries/{id} | Lista de idiomas | Grátis |
| POST | /v1/webhook-endpoints | Registra uma URL de webhook (com sessão iniciada) | Grátis |
| GET | /v1/webhook-endpoints | Lista as URLs de webhook | Grátis |
| DELETE | /v1/webhook-endpoints/{id} | Desativa uma URL de webhook (com sessão iniciada) | Grátis |
| GET | /v1/usage | Créditos e plano | Grátis |
| GET | /v1/capabilities | O que a sua chave pode fazer | Grátis |
Criar uma tarefa#
/v1/jobsRetorna 202 com a tarefa. Se o seu workspace já tiver uma transcrição do mesmo vídeo com as mesmas opções, retorna 200 com uma tarefa concluída, sem custo (billing.kind: "cached").
{"id": "11111111-1111-4111-8111-111111111111","status": "queued","stage": "resolve","result_id": null,"error": null,"billing": { "credits_reserved": 2, "credits_charged": 0, "kind": "captions" },"source": { "platform": "tiktok", "media_id": "7680721699171601694", "canonical_url": "https://www.tiktok.com/@tiktok/video/7680721699171601694", "title": null, "upload_id": null },"options": { "mode": "auto", "language": null, "caption_preference": "prefer_creator" },"created_at": "2026-09-16T10:00:00.000Z"}
O objeto da tarefa#
Obter uma tarefa#
/v1/jobs/{id}?wait=25Retorna a tarefa. Com wait (de 0 a 25 segundos), a requisição fica aberta e responde assim que a tarefa estiver final, então uma única chamada substitui um laço de consultas. As leituras têm limite próprio, de 120 por minuto.
Listar tarefas#
/v1/jobs?limit=20&cursor=Da mais recente para a mais antiga, com limit de 1 a 100. Envie o next_cursor da resposta de volta como cursor para a próxima página. Adicione platform e source_id (o source.media_id de uma tarefa) juntos para ver apenas as tarefas de um vídeo, em qualquer modo. É assim que um cliente verifica se o workspace já tem uma transcrição antes de enviar o mesmo vídeo de novo.
Cancelar uma tarefa#
/v1/jobs/{id}/cancelCancela uma tarefa que ainda não começou a transcrição por IA; a reserva é liberada. Depois disso, retorna 409 CANCELLATION_NOT_ALLOWED e a tarefa termina.
Tentar de novo uma tarefa#
/v1/jobs/{id}/retryCria uma nova tarefa a partir de uma que falhou com erro retryable. Exige um novo Idempotency-Key; corpo opcional { "max_credits": 40 }. Tem o mesmo preço de uma tarefa nova. Falhas definitivas (vídeo privado, sem legendas) retornam 409 JOB_NOT_RETRYABLE: corrija a entrada e envie de novo.
A transcrição#
/v1/transcripts/{id}{"id": "22222222-2222-4222-8222-222222222222","source": { "platform": "youtube", "media_id": "dQw4w9WgXcQ", "canonical_url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ", "title": "Never Gonna Give You Up" },"source_origin": "creator_captions","language": "en","text": "Never gonna give you up. Never gonna let you down.","segments": [{ "start": 0, "end": 2.4, "text": "Never gonna give you up" },{ "start": 2.4, "end": 4.9, "text": "Never gonna let you down" }],"words": null,"timing_granularity": "segment","duration_seconds": 212.0,"recognition": null,"extraction_version": "worker-0.1.0","created_at": "2026-09-16T10:00:14.000Z"}
As transcrições são mantidas pelo período de retenção do seu plano (7 dias no teste, 30 no Starter, 90 no Pro e no Scale). DELETE /v1/transcripts/{id} remove uma antes disso.
Exportar uma transcrição#
/v1/transcripts/{id}/export?format=srt| format | Você recebe |
|---|---|
| txt | Texto simples, um segmento por linha. |
| srt | Legendas SubRip. |
| vtt | Legendas WebVTT. |
| json | O objeto de transcrição acima. |
Grátis e sem limite. srt e vtt precisam de tempos; uma transcrição sem tempos retorna 422 TIMESTAMPS_UNAVAILABLE.
Lotes#
/v1/batchesUma requisição, muitos vídeos. Cada item tem os mesmos campos de Criar uma tarefa; o lote inteiro é aceito ou recusado de uma vez. Itens por lote: 1 no teste, 10 no Starter, 25 no Pro e 50 no Scale.
curl -X POST https://www.transcriptdock.com/v1/batches \-H "Authorization: Bearer td_live_YOUR_KEY" \-H "Idempotency-Key: playlist-2026-09-16" \-H "Content-Type: application/json" \-d '{ "items": [{ "source": { "url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ" }, "mode": "captions_only" },{ "source": { "url": "https://www.tiktok.com/@tiktok/video/7680721699171601694" }, "mode": "captions_only" }] }'
O objeto do lote tem status (queued, processing, succeeded, partial_success, failed, cancelled), contagens (total_items, succeeded_items, failed_items, cancelled_items) e items, cada um com o seu job_id. Leia com GET /v1/batches/{id}; cada item é uma tarefa comum.
Envios de arquivo#
Seu próprio áudio ou vídeo (mp3, wav, m4a, ogg, aac, mp4, webm; até 250 MB e 2 horas). Reserve um espaço, envie o arquivo com PUT para a URL assinada, marque como concluído e depois envie-o como tarefa com source.upload_id. Envios sempre usam transcrição por IA.
# 1. Reservecurl -X POST https://www.transcriptdock.com/v1/uploads \-H "Authorization: Bearer td_live_YOUR_KEY" \-H "Content-Type: application/json" \-d '{ "filename": "interview.mp3", "content_type": "audio/mpeg", "bytes": 48213920 }'# -> { "id": "33333333-...", "signed_upload_url": "https://...", "expires_at": "...", "status": "pending" }# 2. Upload the bytes (same Content-Type you declared)curl -X PUT "SIGNED_UPLOAD_URL" -H "Content-Type: audio/mpeg" --data-binary @interview.mp3# 3. Completecurl -X POST https://www.transcriptdock.com/v1/uploads/33333333-3333-4333-8333-333333333333/complete \-H "Authorization: Bearer td_live_YOUR_KEY"# 4. Transcribe itcurl -X POST https://www.transcriptdock.com/v1/jobs \-H "Authorization: Bearer td_live_YOUR_KEY" \-H "Idempotency-Key: interview-01" \-H "Content-Type: application/json" \-d '{ "source": { "upload_id": "33333333-3333-4333-8333-333333333333" }, "mode": "transcribe" }'
A URL assinada vale por 24 horas; depois disso, reserve um novo espaço.
Webhooks#
/v1/webhook-endpointsRegistre uma URL https e passe o seu id como webhook_endpoint_id ao enviar. Enviamos um evento por POST quando a tarefa ou o lote termina. A resposta inclui o secret de assinatura, uma única vez. Registrar e desativar endpoints exige uma sessão iniciada, então faça isso na página Webhooks do painel; uma chave de API recebe 403 FORBIDDEN nessas operações, mas pode listar os endpoints.
Eventos#
{"event_id": "44444444-4444-4444-8444-444444444444","type": "job.succeeded","created_at": "2026-09-16T10:00:14.000Z","job_id": "11111111-1111-4111-8111-111111111111","batch_id": null,"result_id": "22222222-2222-4222-8222-222222222222","metadata": { "order": "8812" },"error": null}
job.succeeded: busque ou exporte oresult_id.job.failed:errortraz o código.batch.completed: todos os itens estão finais; leia o lote para ver o resultado de cada item.
Responda com qualquer código 2xx em poucos segundos. Entregas que falham são reenviadas com intervalos crescentes e podem ser reenviadas pelo painel. Um evento pode chegar mais de uma vez: remova duplicados pelo event_id.
Verificar a assinatura#
Cabeçalhos X-TranscriptDock-Event-Id, X-TranscriptDock-Timestamp e X-TranscriptDock-Signature. A assinatura é o HMAC-SHA256 (hex) de {event_id}.{timestamp}.{raw_body} com o seu secret. Rejeite qualquer evento com mais de cinco minutos.
import crypto from "node:crypto";export function verify(eventId: string, timestamp: string, rawBody: string, signature: string, secret: string): boolean {if (Math.abs(Date.now() / 1000 - Number(timestamp)) > 300) return false;const expected = crypto.createHmac("sha256", secret).update(`${eventId}.${timestamp}.${rawBody}`).digest("hex");return expected.length === signature.length && crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(signature));}
Encontrar vídeos#
/v1/video-discoveriesPesquise no YouTube ou liste um canal, uma playlist ou um perfil do TikTok, e depois envie as URLs como tarefas. Uma descoberta roda em segundo plano, como uma tarefa: leia-a com GET /v1/video-discoveries/{id} até que status seja succeeded. São 1 crédito por página; os resultados são mantidos por 24 horas.
curl -X POST https://www.transcriptdock.com/v1/video-discoveries \-H "Authorization: Bearer td_live_YOUR_KEY" \-H "Idempotency-Key: search-async-rust-1" \-H "Content-Type: application/json" \-d '{ "kind": "youtube_search", "query": "async rust", "limit": 5 }'
O POST responde 202 com status: "queued". Uma resposta de GET /v1/video-discoveries/{id} já concluída tem este formato:
{"id": "55555555-5555-4555-8555-555555555555","kind": "youtube_search","status": "succeeded","request": { "kind": "youtube_search", "query": "async rust", "limit": 5 },"videos": [{"platform": "youtube","video_id": "wXtngLBkK4Q","url": "https://www.youtube.com/watch?v=wXtngLBkK4Q","title": "Async Rust explained in 20 minutes","channel_id": "UCSp-OaMpsO8K0KkOqyBl7_w","channel_title": "Let's Get Rusty","duration_s": 1155,"published_at": null,"published_text": "5 months ago","view_count": 83635,"view_count_text": "83,635 views","thumbnail_url": "https://i.ytimg.com/vi/wXtngLBkK4Q/hq720.jpg","tiktok": null}],"profile": null,"next_cursor": "opaque-token","error": null,"created_at": "2026-09-16T12:00:00.000Z","updated_at": "2026-09-16T12:00:03.000Z","expires_at": "2026-09-17T12:00:00.000Z"}
Idiomas de legenda de um vídeo#
/v1/language-discoveriesCorpo { "source": { "url": "https://www.youtube.com/watch?v=..." } } com um Idempotency-Key (somente YouTube, 1 crédito). Leia GET /v1/language-discoveries/{id} para ver a lista de faixas de legenda, com os códigos, se são do criador ou automáticas e qual é a padrão. Use os códigos em caption_languages.
Uso e recursos#
/v1/usage{"plan": "pro","period_end": "2026-10-16T00:00:00.000Z","credits_remaining": 5840,"credits_total": 6000,"credits_reserved": 4,"prepaid_credits": 0,"transcribe_credits_per_minute": 2,"minimum_billable_seconds": 60,"subscription": {"status": "active","interval": "yearly","cancel_at_period_end": false,"current_period_end": "2027-09-16T00:00:00.000Z"}}
credits_remaining já desconta credits_reserved (reservas de tarefas em execução). prepaid_credits são créditos comprados, que nunca expiram.
subscription é a assinatura da Stripe por trás de um plano pago, e null quando não há uma: o seu status, o interval de cobrança (monthly ou yearly), cancel_at_period_end e current_period_end, que mostram quando o plano é renovado ou termina. period_end é quando os créditos mensais são renovados, todo mês, inclusive nos planos anuais.
/v1/capabilitiesO que a sua chave pode fazer agora: por fonte (youtube, tiktok, instagram, upload, direct), quais modos estão disponíveis, além dos limites e preços. Use isso em vez de fixar valores no código; o teste, por exemplo, informa auto e transcribe como false.
{"youtube": { "enabled": true, "captions_only": true, "auto": true, "transcribe": true, "social_acquisition_fee": true },"tiktok": { "enabled": true, "captions_only": true, "auto": true, "transcribe": true, "social_acquisition_fee": true },"instagram": { "enabled": false, "captions_only": false, "auto": false, "transcribe": false, "social_acquisition_fee": true, "native_captions": "unverified_disabled" },"upload": { "enabled": true, "captions_only": false, "auto": true, "transcribe": true, "social_acquisition_fee": false },"direct": { "enabled": true, "captions_only": false, "auto": true, "transcribe": true, "social_acquisition_fee": false },"max_duration_ms": 7200000,"max_media_bytes": 262144000,"export_formats": ["txt", "json", "srt", "vtt"],"recognition_profiles": ["standard"],"transcribe_credits_per_minute": 2,"minimum_billable_seconds": 60,"price_version": "2026-09-14","asr_enabled": true,"instagram_enabled": false,"free_caption_credits": 50,"free_caption_credits_period": "month"}
Limites de requisições#
| Plano | Envios por min | Tarefas em andamento | Leituras por min |
|---|---|---|---|
| Teste grátis | 10 | 10 | 120 |
| Starter | 60 | 100 | 120 |
| Pro | 120 | 500 | 120 |
| Scale | 300 | 2.000 | 120 |
Acima de um limite você recebe 429 RATE_LIMITED com um cabeçalho retry-after (em segundos) e details.detail indicando qual limite. Nada é cobrado. Webhooks e ?wait=25 mantêm você bem abaixo do limite de leituras.
Erros#
Todo erro tem o mesmo formato. retryable: true significa que a mesma requisição pode dar certo depois: espere os segundos de retry-after, se houver, ou aguarde alguns segundos, e reutilize o mesmo Idempotency-Key, para não duplicar nada. Qualquer outro erro exige uma mudança do seu lado.
{"error": {"code": "INSUFFICIENT_BALANCE","message": "Not enough credits. Buy credits or upgrade your plan to continue.","retryable": false,"details": { "detail": "This job needs more credits than you have left. Buy credits or upgrade your plan." },"doc_url": "https://www.transcriptdock.com/docs/api-reference#error-codes"},"request_id": "66666666-6666-4666-8666-666666666666"}
Uma tarefa que falha depois de aceita continua com 200 em GET /v1/jobs/{id}, com status: "failed" e o mesmo objeto de erro em error. Nada é cobrado por uma tarefa com falha.
Códigos#
| Código | HTTP | Tentar de novo | Significado e o que fazer |
|---|---|---|---|
INVALID_REQUEST | 422 | Não | Um campo está faltando ou tem formato errado. details.detail indica qual. |
INVALID_URL | 422 | Não | A URL não é um link https válido (máximo de 2048 caracteres). |
UNSUPPORTED_SOURCE | 422 | Não | O link não é uma URL de vídeo do YouTube ou do TikTok. |
UNSAFE_URL | 422 | Não | O link direto da mídia aponta para um endereço de rede privado ou bloqueado. |
INVALID_CURSOR | 422 | Não | Um cursor de descoberta de vídeos expirou ou está inválido. Comece de novo pela primeira página. Um cursor inválido na lista de tarefas retorna INVALID_REQUEST. |
CAPABILITY_UNAVAILABLE | 422 | Não | Este modo não está disponível para esta fonte. GET /v1/capabilities mostra o que está. |
IDEMPOTENCY_CONFLICT | 409 | Não | Esta Idempotency-Key já foi usada com um corpo diferente. Use uma chave nova. |
UNAUTHENTICATED | 401 | Não | Nenhuma chave de API ou token OAuth válido no cabeçalho Authorization. |
FORBIDDEN | 403 | Não | A chave não tem o escopo deste endpoint, ou o workspace está desativado. |
EMAIL_UNVERIFIED | 403 | Não | Confirme o e-mail da conta antes de usar a API. |
NOT_FOUND | 404 | Não | Este objeto não existe neste workspace. |
INSUFFICIENT_BALANCE | 402 | Não | Créditos insuficientes para esta tarefa. Compre créditos ou faça upgrade. |
PLAN_REQUIRED | 402 | Não | Isso exige um plano pago (transcrição por IA, lotes maiores) ou os créditos do teste já foram usados. |
BUDGET_EXCEEDED | 402 | Não | O custo medido está acima de max_credits. Nada foi cobrado; aumente o limite ou ignore a mídia. |
JOB_NOT_RETRYABLE | 409 | Não | A falha foi definitiva (vídeo privado, sem legendas). Corrija a entrada e envie uma nova tarefa. |
CANCELLATION_NOT_ALLOWED | 409 | Não | A transcrição por IA já começou; a tarefa vai terminar. |
RATE_LIMITED | 429 | Sim | Acima de um limite de requisições ou de fila. Espere os segundos de retry-after; details.detail indica qual limite. |
SOURCE_NOT_FOUND | 422 | Não | O vídeo não existe ou foi removido. |
SOURCE_PRIVATE | 422 | Não | O vídeo é privado. Somente vídeos públicos funcionam. |
SOURCE_AUTH_REQUIRED | 422 | Não | O vídeo exige login, verificação de idade ou assinatura de membro. |
SOURCE_REGION_RESTRICTED | 422 | Não | O vídeo está bloqueado nas regiões de onde buscamos. |
NO_CAPTIONS | 422 | Não | O vídeo não tem legendas e o modo era captions_only. Use auto ou transcribe. |
LANGUAGE_UNAVAILABLE | 422 | Não | Nenhum dos idiomas de caption_languages existe neste vídeo. Remova a lista para usar a faixa padrão. |
LANGUAGE_UNSUPPORTED | 422 | Não | A transcrição por IA não oferece suporte ao idioma pedido. |
NO_SPEECH | 422 | Não | A transcrição por IA não encontrou fala no áudio. |
NO_AUDIO | 422 | Não | A mídia não tem faixa de áudio. |
INVALID_MEDIA | 422 | Não | O arquivo não pôde ser lido como áudio ou vídeo. |
UNSUPPORTED_MEDIA | 422 | Não | Não é um tipo de arquivo de áudio ou vídeo. |
DURATION_LIMIT_EXCEEDED | 413 | Não | Mais longo que 2 horas. |
FILE_TOO_LARGE | 413 | Não | Maior que 250 MB. |
TIMESTAMPS_UNAVAILABLE | 422 | Não | Esta transcrição não tem tempos, então as exportações srt e vtt não estão disponíveis. Use txt ou json. |
PROVIDER_REJECTED | 422 | Não | O modelo de transcrição por IA não conseguiu processar este áudio. |
SOURCE_RATE_LIMITED | 503 | Sim | A plataforma de origem está limitando o nosso acesso. A tarefa tenta de novo sozinha. |
SOURCE_BLOCKED | 503 | Sim | A plataforma de origem bloqueou a busca. A tarefa tenta de novo sozinha. |
SOURCE_TIMEOUT | 503 | Sim | A plataforma de origem excedeu o tempo limite. A tarefa tenta de novo sozinha. |
SOURCE_CHANGED | 503 | Sim | A origem mudou enquanto a líamos. A tarefa tenta de novo sozinha. |
PROVIDER_UNAVAILABLE | 503 | Sim | A transcrição por IA está temporariamente indisponível. A tarefa tenta de novo sozinha. |
STORAGE_UNAVAILABLE | 503 | Sim | O armazenamento de arquivos está temporariamente indisponível. Tente a requisição de novo. |
INTERNAL_ERROR | 500 | Sim | Erro do nosso lado. Tente de novo com a mesma Idempotency-Key; informe o X-Request-Id se o problema persistir. |