API Documentation

Learn how to integrate Subtube API into your application.

Quick Start

# JSON (default)
curl -X GET "https://subtube.app/api/v1/transcribe/?videoUrl=https://www.youtube.com/watch?v=dQw4w9WgXcQ" \
  -H "Authorization: Bearer YOUR_API_KEY"

# SRT format
curl -X GET "https://subtube.app/api/v1/transcribe/?videoUrl=https://www.youtube.com/watch?v=dQw4w9WgXcQ&format=srt" \
  -H "Authorization: Bearer YOUR_API_KEY"

# VTT format
curl -X GET "https://subtube.app/api/v1/transcribe/?videoUrl=https://www.youtube.com/watch?v=dQw4w9WgXcQ&format=vtt" \
  -H "Authorization: Bearer YOUR_API_KEY"

# Plain text (no timestamps)
curl -X GET "https://subtube.app/api/v1/transcribe/?videoUrl=https://www.youtube.com/watch?v=dQw4w9WgXcQ&format=txt" \
  -H "Authorization: Bearer YOUR_API_KEY"
const videoUrl = 'https://www.youtube.com/watch?v=dQw4w9WgXcQ';
const format = 'srt'; // json | srt | vtt | txt
const url = `https://subtube.app/api/v1/transcribe/?videoUrl=${encodeURIComponent(videoUrl)}&format=${format}`;

const response = await fetch(url, {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_API_KEY',
  }
});

if (format === 'json') {
  const data = await response.json();
  console.log(data);
} else {
  const text = await response.text();
  console.log(text);
}
import requests
from urllib.parse import urlencode

video_url = 'https://www.youtube.com/watch?v=dQw4w9WgXcQ'
format = 'srt'  # json | srt | vtt | txt
params = {'videoUrl': video_url, 'format': format}
url = f'https://subtube.app/api/v1/transcribe/?{urlencode(params)}'

headers = {
    'Authorization': 'Bearer YOUR_API_KEY'
}

response = requests.get(url, headers=headers)

if format == 'json':
    print(response.json())
else:
    print(response.text)
<?php
$videoUrl = 'https://www.youtube.com/watch?v=dQw4w9WgXcQ';
$format = 'srt'; // json | srt | vtt | txt
$url = 'https://subtube.app/api/v1/transcribe/?videoUrl=' . urlencode($videoUrl) . '&format=' . $format;

$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_API_KEY'
]);

$response = curl_exec($ch);
curl_close($ch);

if ($format === 'json') {
    $data = json_decode($response, true);
    print_r($data);
} else {
    echo $response;
}

📝 Don't have an API key yet? Create one here

Parameters

ParameterRequiredDescription
videoUrlYesYouTube video URL
formatNoResponse format: json (default), srt, vtt, txt

Response

JSON (default)

{
  "success": true,
  "videoId": "dQw4w9WgXcQ",
  "language": "en",
  "isAutoGenerated": false,
  "subtitles": [
    {
      "start": 0.0,
      "end": 2.5,
      "text": "First subtitle line"
    },
    {
      "start": 2.5,
      "end": 5.0,
      "text": "Second subtitle line"
    }
  ]
}

SRT

1
00:00:00,000 --> 00:00:02,500
First subtitle line

2
00:00:02,500 --> 00:00:05,000
Second subtitle line

VTT

WEBVTT

00:00:00.000 --> 00:00:02.500
First subtitle line

00:00:02.500 --> 00:00:05.000
Second subtitle line

TXT

First subtitle line
Second subtitle line

Error Codes

CodeDescription
400Bad Request - Invalid parameters
401Unauthorized - Invalid or missing API key
403Forbidden - API key is inactive or revoked
409Conflict - Temporarily cannot be fetched; retry later (live stream not ready, geo-restricted, or YouTube temporarily blocked the request — status: "try_again", retryable: true). Includes a Retry-After header. This is a transient, YouTube-side condition, not a subtube error (those are 5xx). See Unavailable Videos.
410Gone - Video is permanently unavailable (deleted, private, removed, etc.). Do not retry. See Unavailable Videos.
429Too Many Requests - Rate limit exceeded
500Internal Server Error - Something went wrong on our end
504Gateway Timeout - Video processing took too long

Unavailable Videos

When a video itself cannot be fetched, the response tells you exactly why and whether retrying could help. Use the retryable flag to decide: retry the same request later when it is true, give up when it is false.

  • 410 Gone — permanently unavailable, retryable: false. The video is deleted, private, removed, or otherwise gone for good.
  • 409 Conflict — temporarily unavailable, retryable: true. A later attempt may succeed (e.g. a geo-restricted video routed through a different region). A Retry-After header suggests how long to wait.

Response body

{
  "success": false,
  "status": "video_unavailable",
  "reason": "removed_by_uploader",
  "retryable": false,
  "message": "This video has been removed by the uploader and is no longer available."
}

Reasons

reasonHTTPretryableMeaning
removed_by_uploader410falseDeleted by the channel owner.
private410falseMarked private by the owner.
account_terminated410falseThe uploader's account was terminated.
terms_violation410falseRemoved for violating YouTube's Terms of Service.
copyright410falseRemoved on copyright grounds.
age_restricted410falseAge-restricted; cannot be fetched without sign-in.
unavailable410falseUnavailable for an unspecified reason.
geo_restricted409trueBlocked in the region the request was routed through.
Note: a video that exists but simply has no captions is not an "unavailable" video. That case returns 200 OK with { "success": false } and no subtitles — distinct from the 409/410 responses above.

Need Help?

If you have questions or need assistance, check out our request history to debug issues or contact support.