Textract via SDK Best Practices
This is the checklist to run before and after taking a Textract workload to production from the SDK.
Search across all documentation pages
This is the checklist to run before and after taking a Textract workload to production from the SDK.
Each item is a rule stated positively, with a one-line rationale. Work top to bottom - the groups run from confidence handling and input quality, through request patterns and structured-extraction efficiency, to reliability, human review, and security. Most rules apply whether you call Textract from boto3 or the AWS SDK v3.
MinConfidence request parameter like Rekognition does - every block comes back regardless of score, and thresholding is entirely your responsibility.Apply the threshold as a filtering step immediately after the call, before any downstream logic sees the data.
# --- Python (boto3) ---
import boto3
textract = boto3.client("textract")
with open("form.png", "rb") as f:
response = textract.analyze_document(Document={"Bytes": f.read()}, FeatureTypes=["FORMS"])
MIN_CONFIDENCE = 85.0
low_confidence = [
b for b in response["Blocks"]
if b["BlockType"] == "KEY_VALUE_SET" and b["Confidence"] < MIN_CONFIDENCE
]
if low_confidence:
print(f"{len(low_confidence)} fields below threshold - route to review")// --- TypeScript (AWS SDK v3) ---
import { TextractClient, AnalyzeDocumentCommand } from "@aws-sdk/client-textract";
import { readFileSync } from "node:fs";
const textract = new TextractClient({});
const response = await textract.send(new AnalyzeDocumentCommand({
Document: { Bytes: readFileSync("form.png") },
FeatureTypes: ["FORMS"],
}));
const MIN_CONFIDENCE = 85.0;
const lowConfidence = (response.Blocks ?? []).filter(
(b) => b.BlockType === "KEY_VALUE_SET" && (b.Confidence ?? 0) < MIN_CONFIDENCE,
);
if (lowConfidence.length) {
console.log(`${lowConfidence.length} fields below threshold - route to review`);
}DetectDocumentText/AnalyzeDocument reject multi-page PDFs and TIFFs outright - build for Start*/Get* before it becomes a production incident.Bytes path caps at 5 MB; S3Object raises that ceiling to 10 MB for synchronous calls and is required for all async calls.NotificationChannel avoids wasted Get* calls and reduces latency versus a fixed poll interval.TABLES, FORMS, QUERIES, and SIGNATURES adds to response size and billed cost - don't request all four by default.Reuse a single traversal helper across every extraction path rather than special-casing each FeatureType's parsing.
# --- Python (boto3) ---
def resolve_children(block: dict, by_id: dict, relationship_type: str = "CHILD") -> list[dict]:
"""Shared helper: follow a relationship type from any block to its linked blocks."""
ids = [
child_id
for rel in block.get("Relationships", [])
if rel["Type"] == relationship_type
for child_id in rel["Ids"]
]
return [by_id[i] for i in ids if i in by_id]
# Works identically for KEY -> VALUE ("VALUE"), TABLE -> CELL ("CHILD"), and QUERY -> QUERY_RESULT ("ANSWER").// --- TypeScript (AWS SDK v3) ---
import type { Block } from "@aws-sdk/client-textract";
function resolveChildren(block: Block, byId: Map<string, Block>, relationshipType = "CHILD"): Block[] {
// Shared helper: follow a relationship type from any block to its linked blocks.
const ids = (block.Relationships ?? [])
.filter((rel) => rel.Type === relationshipType)
.flatMap((rel) => rel.Ids ?? []);
return ids.map((id) => byId.get(id)).filter((b): b is Block => b !== undefined);
}
// Works identically for KEY -> VALUE ("VALUE"), TABLE -> CELL ("CHILD"), and QUERY -> QUERY_RESULT ("ANSWER").Assuming Textract filters by confidence for you. It doesn't - every block comes back regardless of score, so a pipeline that never checks Confidence will happily pass through low-quality extractions as if they were certain.
No. Textract has no equivalent request parameter - filtering on Confidence happens entirely in your own code, after the response comes back.
The synchronous operations only accept a single page. Anything with more than one page needs StartDocumentAnalysis/GetDocumentAnalysis (or the text-only equivalents) instead.
No. Each FeatureType adds to response size and cost - request only TABLES, FORMS, QUERIES, or SIGNATURES when that specific call actually needs them.
For anything low-stakes, generally yes once tuned against your own documents. For anything consequential, pair the threshold with A2I human review for the specific fields that matter most, rather than trusting a single number completely.
Use NotificationChannel so Textract pushes completion to SNS instead of polling, always page through Get* results with NextToken, and explicitly handle PARTIAL_SUCCESS as distinct from full success.
No. Create one client and reuse it across calls so connection pooling and resolved credentials are shared, which lowers latency, especially in long-running services or warm Lambda invocations.
Stack versions: This page was written for boto3 1.43.x (Python 3.10+) and the AWS SDK for JavaScript v3 (Node.js 18+).
Reviewed by Chris St. John·Last updated Jul 25, 2026