https://txtfetch.com/extract/image/
OCR that's just another curl request.
Scanned receipts, faxed contracts, photographed whiteboards. Tesseract OCR runs automatically, with no separate OCR pipeline to stand up.
the-problem
Scans and photos aren't 'documents' to most extraction stacks. They're a special case. Handling them usually means a separate OCR service, with its own SDK and its own error handling. It also means a branch in your ingestion code, to detect when a file needs OCR instead of ordinary parsing. Standing that up, and keeping it patched, is its own project.
one-request-solution
In txtfetch, OCR isn't a separate code path. It's just what happens when pixels arrive. POST a PNG, JPG, or TIFF (or a scanned PDF), and Tesseract OCR runs automatically behind the same /v1/extract endpoint. You get back the same { status, extracted_text } shape as any other format.
curl -X POST https://api.txtfetch.com/v1/extract \
-H "Authorization: Bearer $TXTFETCH_KEY" \
-F file=@scanned-invoice.pngimport os
import requests
with open("scanned-invoice.png", "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"])import { readFile } from "node:fs/promises";
const file = new Blob([await readFile("scanned-invoice.png")]);
const form = new FormData();
form.append("file", file, "scanned-invoice.png");
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);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("scanned-invoice.png")
if err != nil {
panic(err)
}
defer f.Close()
var body bytes.Buffer
writer := multipart.NewWriter(&body)
part, err := writer.CreateFormFile("file", "scanned-invoice.png")
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 -X POST "https://api.txtfetch.com/v1/extract?url=https://example.com/uploads/receipt-photo.jpg" \
-H "Authorization: Bearer $TXTFETCH_KEY"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/uploads/receipt-photo.jpg"},
)
print(r.json()["extracted_text"])const endpoint = new URL("https://api.txtfetch.com/v1/extract");
endpoint.searchParams.set("url", "https://example.com/uploads/receipt-photo.jpg");
const res = await fetch(endpoint, {
method: "POST",
headers: { Authorization: `Bearer ${process.env.TXTFETCH_KEY}` },
});
const { extracted_text } = await res.json();
console.log(extracted_text);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/uploads/receipt-photo.jpg")
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 has no recorded .png document yet, so we have nothing honest to show you here. Run one of your own instead. The free converter reads the file in your browser, and nothing is uploaded.
formats-covered
.png.jpg.jpeg.tiff.tif.bmp.gif
faq
- How do I OCR a scanned document via API?
- POST the image as multipart form data to https://api.txtfetch.com/v1/extract. Tesseract OCR runs automatically. The response is { "status": "success", "extracted_text": "..." }, with no OCR-specific parameters needed.
- Which image formats are supported for OCR?
- PNG, JPG/JPEG, TIFF, BMP, and GIF are all routed through Tesseract OCR automatically, the same as scanned PDF pages with no text layer.
- Do I need to tell txtfetch that a file needs OCR?
- No. Format detection is automatic. Any file with no extractable text layer, image or PDF, is OCR'd without any extra flag or parameter.
go-further
Images & OCR
Send a real .png through it.
One HTTP call returns the text. Read one in your browser first, for free.
Get an API key →