Cómo crear subtítulos SRT de un vídeo con una API

Envía el vídeo a POST /transcribe indicando output_format como srt y word_timestamps en false, espera a que termine la tarea y guarda el texto que devuelve en un archivo .srt. Si quieres los subtítulos traducidos a otro idioma, añade translate_to. Aquí veremos qué petición enviar exactamente, qué lleva dentro el SRT, cómo armar líneas a tu medida usando los tiempos por palabra, cómo pasarlo a WebVTT y cómo quemar los subtítulos en el vídeo.

Obtener una clave de API Inicio rápido Idiomas compatibles

Última actualización: · Markdown

La solicitud

Dos llamadas: iniciar el trabajo y consultarlo. El result de un trabajo srt terminado es una cadena, no JSON. Esta versión solo necesita curl y jq:

# 1. Start the job. "word_timestamps": false gives one cue per segment.
JOB=$(curl -s https://app.kolwrite.com/transcribe \
  -H "x-api-key: $KOLWRITE_API_KEY" -H "Content-Type: application/json" \
  -d '{"audio_url": "https://example.com/talk.mp3", "model": "oracle", "output_format": "srt", "word_timestamps": false}' \
  | jq -r .jobId)

# 2. Poll until it is done and save the result string as a .srt file.
while true; do
  REPLY=$(curl -s "https://app.kolwrite.com/transcribe/$JOB" -H "x-api-key: $KOLWRITE_API_KEY")
  STATUS=$(echo "$REPLY" | jq -r .status)
  if [ "$STATUS" = "COMPLETED" ]; then echo "$REPLY" | jq -r .result > talk.srt; break; fi
  if [ "$STATUS" = "FAILED" ]; then echo "$REPLY" | jq -r .error; break; fi
  sleep 5
done

Puedes mandar el vídeo directamente, porque la API ya se encarga de leer la pista de audio. Si tienes el archivo en local, súbelo antes con POST /upload (admite hasta 100 MiB). Para vídeos largos que pesen más, saca primero el audio con ffmpeg. En la guía de edición de vídeo tienes una función en Python que hace todo el proceso.

Qué trae el archivo SRT

Si pasas "word_timestamps": false, sale una línea por segmento, igual que en la mayoría de los archivos de subtítulos:

1
00:00:01,000 --> 00:00:03,750
Hello everyone and welcome to the show.

2
00:00:05,000 --> 00:00:07,750
Today we talk about editing video from a transcript.

Los tiempos por palabra vienen activados por defecto. En ese modo, la API genera una entrada por palabra donde repite toda la frase y envuelve la palabra activa en una etiqueta <font> amarilla. Esto sirve para ir marcando la lectura estilo karaoke, pero no para unos subtítulos normales:

1
00:00:01,000 --> 00:00:01,350
<font color="yellow">Hello</font> everyone and welcome to the show.

2
00:00:01,400 --> 00:00:01,750
Hello <font color="yellow">everyone</font> and welcome to the show.

Si quieres subtítulos estándar, pasa "word_timestamps": false o arma tú mismo las líneas a partir de las palabras del JSON, como hacemos aquí abajo.

Armar líneas a medida con los tiempos por palabra

Pide output_format: "json" y agrupa tú mismo las palabras con un máximo de 42 caracteres y 6 segundos por línea, abriendo línea nueva después de cada pausa. Así controlas el largo de cada línea, que es importante cuando incrustas los subtítulos en el vídeo.

srt_tools.py

# srt_tools.py — neat subtitle cues from word timings
def srt_time(seconds):
    ms = round(seconds * 1000)
    h, ms = divmod(ms, 3_600_000)
    m, ms = divmod(ms, 60_000)
    s, ms = divmod(ms, 1000)
    return f"{h:02}:{m:02}:{s:02},{ms:03}"


def cues_from_words(words, max_chars=42, max_seconds=6.0, max_gap=0.8):
    """Group timed words into subtitle cues: (start, end, text)."""
    cues, line = [], []
    for word in words:
        text = word["word"].strip()
        if not text:
            continue
        if line:
            length = sum(len(w["word"]) + 1 for w in line) + len(text)
            if (length > max_chars
                    or word["end"] - line[0]["start"] > max_seconds
                    or word["start"] - line[-1]["end"] > max_gap):
                cues.append(line)
                line = []
        line.append({"start": word["start"], "end": word["end"], "word": text})
    if line:
        cues.append(line)
    return [(c[0]["start"], c[-1]["end"], " ".join(w["word"] for w in c)) for c in cues]


def cues_from_result(result):
    """Cues for a whole JSON result; a segment without word times becomes one cue."""
    cues = []
    for segment in result["segments"]:
        if segment.get("words"):
            cues += cues_from_words(segment["words"])
        elif segment["text"].strip():
            cues.append((segment["start"], segment["end"], segment["text"].strip()))
    return cues


def write_srt(cues, path):
    with open(path, "w", encoding="utf-8") as out:
        for number, (start, end, text) in enumerate(cues, 1):
            out.write(f"{number}\n{srt_time(start)} --> {srt_time(end)}\n{text}\n\n")

La función cues_from_result(result) toma el resultado completo y devuelve tuplas (start, end, text). Si algún modelo no entrega tiempos por palabra, usa los tiempos del segmento como respaldo. Luego, write_srt(cues, "talk.srt") guarda el archivo en disco.

Subtítulos traducidos a otro idioma

Pasa el código del idioma en translate_to y elige un modelo Oracle (oracle_mini es el más económico con traducción). El texto ya te llegará traducido. Acuérdate de dejar word_timestamps en false para tener una línea por segmento.

{
  "audio_url": "https://example.com/lecture-he.mp4",
  "model": "oracle_mini",
  "language": "he",
  "translate_to": "en",
  "output_format": "srt",
  "word_timestamps": false
}

Si necesitas el vídeo en varios idiomas, lanza un trabajo por cada idioma. Tómalo como un primer borrador y dale una vuelta a los nombres propios, las cifras y la terminología antes de publicar.

WebVTT, ASS y otros formatos

La API solo entrega json, txt o srt. Para usar una pista <video> de HTML5, convierte el SRT a WebVTT con ffmpeg:

ffmpeg -i video.srt video.vtt

Con ffmpeg puedes convertir entre la mayoría de los formatos de subtítulos siguiendo el mismo paso: basta con cambiar la extensión del archivo de salida.

Incrusta los subtítulos o adjúntalos

Quemados en el vídeo:

ffmpeg -i edited.mp4 -vf "subtitles=edited.srt" -c:a copy burned.mp4

Como pista opcional dentro de un MP4 (el usuario puede activarla o quitarla):

ffmpeg -i edited.mp4 -i edited.srt -c copy -c:s mov_text -metadata:s:s:0 language=eng soft.mp4

Tanto YouTube Studio como la mayoría de los reproductores y programas de edición aceptan el archivo .srt tal cual.

Subtítulos en hebreo y árabe

  • La API devuelve texto en Unicode estándar. De mostrarlo de derecha a izquierda se encarga el reproductor o el filtro de subtítulos de ffmpeg, que se lleva bien con el hebreo y el árabe siempre que la tipografía incluya esos caracteres.
  • Elige una fuente que soporte ese alfabeto y saca un fotograma de prueba antes de poner a codificar todo el vídeo. En nuestras pruebas, el hebreo salió bien con Arial, pero con esa misma fuente falló un carácter en una palabra árabe, así que para árabe conviene buscar otra tipografía.
ffmpeg -i video.mp4 -vf "subtitles=video.he.srt:force_style='FontName=Arial,FontSize=24'" -c:a copy burned.mp4
  • Si una línea termina en un signo de puntuación o en una palabra en alfabeto latino (como una marca o un número), un reproductor sin soporte para texto bidireccional puede mostrarla en un orden extraño. Haz la prueba en los reproductores que usa tu público.

Cuánto cuesta

TrabajoModeloCoste
Clase de 45 minutos, con subtítulosoracle1,08 US$
La misma clase, con subtítulos traducidosoracle_mini0,72 US$
Una hora de audio limpio, con subtítulosbase0,48 US$

Se cobra por segundo, sin mínimo. Consulta las tarifas en /llms/pricing.md.

Preguntas y respuestas

¿Cómo saco un archivo SRT de un vídeo?

Haz una llamada a POST /transcribe pasando la URL del vídeo, output_format: "srt" y word_timestamps: false. Luego consulta el estado del trabajo y guarda el texto de result en un archivo .srt.

¿La API devuelve archivos WebVTT?

No, solo entrega json, txt o srt. Si necesitas pasar el SRT a WebVTT, ejecuta ffmpeg -i video.srt video.vtt.

¿Por qué me sale una línea por palabra en el SRT?

Porque las marcas de tiempo por palabra están activadas por defecto, lo que genera un SRT con una línea por palabra donde se va resaltando la que suena en ese momento. Pasa word_timestamps: false si prefieres una línea por tramo, o arma tú mismo las líneas desde el JSON.

¿Puedo sacar los subtítulos directamente en otro idioma?

Sí. Pasa el código del idioma en translate_to y usa cualquiera de los modelos Oracle. El texto ya saldrá traducido.

¿Qué diferencia hay entre los subtítulos normales y los subtítulos para sordos?

Los subtítulos normales recogen o traducen los diálogos pensando en quien sí oye el audio, mientras que los subtítulos para sordos también indican los ruidos y efectos sonoros. La API te da lo que se dice con sus tiempos; las indicaciones de sonido tendrás que añadirlas aparte.