> Source: https://txtfetch.com/migrate/aws-textract > Plain-text twin — every page on txtfetch.com has one. https://txtfetch.com/text --- # Migrate from AWS Textract to txtfetch A flat Blocks array, joined by hand. That join is the whole adapter. Here's what your code looks like before and after. ## Where you are today The call below is DetectDocumentText, checked against AWS Textract's current docs. Python ```python import boto3 client = boto3.client("textract") with open("report.pdf", "rb") as f: response = client.detect_document_text(Document={"Bytes": f.read()}) lines = [b["Text"] for b in response["Blocks"] if b["BlockType"] == "LINE"] text = "\n".join(lines) ``` Checked against [AWS Textract's docs](https://docs.aws.amazon.com/textract/latest/dg/API_DetectDocumentText.html) on 2026-09-10, using boto3 1.43.x. the call that replaces it One HTTP call. No SDK to install, and no job to poll. curl ```curl curl -X POST https://api.txtfetch.com/v1/extract \ -H "Authorization: Bearer $TXTFETCH_KEY" \ -F file=@report.pdf ``` Python ```python import os import requests with open("report.pdf", "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("report.pdf")]); const form = new FormData(); form.append("file", file, "report.pdf"); 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); ``` drop-in adapter A short function standing in for `[b["Text"] for b in response["Blocks"] if b["BlockType"] == "LINE"]`. Change one function, not your whole pipeline. Python ```python import os import requests def detect_text_like_blocks(file_path: str) -> list[dict]: """Drop-in swap for the LINE blocks in response["Blocks"]. Downstream code that reads b["Text"] per LINE block keeps working unmodified. txtfetch returns one block for the whole document, not one per line. """ with open(file_path, "rb") as f: r = requests.post( "https://api.txtfetch.com/v1/extract", headers={"Authorization": f"Bearer {os.environ['TXTFETCH_KEY']}"}, files={"file": f}, ) r.raise_for_status() text = r.json()["extracted_text"] return [{"BlockType": "LINE", "Text": line} for line in text.splitlines()] ``` JavaScript ```javascript import { readFile } from "node:fs/promises"; // Drop-in swap for the LINE blocks in response.Blocks. Downstream code // that reads b.Text per LINE block keeps working unmodified. async function detectTextLikeBlocks(filePath) { const file = new Blob([await readFile(filePath)]); const form = new FormData(); form.append("file", file, filePath); 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(); return extracted_text.split("\n").map((line) => ({ BlockType: "LINE", Text: line })); } ``` field mapping | AWS Textract field | txtfetch equivalent | Note | | --- | --- | --- | | Blocks\[\] where BlockType == "LINE" | extracted\_text, split on newline | Textract returns lines in reading order per page. Join them, or split txtfetch's text the same way. | | Block.Confidence (per word/line) | not returned | The plain-text response carries no per-line or per-word confidence score. | | Block.Geometry (BoundingBox, Polygon) | not returned | No coordinates come back. See what you lose below. | | AnalyzeDocument FORMS (key-value pairs) | not returned | txtfetch has no forms-analysis mode. Keep AnalyzeDocument for key-value extraction. | what you lose - Per-word and per-line Confidence scores. - Geometry: BoundingBox and Polygon coordinates for every block. - AnalyzeDocument's FORMS mode (key-value pairs) and TABLES mode (structured cells). - SIGNATURES detection. - Deep AWS IAM/VPC integration if you're already running there. See the full capability table on [txtfetch vs AWS Textract](https://txtfetch.com/compare/aws-textract). what you gain - Hundreds of formats through one endpoint (see /formats/coverage), not PDF, PNG, JPEG, and TIFF only. - One HTTP call instead of a boto3 client and a Blocks array to walk. - Billing per document, not per 1,000 pages. See the pricing math. The pricing math behind per-document billing lives on [why per-page pricing punishes long documents](https://txtfetch.com/compare/per-page-pricing). ## Cutover Run both calls on the same file while you switch traffic over. Diff the two outputs, or clean up formatting differences first on pages where a table mattered. - [Compare outputs side by side](https://txtfetch.com/diff), to dual-run both calls on the same file. - [Clean up extracted text](https://txtfetch.com/tools/clean-extracted-text), if the two outputs disagree on formatting. sources - [AWS: DetectDocumentText API reference](https://docs.aws.amazon.com/textract/latest/dg/API_DetectDocumentText.html) Accessed 2026-09-10 - [boto3: Textract client reference (detect\_document\_text)](https://docs.aws.amazon.com/boto3/latest/reference/services/textract/client/detect_document_text.html) Accessed 2026-09-10 - [AWS: How Amazon Textract Works: Lines and Words](https://docs.aws.amazon.com/textract/latest/dg/how-it-works-lines-words.html) Accessed 2026-09-10 - [AWS Textract: Pricing](https://aws.amazon.com/textract/pricing/) Accessed 2026-09-10 frequently asked questions **Does txtfetch return Textract's Blocks array?**: No. txtfetch returns one plain-text string. If your code reads response["Blocks"], use the adapter above or rewrite it to read one string. **Can I keep Textract for forms and tables, and use txtfetch for plain text?**: Yes. Some teams run AnalyzeDocument only on the documents that need key-value pairs or structured tables, and send everything else to txtfetch. **Do Textract's LINE blocks already come back in reading order?**: Yes, per AWS's own docs, for a single-column page. A multi-column layout can still need reordering on either side. other migrations - [Migrate from Unstructured.io →](https://txtfetch.com/migrate/unstructured) - [Migrate from LlamaParse →](https://txtfetch.com/migrate/llamaparse) - [Migrate from Azure AI Document Intelligence →](https://txtfetch.com/migrate/azure-document-intelligence) - [Migrate from Mindee →](https://txtfetch.com/migrate/mindee) - [All migrations →](https://txtfetch.com/migrate) ## Swap the call. Keep the pipeline. Point your AWS Textract code at txtfetch, then diff the output before you cut over. [Get an API key →](https://app.txtfetch.com/signup) [See the migration guide →](https://txtfetch.com/migrate)