Turn incoming documents into structured action.

Invoices, inbound email, and scanned forms all start the same way. Extract the text first, then classify, route, or act on it downstream.

the-problem

Document-driven workflows share the same first step: turn an incoming file into text before any business logic runs. That step is usually the least reliable part of the pipeline. The inputs are the least controlled: a photographed form, a forwarded email thread, a scan from a fax gateway. Stitching together OCR, a mail parser, and a document library often breaks on the one case nobody tested.

how-txtfetch-solves-it

txtfetch is the extraction step, not a workflow engine. POST the incoming file, or its URL, and get back plain text, whatever the source. Async mode with a webhook_url means the workflow never blocks on OCR. The callback fires when the text is ready, and your automation, a queue worker, or a Lambda, picks up from there.

  • OCR runs automatically on scanned forms and photographed documents, with no separate vision service to wire in.
  • Email extraction (.eml/.msg/.mbox) includes headers and attachment text, useful for routing rules.
  • Webhook callbacks (webhook_url) mean a long-running OCR job never holds a workflow connection open.
  • Webhook payloads are HMAC-signed and verifiable with the SDK's verifyWebhook or verify_webhook helper.
  • Idempotency-Key support stops a retried webhook delivery from reprocessing the same document twice.
  • The same endpoint handles a PDF, a scanned form, and an email thread with no branch in your workflow code.
  • A queue worker, a Lambda function, or a Zapier automation can each pick up the extracted text the same way.
curl
curl -X POST https://api.txtfetch.com/v1/extract \
  -H "Authorization: Bearer $TXTFETCH_KEY" \
  -F file=@scanned-form.png
Python
import os
import requests

with open("scanned-form.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"])
JavaScript
import { readFile } from "node:fs/promises";

const file = new Blob([await readFile("scanned-form.png")]);
const form = new FormData();
form.append("file", file, "scanned-form.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);
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("scanned-form.png")
	if err != nil {
		panic(err)
	}
	defer f.Close()

	var body bytes.Buffer
	writer := multipart.NewWriter(&body)
	part, err := writer.CreateFormFile("file", "scanned-form.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)
}
{
  "status": "success",
  "extracted_text": "..."
}

faq

Can txtfetch OCR a photographed form for an automation pipeline?
Yes. POST the image (PNG/JPG/TIFF) to /v1/extract, and Tesseract OCR runs automatically. The response is the same { status, extracted_text } shape as any other format.
How do I avoid blocking a workflow on a slow OCR job?
Pass async=true and supply a webhook_url. txtfetch calls it back with the result when extraction finishes, so the workflow can move on.
Are webhook deliveries verifiable, so I know they came from txtfetch?
Yes. Each webhook is HMAC-SHA256 signed. Verify it with the SDK's verifyWebhook (JS) or verify_webhook (Python) helper. You can also reproduce the raw HMAC check documented at /docs/async.

related-reading

Start on the free plan.

Run your own documents through it before you commit. The Hobby plan needs no card.

Get started →

See the pricing →