Textract Basics
Six examples to get you started with Amazon Textract - four basic and two intermediate.
Busca en todas las páginas de la documentación
Six examples to get you started with Amazon Textract - four basic and two intermediate.
Each example shows the same operation in both Python (boto3) and TypeScript (AWS SDK for JavaScript v3). Every call here uses DetectDocumentText, Textract's synchronous, raw-OCR operation - no forms, no tables, no queries. That structured extraction is covered in the next few pages; this page is about getting text out reliably first.
pip install boto3 (boto3 1.43.x, Python 3.10+). TypeScript: npm install @aws-sdk/client-textract (Node.js 18+).aws configure, environment variables, or an IAM role - the SDK resolves them automatically.DetectDocumentText reads an image's bytes directly and returns every line and word it found, in reading order.
# --- Python (boto3) ---
import boto3
textract = boto3.client("textract", region_name="us-east-1")
with open("receipt.png", "rb") as f:
image_bytes = f.read()
response = textract.detect_document_text(Document={"Bytes": image_bytes})
for block in response["Blocks"]:
if block["BlockType"] == "LINE":
print(block["Text"])// --- TypeScript (AWS SDK v3) ---
import { TextractClient, DetectDocumentTextCommand } from "@aws-sdk/client-textract";
import { readFileSync } from "node:fs";
const textract = new TextractClient({ region: "us-east-1" });
const imageBytes = readFileSync("receipt.png");
const response = await textract.send(new DetectDocumentTextCommand({
Document: { Bytes: imageBytes },
}));
for (const block of response.Blocks ?? []) {
if (block.BlockType === "LINE") console.log(block.Text);
}Document.Bytes takes raw file bytes directly, capped at 5 MB for this path.Blocks array - filtering on BlockType === "LINE" gives you readable lines without the individual WORD entries underneath them.Bytes input are JPEG and PNG only - PDF requires the S3 path shown next.Related: Textract's Structured-Extraction Model - what raw OCR does and does not give you.
For PDFs, larger images, or documents already stored in your pipeline, reference an S3 object instead of sending bytes.
# --- Python (boto3) ---
import boto3
textract = boto3.client("textract")
response = textract.detect_document_text(
Document={"S3Object": {"Bucket": "my-bucket", "Name": "uploads/receipt.png"}},
)
for block in response["Blocks"]:
if block["BlockType"] == "LINE":
print(block["Text"])// --- TypeScript (AWS SDK v3) ---
import { TextractClient, DetectDocumentTextCommand } from "@aws-sdk/client-textract";
const textract = new TextractClient({});
const response = await textract.send(new DetectDocumentTextCommand({
Document: { S3Object: { Bucket: "my-bucket", Name: "uploads/receipt.png" } },
}));
for (const block of response.Blocks ?? []) {
if (block.BlockType === "LINE") console.log(block.Text);
}S3Object raises the size ceiling to 10 MB and lets Textract read the file directly - your process never downloads it.s3:GetObject permission on the referenced bucket and key.S3Object path; a multi-page PDF does not - that needs the async operations covered later in this section.DetectDocumentText returns both LINE and WORD blocks in the same flat array - a line's CHILD relationship lists the Ids of the words that make it up.
# --- Python (boto3) ---
import boto3
textract = boto3.client("textract")
with open("receipt.png", "rb") as f:
response = textract.detect_document_text(Document={"Bytes": f.read()})
blocks_by_id = {b["Id"]: b for b in response["Blocks"]}
for block in response["Blocks"]:
if block["BlockType"] != "LINE":
continue
word_ids = [
child_id
for rel in block.get("Relationships", [])
if rel["Type"] == "CHILD"
for child_id in rel["Ids"]
]
words = [blocks_by_id[wid]["Text"] for wid in word_ids if blocks_by_id[wid]["BlockType"] == "WORD"]
print(block["Text"], "->", words)// --- TypeScript (AWS SDK v3) ---
import { TextractClient, DetectDocumentTextCommand, Block } from "@aws-sdk/client-textract";
import { readFileSync } from "node:fs";
const textract = new TextractClient({});
const response = await textract.send(new DetectDocumentTextCommand({
Document: { Bytes: readFileSync("receipt.png") },
}));
const blocks: Block[] = response.Blocks ?? [];
const blocksById = new Map(blocks.map((b) => [b.Id, b]));
for (const block of blocks) {
if (block.BlockType !== "LINE") continue;
const wordIds = (block.Relationships ?? [])
.filter((rel) => rel.Type === "CHILD")
.flatMap((rel) => rel.Ids ?? []);
const words = wordIds
.map((id) => blocksById.get(id))
.filter((b): b is Block => b?.BlockType === "WORD")
.map((b) => b.Text);
console.log(block.Text, "->", words);
}LINE block's Text is already the full assembled line - walking to its WORD children is only needed when you want per-word confidence or bounding boxes.Relationships is a list because a block can have more than one relationship type; CHILD is the one that points to constituent parts.Id-keyed lookup dictionary once, up front, avoids re-scanning the full Blocks array for every relationship you follow.Id/Relationships pattern is how every structured block type in Textract links to its data - forms and tables covered next just add more block types to walk.Synchronous operations accept a single-page PDF through the same Document parameter as an image - no different call, just a different S3 object.
# --- Python (boto3) ---
import boto3
textract = boto3.client("textract")
response = textract.detect_document_text(
Document={"S3Object": {"Bucket": "my-bucket", "Name": "uploads/one-page-form.pdf"}},
)
line_count = sum(1 for b in response["Blocks"] if b["BlockType"] == "LINE")
print(f"{line_count} lines detected")// --- TypeScript (AWS SDK v3) ---
import { TextractClient, DetectDocumentTextCommand } from "@aws-sdk/client-textract";
const textract = new TextractClient({});
const response = await textract.send(new DetectDocumentTextCommand({
Document: { S3Object: { Bucket: "my-bucket", Name: "uploads/one-page-form.pdf" } },
}));
const lineCount = (response.Blocks ?? []).filter((b) => b.BlockType === "LINE").length;
console.log(`${lineCount} lines detected`);DetectDocumentText and AnalyzeDocument both accept a PDF only if it's a single page and referenced via S3Object, not raw Bytes.PAGE block is still present in the response even for a one-page PDF - it's the root of the block graph for that page.Related: Asynchronous Document Analysis for Multi-Page Files - the job-based pattern for multi-page PDFs.
Every block carries its own Confidence score - filtering on it server-side in your own code keeps low-quality reads out of downstream logic.
# --- Python (boto3) ---
import boto3
textract = boto3.client("textract")
with open("receipt.png", "rb") as f:
response = textract.detect_document_text(Document={"Bytes": f.read()})
MIN_CONFIDENCE = 80.0
trusted_lines = [
b["Text"] for b in response["Blocks"]
if b["BlockType"] == "LINE" and b["Confidence"] >= MIN_CONFIDENCE
]
flagged = [
b["Text"] for b in response["Blocks"]
if b["BlockType"] == "LINE" and b["Confidence"] < MIN_CONFIDENCE
]
print(f"{len(trusted_lines)} trusted, {len(flagged)} flagged for review")// --- TypeScript (AWS SDK v3) ---
import { TextractClient, DetectDocumentTextCommand } from "@aws-sdk/client-textract";
import { readFileSync } from "node:fs";
const textract = new TextractClient({});
const response = await textract.send(new DetectDocumentTextCommand({
Document: { Bytes: readFileSync("receipt.png") },
}));
const MIN_CONFIDENCE = 80.0;
const lines = (response.Blocks ?? []).filter((b) => b.BlockType === "LINE");
const trustedLines = lines.filter((b) => (b.Confidence ?? 0) >= MIN_CONFIDENCE).map((b) => b.Text);
const flagged = lines.filter((b) => (b.Confidence ?? 0) < MIN_CONFIDENCE).map((b) => b.Text);
console.log(`${trustedLines.length} trusted, ${flagged.length} flagged for review`);DetectDocumentText and AnalyzeDocument have no MinConfidence request parameter - every block comes back regardless of score, and filtering happens in your own code.Confidence is a 0-100 float on every block, not just LINE - apply the same check to WORD, KEY_VALUE_SET, and CELL blocks once you're working with structured output.A common real-world shape: text arrives as an in-memory buffer from a web upload, not a file already on disk.
# --- Python (boto3) ---
import boto3
textract = boto3.client("textract")
def extract_text(document_bytes: bytes) -> list[str]:
response = textract.detect_document_text(Document={"Bytes": document_bytes})
return [b["Text"] for b in response["Blocks"] if b["BlockType"] == "LINE"]
# document_bytes would come from an upload handler, not open() - shown here for a self-contained example.
with open("upload.png", "rb") as f:
lines = extract_text(f.read())
print(lines[:3])// --- TypeScript (AWS SDK v3) ---
import { TextractClient, DetectDocumentTextCommand } from "@aws-sdk/client-textract";
import { readFileSync } from "node:fs";
const textract = new TextractClient({});
async function extractText(documentBytes: Uint8Array): Promise<string[]> {
const response = await textract.send(new DetectDocumentTextCommand({
Document: { Bytes: documentBytes },
}));
return (response.Blocks ?? []).filter((b) => b.BlockType === "LINE").map((b) => b.Text ?? "");
}
// documentBytes would come from an upload handler, not readFileSync - shown here for a self-contained example.
const lines = await extractText(readFileSync("upload.png"));
console.log(lines.slice(0, 3));Document.Bytes accepts any bytes/Uint8Array value, whether it came from disk, a web framework's upload buffer, or another service's response.extract_text/extractText keeps the 5 MB Bytes cap and the S3 fallback path (example 2) as a single decision point in your codebase.S3Object instead.Stack versions: This page was written for boto3 1.43.x (Python 3.10+) and the AWS SDK for JavaScript v3 (Node.js 18+).
Revisado por Chris St. John·Última actualización: 24 jul 2026