> Source: https://txtfetch.com/extract/captions > Plain-text twin — every page on txtfetch.com has one. https://txtfetch.com/text --- # Where the text in a video or audio file actually lives. Not every media file has text in it. What you get back depends entirely on the container. This page maps a caption sidecar, a tagged MP3, and a plain WAV to what each one actually returns. That includes the cases where the honest answer is nothing. the-problem "Extract text from video" usually bundles two different jobs into one request. One is pulling whatever text is already sitting in the file. The other is listening to the audio and writing down what's said. Almost nothing documents the difference. Every container handles the first job differently, and none of them do the second at all. A caption sidecar (.srt/.vtt/.ttml) carries a real transcript, but wrapped in cue numbers and timestamps that a naive read prints right alongside the words. An .mp3 or .mp4 carries whatever tag data its encoder wrote: a title, an artist, sometimes nothing. That tag data is the only text in the file, unless a caption track was muxed in beside it. Some containers have no text-bearing parser registered at all, checked against the exact Tika 3.3.1 build this API runs. They detect fine off their bytes, and then hand back nothing. one-request-solution txtfetch reads whatever text genuinely exists in the container, and says so plainly. A caption sidecar comes back close to verbatim, cue numbers, timestamps, and all. Cleaning that up is a separate job (see the free tool below). A tagged .mp3, .mp4, .ogg, or .flac comes back as a few bare lines of tag data, not a transcript. A file with nothing parseable, a plain .wav, an .mkv, a .webm, returns an explicit error instead of a silent empty success. The free in-browser subtitle tool goes further than this API in one respect. It opens an .mp4 or .mkv itself and reads the embedded caption track, client-side. That's a genuinely different capability, not the same thing described twice. txtfetch does not transcribe audio or video. If a file's words only ever existed as sound, run it through speech recognition first, or pull the platform's own caption track. Then send the resulting text through txtfetch like any other document. curl ```curl curl -X POST https://api.txtfetch.com/v1/extract \ -H "Authorization: Bearer $TXTFETCH_KEY" \ -F file=@team-standup.mp4 ``` Python ```python import os import requests with open("team-standup.mp4", "rb") as f: r = requests.post( "https://api.txtfetch.com/v1/extract", headers={"Authorization": f"Bearer {os.environ['TXTFETCH_KEY']}"}, files={"file": f}, ) print(r.json()["extracted_text"]) ``` JavaScript ```javascript import { readFile } from "node:fs/promises"; const file = new Blob([await readFile("team-standup.mp4")]); const form = new FormData(); form.append("file", file, "team-standup.mp4"); const res = await fetch("https://api.txtfetch.com/v1/extract", { method: "POST", headers: { Authorization: `Bearer ${process.env.TXTFETCH_KEY}` }, body: form, }); const { extracted_text } = await res.json(); console.log(extracted_text); ``` Go ```go package main import ( "bytes" "encoding/json" "fmt" "io" "mime/multipart" "net/http" "os" ) type extractResponse struct { Status string `json:"status"` ExtractedText string `json:"extracted_text"` } func main() { f, err := os.Open("team-standup.mp4") if err != nil { panic(err) } defer f.Close() var body bytes.Buffer writer := multipart.NewWriter(&body) part, err := writer.CreateFormFile("file", "team-standup.mp4") if err != nil { panic(err) } if _, err := io.Copy(part, f); err != nil { panic(err) } writer.Close() req, err := http.NewRequest("POST", "https://api.txtfetch.com/v1/extract", &body) if err != nil { panic(err) } req.Header.Set("Authorization", "Bearer "+os.Getenv("TXTFETCH_KEY")) req.Header.Set("Content-Type", writer.FormDataContentType()) resp, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer resp.Body.Close() var result extractResponse if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { panic(err) } fmt.Println(result.ExtractedText) } ``` Or skip the download. Pass a `url` parameter and txtfetch fetches the document server-side: curl ```curl curl -X POST "https://api.txtfetch.com/v1/extract?url=https://example.com/webinar-captions.vtt" \ -H "Authorization: Bearer $TXTFETCH_KEY" ``` Python ```python import os import requests r = requests.post( "https://api.txtfetch.com/v1/extract", headers={"Authorization": f"Bearer {os.environ['TXTFETCH_KEY']}"}, params={"url": "https://example.com/webinar-captions.vtt"}, ) print(r.json()["extracted_text"]) ``` JavaScript ```javascript const endpoint = new URL("https://api.txtfetch.com/v1/extract"); endpoint.searchParams.set("url", "https://example.com/webinar-captions.vtt"); const res = await fetch(endpoint, { method: "POST", headers: { Authorization: `Bearer ${process.env.TXTFETCH_KEY}` }, }); const { extracted_text } = await res.json(); console.log(extracted_text); ``` Go ```go package main import ( "encoding/json" "fmt" "net/http" "net/url" "os" ) type extractResponse struct { Status string `json:"status"` ExtractedText string `json:"extracted_text"` } func main() { endpoint, err := url.Parse("https://api.txtfetch.com/v1/extract") if err != nil { panic(err) } q := endpoint.Query() q.Set("url", "https://example.com/webinar-captions.vtt") endpoint.RawQuery = q.Encode() req, err := http.NewRequest("POST", endpoint.String(), nil) if err != nil { panic(err) } req.Header.Set("Authorization", "Bearer "+os.Getenv("TXTFETCH_KEY")) resp, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer resp.Body.Close() var result extractResponse if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { panic(err) } fmt.Println(result.ExtractedText) } ``` ``` { "status": "success", "extracted_text": "..." } ``` what-comes-back The corpus behind [/diff](https://txtfetch.com/diff) has no recorded media captions document yet, so we have nothing honest to show you here. Run one of your own instead. The [free converter](https://txtfetch.com/tools/file-to-text) reads the file in your browser, and nothing is uploaded. formats-covered - `.srt` - `.vtt` - `.ttml` - `.mp4` - `.mov` - `.m4a` - `.mp3` - `.ogg` - `.flac` response-options Need JSON instead of plain text? ?format=json wraps the same extracted\_text in the typed envelope every route uses. For media files that means the same tag lines or the same error, not new fields this parser doesn't produce. faq **What comes back for an .srt or .vtt caption file?**: Close to what's already in the file: the plain-text body, cue numbers and timestamps included. This route doesn't clean anything up. It hands back the sidecar's text as-is, which is exactly the raw material the free subtitle-to-text tool strips down into a real transcript. **Is a .ttml or .dfxp caption file different?**: Yes, in a way worth knowing about. TTML stores its timing in XML attributes, not in the text itself. Once the markup is parsed away, what's left is just the caption lines: no cue numbers, no timestamps left to strip. An .srt or .vtt of the identical dialogue comes back messier than the .ttml version of the same thing. **What does an .mp3, .mp4, .ogg, or .flac file return if it has no caption track?**: Whatever tag data the encoder wrote, and nothing else. That's a title, an artist, sometimes an album, run together as a few bare lines with no field names attached. It's real text pulled from the file, not a transcript of anything spoken in it. The same file with no tags at all, and no caption track either, returns an error instead of an empty success. **Does a .wav file's title/artist tags come back the same way?**: No, and this is the one genuine surprise checking against a real build turned up. A .wav can carry the same kind of title/artist tags an .mp3 does. But the pinned engine's WAV reader only reports technical properties, sample rate, channel count, bit depth, and never those tag values. A tagged .wav with no other text in it returns the same extraction error as a file with no tags at all. **What happens with an .mkv, .webm, or .avi file?**: An explicit error, not an empty success. The pinned Tika 3.3.1 build recognizes all three from their bytes, but has no registered parser behind any of them. That was confirmed by sending one through the exact request this API makes. It's a real difference from a container returning a blank string: you find out immediately, instead of getting a silent, empty result. **Does txtfetch transcribe speech from audio or video?**: No. Every answer on this page is about text that already existed in the file, a caption sidecar, a container tag, never audio converted into words. If a file's content only ever existed as sound, run it through a speech-to-text tool first. Whisper, a hosted ASR API, or a platform's own auto-captions all work. Then send the resulting transcript back through txtfetch like any other text file. go-further - [Read the media captions guide →](https://txtfetch.com/blog/text-from-video-and-audio) - [Clean captions into a real transcript, free →](https://txtfetch.com/tools/subtitles-to-text) - [Check what's genuinely parsed vs. only detected →](https://txtfetch.com/formats/coverage) - [Markup stripped a different way, the HTML/XML guide →](https://txtfetch.com/extract/html) - [API quickstart →](https://txtfetch.com/docs) - [How the pipeline works →](https://txtfetch.com/how-it-works) - [Extract it from your language →](https://txtfetch.com/for) - [Get an API key →](https://app.txtfetch.com/signup) more-formats - [All formats →](https://txtfetch.com/extract) ## Send a real caption file through it. One HTTP call returns the text. Read one in your browser first, for free. [Get an API key →](https://app.txtfetch.com/signup) [Open the free caption file reader →](https://txtfetch.com/tools/subtitles-to-text)