Skip to main content
POST https://api.transcriptmagic.com/api/twitter/transcript
Public video tweets are supported. Both x.com and twitter.com /status/ URLs work. The video must be under 2 minutes — X tweets are AI-transcribed, so this endpoint is slower than the others.

Request

curl -X POST https://api.transcriptmagic.com/api/twitter/transcript \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url":"https://x.com/TheoVon/status/1916982720317821050"}'
import requests

response = requests.post(
    "https://api.transcriptmagic.com/api/twitter/transcript",
    headers={"Authorization": "Bearer YOUR_API_KEY"},
    json={"url": "https://x.com/TheoVon/status/1916982720317821050"},
)

data = response.json()
print(data["transcript"])  # plain string, newline-separated
const response = await fetch("https://api.transcriptmagic.com/api/twitter/transcript", {
  method: "POST",
  headers: {
    "Authorization": "Bearer YOUR_API_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ url: "https://x.com/TheoVon/status/1916982720317821050" }),
});

const data = await response.json();
console.log(data.transcript); // plain string, newline-separated
package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"net/http"
)

func main() {
	body, _ := json.Marshal(map[string]string{
		"url": "https://x.com/TheoVon/status/1916982720317821050",
	})

	req, _ := http.NewRequest("POST",
		"https://api.transcriptmagic.com/api/twitter/transcript",
		bytes.NewBuffer(body))
	req.Header.Set("Authorization", "Bearer YOUR_API_KEY")
	req.Header.Set("Content-Type", "application/json")

	resp, _ := http.DefaultClient.Do(req)
	defer resp.Body.Close()

	var data struct {
		Transcript string `json:"transcript"`
	}
	json.NewDecoder(resp.Body).Decode(&data)
	fmt.Println(data.Transcript)
}
<?php
$ch = curl_init('https://api.transcriptmagic.com/api/twitter/transcript');

curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_API_KEY',
    'Content-Type: application/json',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
    'url' => 'https://x.com/TheoVon/status/1916982720317821050',
]));

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

$data = json_decode($response, true);
echo $data['transcript'];

Response

{
  "transcript": "Hey everyone, today I want to walk\nyou through how we built the new product line...",
  "credits": 994
}
transcript is a plain string with newlines between spoken lines — there is no per-line timing in the X response and no videoUrls field.

Supported URL formats

FormatExample
x.com statushttps://x.com/{user}/status/{id}
twitter.com statushttps://twitter.com/{user}/status/{id}
Both the x.com and legacy twitter.com domains are accepted as long as the URL points at a /status/{id} tweet. URLs without a /status/ path are rejected with 400 Invalid URL.

FAQ

Yes. Both the x.com and the legacy twitter.com /status/ URLs are accepted.
Yes — the video must be under 2 minutes. Longer videos can’t be transcribed by this endpoint.
X tweets don’t ship with captions, so the video is AI-transcribed on demand. That takes longer than the caption-based endpoints — expect a longer round trip, especially near the 2-minute limit.
Only tweets that contain a video. Text, image, and link tweets have nothing to transcribe and are rejected.
No. X transcripts are returned as plain text with newlines between lines — there is no per-line timing.