curl --request POST \
--url https://api.vidnavigator.com/v1/transcribe \
--header 'Content-Type: application/json' \
--header 'X-API-Key: <api-key>' \
--data '
{
"video_url": "https://www.instagram.com/reel/C86ZvEaqRmo/",
"transcript_text": false,
"all_videos": false,
"include_usage": false
}
'import requests
url = "https://api.vidnavigator.com/v1/transcribe"
payload = {
"video_url": "https://www.instagram.com/reel/C86ZvEaqRmo/",
"transcript_text": False,
"all_videos": False,
"include_usage": False
}
headers = {
"X-API-Key": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'X-API-Key': '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({
video_url: 'https://www.instagram.com/reel/C86ZvEaqRmo/',
transcript_text: false,
all_videos: false,
include_usage: false
})
};
fetch('https://api.vidnavigator.com/v1/transcribe', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.vidnavigator.com/v1/transcribe",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'video_url' => 'https://www.instagram.com/reel/C86ZvEaqRmo/',
'transcript_text' => false,
'all_videos' => false,
'include_usage' => false
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"X-API-Key: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.vidnavigator.com/v1/transcribe"
payload := strings.NewReader("{\n \"video_url\": \"https://www.instagram.com/reel/C86ZvEaqRmo/\",\n \"transcript_text\": false,\n \"all_videos\": false,\n \"include_usage\": false\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("X-API-Key", "<api-key>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.vidnavigator.com/v1/transcribe")
.header("X-API-Key", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"video_url\": \"https://www.instagram.com/reel/C86ZvEaqRmo/\",\n \"transcript_text\": false,\n \"all_videos\": false,\n \"include_usage\": false\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.vidnavigator.com/v1/transcribe")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["X-API-Key"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"video_url\": \"https://www.instagram.com/reel/C86ZvEaqRmo/\",\n \"transcript_text\": false,\n \"all_videos\": false,\n \"include_usage\": false\n}"
response = http.request(request)
puts response.read_body{
"status": "success",
"data": {
"video_info": {
"title": "<string>",
"description": "<string>",
"thumbnail": "<string>",
"url": "<string>",
"channel": "<string>",
"channel_url": "<string>",
"duration": 123,
"views": 123,
"likes": 123,
"published_date": "<string>",
"keywords": [
"<string>"
],
"category": "<string>",
"available_languages": [
"<string>"
],
"selected_language": "<string>",
"carousel_info": {
"total_items": 123,
"video_count": 123,
"image_count": 123,
"selected_index": 123
}
},
"transcript": [
{
"text": "<string>",
"start": 123,
"end": 123
}
]
},
"usage": {
"charges": [
{
"service_type": "standard_request",
"quantity": 123,
"credits": 123,
"waived": true,
"credits_saved": 123,
"tokens": {
"prompt_tokens": 123,
"completion_tokens": 123,
"total_tokens": 123
}
}
],
"total_credits": 123,
"waived": {
"credits_saved": 123
}
}
}{
"status": "error",
"error": "missing_parameter",
"message": "<string>"
}{
"status": "error",
"error": "limit_exceeded",
"message": "<string>"
}{
"status": "error",
"error": "content_restricted",
"message": "<string>"
}{
"status": "error",
"error": "video_not_found",
"message": "<string>"
}{
"status": "error",
"error": "geo_restricted",
"message": "<string>"
}{
"status": "error",
"error": "metadata_fetch_failed",
"message": "<string>"
}Transcribe Online Video
Transcribe online videos (e.g., Instagram, TikTok) using speech-to-text models when a transcript is not available.
Instagram Carousel Posts:
- For posts with multiple videos, the URL can include
?img_index=Nto select a specific video - Example:
https://www.instagram.com/p/ABC123/?img_index=2transcribes the second video - Without
img_index, the first video is transcribed - Set
all_videos=trueto transcribe ALL videos in a carousel post
Options:
transcript_text=true: Returns transcript as a single text string instead of segmentsall_videos=true: For carousel posts, returns all videos with their transcripts
Billing: on cache miss, one standard_request or residential_request is charged for the metadata fetch (residential for Instagram, Facebook-watch, Rumble; standard otherwise), plus transcription_hour proportional to the audio duration. On cache hit (same URL transcribed before for any user), only transcription_hour is charged — the metadata fetch is skipped. Users with the free_on_cache_hit sponsorship flag get the cache-hit transcription_hour waived (waived: true in the per-charge entry). Set include_usage=true to see the breakdown.
Long videos: this endpoint is synchronous and holds the connection open for the whole transcription. For media longer than ~10 minutes use POST /transcribe/async instead, which returns a task_id and can call your webhook when it finishes. Media longer than 10 minutes is rejected here with video_too_long (HTTP 400). The metadata fetch needed to read the video’s duration is billed (standard_request or residential_request) and is not refunded: the limit is documented, so the proxy call was spent answering a request that could not be served. No transcription_hour is charged — no audio is processed. Send long media to the async endpoint, which has no cap. See https://docs.vidnavigator.com/guides/async-jobs
curl --request POST \
--url https://api.vidnavigator.com/v1/transcribe \
--header 'Content-Type: application/json' \
--header 'X-API-Key: <api-key>' \
--data '
{
"video_url": "https://www.instagram.com/reel/C86ZvEaqRmo/",
"transcript_text": false,
"all_videos": false,
"include_usage": false
}
'import requests
url = "https://api.vidnavigator.com/v1/transcribe"
payload = {
"video_url": "https://www.instagram.com/reel/C86ZvEaqRmo/",
"transcript_text": False,
"all_videos": False,
"include_usage": False
}
headers = {
"X-API-Key": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'X-API-Key': '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({
video_url: 'https://www.instagram.com/reel/C86ZvEaqRmo/',
transcript_text: false,
all_videos: false,
include_usage: false
})
};
fetch('https://api.vidnavigator.com/v1/transcribe', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.vidnavigator.com/v1/transcribe",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'video_url' => 'https://www.instagram.com/reel/C86ZvEaqRmo/',
'transcript_text' => false,
'all_videos' => false,
'include_usage' => false
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"X-API-Key: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.vidnavigator.com/v1/transcribe"
payload := strings.NewReader("{\n \"video_url\": \"https://www.instagram.com/reel/C86ZvEaqRmo/\",\n \"transcript_text\": false,\n \"all_videos\": false,\n \"include_usage\": false\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("X-API-Key", "<api-key>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.vidnavigator.com/v1/transcribe")
.header("X-API-Key", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"video_url\": \"https://www.instagram.com/reel/C86ZvEaqRmo/\",\n \"transcript_text\": false,\n \"all_videos\": false,\n \"include_usage\": false\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.vidnavigator.com/v1/transcribe")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["X-API-Key"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"video_url\": \"https://www.instagram.com/reel/C86ZvEaqRmo/\",\n \"transcript_text\": false,\n \"all_videos\": false,\n \"include_usage\": false\n}"
response = http.request(request)
puts response.read_body{
"status": "success",
"data": {
"video_info": {
"title": "<string>",
"description": "<string>",
"thumbnail": "<string>",
"url": "<string>",
"channel": "<string>",
"channel_url": "<string>",
"duration": 123,
"views": 123,
"likes": 123,
"published_date": "<string>",
"keywords": [
"<string>"
],
"category": "<string>",
"available_languages": [
"<string>"
],
"selected_language": "<string>",
"carousel_info": {
"total_items": 123,
"video_count": 123,
"image_count": 123,
"selected_index": 123
}
},
"transcript": [
{
"text": "<string>",
"start": 123,
"end": 123
}
]
},
"usage": {
"charges": [
{
"service_type": "standard_request",
"quantity": 123,
"credits": 123,
"waived": true,
"credits_saved": 123,
"tokens": {
"prompt_tokens": 123,
"completion_tokens": 123,
"total_tokens": 123
}
}
],
"total_credits": 123,
"waived": {
"credits_saved": 123
}
}
}{
"status": "error",
"error": "missing_parameter",
"message": "<string>"
}{
"status": "error",
"error": "limit_exceeded",
"message": "<string>"
}{
"status": "error",
"error": "content_restricted",
"message": "<string>"
}{
"status": "error",
"error": "video_not_found",
"message": "<string>"
}{
"status": "error",
"error": "geo_restricted",
"message": "<string>"
}{
"status": "error",
"error": "metadata_fetch_failed",
"message": "<string>"
}Overview
This endpoint is designed for videos where a pre-existing transcript or caption file cannot be retrieved. It downloads the video content and processes it through an AI speech-to-text model to generate a new transcript. This is particularly useful for social media platforms where direct transcript extraction is not possible, such as Instagram Reels that don’t have captions.Synchronous or async?
This endpoint comes in two modes, with the same request body and the same result:| Mode | Endpoint | Media duration | How you get the result |
|---|---|---|---|
| Synchronous | POST /transcribe | Up to 10 minutes | In the HTTP response |
| Async | POST /transcribe/async | Any length | Poll GET /transcribe/{task_id} or receive a webhook |
400 video_too_long. Use the async mode for anything that may run past 10 minutes — it also works for short videos, so it is the safe default when you don’t know the duration.Billing
Speech-to-text processing consumestranscription_hour usage. 1 credit covers 3 hours of video/audio transcription.
On a cache miss, one standard_request or residential_request is also charged for the metadata fetch (residential for Instagram, Facebook-watch, Rumble; standard otherwise), plus transcription_hour proportional to the audio duration. On a cache hit (same URL transcribed before, by any user), only transcription_hour is charged — the metadata fetch is skipped. Users with the free_on_cache_hit sponsorship flag get the cache-hit transcription_hour waived (waived: true in the per-charge entry).
Set include_usage: true to receive a usage block with the breakdown.
The async mode is billed exactly the same way. A video_too_long rejection on the synchronous endpoint still bills the metadata fetch that read the duration (standard_request or residential_request, not refunded) but no transcription_hour.
Supported Platforms
VidNavigator can generate transcripts from these platforms using speech-to-text:- X / Twitter (just copy the tweet video link)
- Facebook (public videos only)
- TikTok
- Dailymotion
- Loom
- Vimeo
Request Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
video_url | string | Yes | URL of the video to transcribe. For Instagram carousel posts, append ?img_index=N to select a specific video. |
transcript_text | boolean | No | When true, returns the transcript as a single plain-text string instead of an array of segments. |
all_videos | boolean | No | For Instagram carousel posts only. When true, transcribes ALL videos in the post and returns them in an array. Ignored for non-carousel URLs (reels, TikTok, etc.). |
Instagram Carousel Behavior
Instagram carousel posts (e.g.https://www.instagram.com/p/.../) may contain multiple videos and/or images.
- Select a specific item: Append
?img_index=Nto the URL to select a specific item (1-based).
Example:https://www.instagram.com/p/ABC123/?img_index=2selects the second item. - Transcribe all videos: Set
all_videos=trueto transcribe all videos in the carousel. The response format changes (see “Success Response”).
400 with error=no_videos_found.Example Usage
curl -X POST "https://api.vidnavigator.com/v1/transcribe" \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"video_url": "https://www.instagram.com/reel/C86ZvEaqRmo/"
}'
from vidnavigator import VidNavigatorClient, VidNavigatorError
client = VidNavigatorClient()
try:
result = client.transcribe_video(
video_url="https://www.instagram.com/reel/C86ZvEaqRmo/"
)
print("Video Title:", result.data.video_info.title)
for segment in result.data.transcript:
print(f"[{segment.start:.2f}s - {segment.end:.2f}s] {segment.text}")
except VidNavigatorError as e:
print(f"An error occurred: {e.message}")
const { VidNavigatorClient, VidNavigatorError } = require('vidnavigator');
const client = new VidNavigatorClient({
apiKey: process.env.VIDNAVIGATOR_API_KEY,
});
async function transcribeVideo() {
try {
const { video_info, transcript } = await client.transcribeVideo({
video_url: "https://www.instagram.com/reel/C86ZvEaqRmo/"
});
console.log("Video Title:", video_info.title);
transcript.forEach(segment => {
console.log(`[${segment.start.toFixed(2)}s - ${segment.end.toFixed(2)}s] ${segment.text}`);
});
} catch (error) {
if (error instanceof VidNavigatorError) {
console.error(`An error occurred: ${error.message}`);
} else {
console.error("An unexpected error occurred:", error);
}
}
}
transcribeVideo();
Instagram Carousel (select a specific video with ?img_index=2)
curl -X POST "https://api.vidnavigator.com/v1/transcribe" \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"video_url": "https://www.instagram.com/p/ABC123/?img_index=2"
}'
Instagram Carousel (transcribe all videos with all_videos=true)
curl -X POST "https://api.vidnavigator.com/v1/transcribe" \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"video_url": "https://www.instagram.com/p/ABC123/",
"all_videos": true
}'
Success Response (200 OK)
This endpoint has two success response shapes (aoneOf), depending on whether you set all_videos=true:
- Single-video response (default): returns
data.video_info+data.transcript - All-videos response (
all_videos=true): returnsdata.carousel_info+data.videos[]
transcript_text=true affects the shape of each returned transcript (string vs segments) in both response types.Single-video response (default)
{
"status": "success",
"data": {
"video_info": {
"title": "A Reel Interesting Video",
"description": "An example description for the video.",
"thumbnail": "https://example.com/thumbnail.jpg",
"url": "https://www.instagram.com/reel/C86ZvEaqRmo/",
"channel": "Example Creator",
"duration": 58.5,
"views": 123456,
"likes": 7890,
"published_date": "2024-07-01",
"keywords": ["example", "social media", "video"],
"category": "Entertainment"
},
"transcript": [
{
"text": "This is the first sentence of the video.",
"start": 0.5,
"end": 3.2
},
{
"text": "And this is the second.",
"start": 3.5,
"end": 5.1
}
]
}
}
All-videos response (all_videos=true)
{
"status": "success",
"data": {
"carousel_info": {
"total_items": 5,
"video_count": 2,
"image_count": 3,
"transcribed_count": 2,
"total_duration": 123.4
},
"videos": [
{
"index": 1,
"status": "success",
"video_info": {
"title": "Video 1",
"description": "Example description",
"thumbnail": "https://example.com/thumb1.jpg",
"url": "https://www.instagram.com/p/ABC123/?img_index=1",
"channel": "Example Creator",
"duration": 60.0,
"views": 123,
"likes": 45,
"published_date": "2024-07-01",
"keywords": ["example"],
"category": "Entertainment"
},
"transcript": [
{ "text": "First line", "start": 0.0, "end": 2.0 }
]
},
{
"index": 2,
"status": "error",
"error": "transcript_not_available",
"message": "Transcript could not be generated for this video."
}
]
}
}
Error Responses
| Status | error | Description |
|---|---|---|
400 | missing_parameter, invalid_parameter, invalid_url, unsupported_url, unsupported_platform, request_body_required | Invalid request. |
400 | no_videos_found | The post carries no video (e.g. an image-only carousel). |
400 | video_too_long | The media is longer than 10 minutes. Use POST /transcribe/async. |
402 | limit_exceeded | Not enough credits. |
403 | content_restricted | The content requires login or age verification. |
404 | video_not_found, video_unavailable, transcript_not_available | The video can’t be found or accessed, or no transcript could be generated. |
451 | geo_restricted | The content is not available in this region. |
500 | metadata_fetch_failed, audio_extraction_failed, transcription_failed, internal_server_error | Processing failed. Charges are reverted. |
Authorizations
API key authentication. Include your VidNavigator API key in the X-API-Key header.
Body
URL of the video to transcribe. For Instagram carousel posts, append ?img_index=N to select a specific video.
"https://www.instagram.com/reel/C86ZvEaqRmo/"
When true, returns the transcript as a single plain-text string instead of an array of segments.
For carousel posts only. When true, transcribes ALL videos in the post and returns them in an array. Ignored for non-carousel URLs (reels, TikTok, etc.).
When true, the response includes a usage block listing every meter charged during this request, the total credits deducted, and the user's remaining balance.
Response
Video transcribed successfully. Response format depends on all_videos parameter.
- Option 1
- Option 2
Single video response (default)
success Show child attributes
Show child attributes
Per-call usage disclosure. Returned only when the caller passes include_usage=true in the request body. Lists every meter that fired during this request and the credits actually deducted. Multiple charges of the same meter inside one request are consolidated into a single entry (their quantities and credits are summed). When a charge was waived through a cache-hit sponsorship (e.g. NGO), it carries waived: true + credits_saved, and a top-level waived.credits_saved summary appears.
For endpoints that involve LLM analysis (/extract/video, /extract/file, /analyze/video, /analyze/file, /youtube/search), the consolidated analysis_request charge entry carries a nested tokens object reporting the LLM input/output token tally for the request.
Show child attributes
Show child attributes

