Extracting text from images in Node.js using AWS Rekognition
Use AWS Rekognition’s DetectText operation to read text on signs, labels, and other images from
Node.js. This walkthrough builds a CLI that accepts a local JPEG or PNG, or an existing S3 object,
and prints detected lines with confidence scores and geometry.
Introduction to AWS Rekognition and text detection
DetectText returns both LINE and WORD detections. Printing both repeats the same text, so our
CLI returns only lines and counts those lines. A detected line can be part of a sentence; the result
is not a document’s paragraph structure or a guaranteed reading order. AWS documents a maximum of
100 detected words per image in its text detection guide.
For forms, tables, and multipage document OCR, evaluate
Amazon Textract instead.
Setting up AWS Rekognition
You need an AWS account and an authenticated local profile or an IAM role that allows
rekognition:DetectText. This action uses Resource: "*"; it does not operate on a Rekognition
collection. An administrator can grant the following policy:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "rekognition:DetectText",
"Resource": "*"
}
]
}
Use your organization’s existing authentication method. For a local profile, complete the
AWS SDK authentication setup
and sign in before running the example. We leave credentials unset so the
default credential provider chain
can resolve your profile or role. Environment credentials are also supported; temporary credentials
need AWS_SESSION_TOKEN as well as the access key and secret. Keep credential values out of source
files and terminal output.
Choose a supported AWS Region.
The commands below use us-east-1; replace it with your region and MY_PROFILE with your profile
name. For a deployed IAM role, omit AWS_PROFILE. Sending an image to Rekognition sends its content
to AWS and can incur a charge.
Installing the AWS SDK for Node.js
Install Node.js 24 LTS with npm. The example was tested with
Node.js 24.21.0 and AWS SDK v3’s @aws-sdk/client-rekognition 3.1148.0. The Node.js patch is a tested
version, not an intrinsic API requirement; later maintained releases in the same major are suitable.
The shell commands use Bash and were tested on Linux. Use a shell without NODE_OPTIONS so
injected loaders do not override the new project’s module resolution.
Run this from a directory where you want a new rekognition-text project. The subshell leaves your
current directory unchanged, and mkdir refuses an existing project. Installation targets the new
project explicitly, even inside another npm or Yarn project:
(
command -v node >/dev/null || { printf 'Install Node.js first.\n' >&2; exit 1; }
command -v npm >/dev/null || { printf 'Install npm first.\n' >&2; exit 1; }
[ -z "${NODE_OPTIONS:-}" ] || { printf 'Use a shell without NODE_OPTIONS.\n' >&2; exit 1; }
mkdir rekognition-text && cd rekognition-text &&
printf '{"private":true,"type":"module"}\n' > package.json &&
npm install --prefix "$PWD" --workspaces=false --ignore-scripts --save-exact \
@aws-sdk/client-rekognition@3.1148.0
)
If installation fails after creation, keep the directory and retry the npm install command from
inside it. Do not repeat mkdir or overwrite a project that already contains your work. Retain the
resulting package-lock.json when sharing the example.
Integrating AWS SDK into a Node.js application
Create rekognition-text/detect-text.mjs with this first block. The .mjs extension makes its module
format explicit, including inside a CommonJS project. maxAttempts: 1 makes each invocation attempt
one analysis without automatic retries:
import { DetectTextCommand, RekognitionClient } from '@aws-sdk/client-rekognition'
const client = new RekognitionClient({
region: process.env.AWS_REGION,
maxAttempts: 1,
})
Performing text detection with AWS Rekognition
Append the following block to the same file. The local path is opened directly, without interpreting
URL escapes or relying on its extension. Its read is bounded to 5 MiB plus one byte so oversized
files are rejected before an analysis. Use a regular file that is not changing while it is read.
The SDK accepts the Buffer directly and
handles base64 encoding.
AWS validates the image format and decodes its contents; a successful response does not prove that a
damaged image was recovered completely.
import { open } from 'node:fs/promises'
class InputError extends Error {}
async function localBytes(path) {
const file = await open(path, 'r')
try {
const info = await file.stat()
const limit = 5 * 1024 * 1024
if (!info.isFile() || info.size === 0 || info.size > limit) {
throw new InputError('Use a nonempty regular image file of at most 5 MiB.')
}
const buffer = Buffer.alloc(limit + 1)
let length = 0
while (length < buffer.length) {
const { bytesRead } = await file.read(buffer, length, buffer.length - length, null)
if (bytesRead === 0) break
length += bytesRead
}
if (length === 0 || length > limit) {
throw new InputError('Use a nonempty image file of at most 5 MiB.')
}
return buffer.subarray(0, length)
} finally {
await file.close()
}
}
async function main() {
const [mode, first, second, ...extra] = process.argv.slice(2)
if (!process.env.AWS_REGION) throw new InputError('Set AWS_REGION before running the CLI.')
let image
let source
if (mode === 'local' && first && second === undefined && extra.length === 0) {
image = { Bytes: await localBytes(first) }
source = { mode, path: first }
} else if (mode === 's3' && first && second && extra.length === 0) {
image = { S3Object: { Bucket: first, Name: second } }
source = { mode, bucket: first, key: second }
} else {
throw new InputError('Usage: node detect-text.mjs local PATH | s3 BUCKET KEY')
}
const response = await client.send(new DetectTextCommand({ Image: image }))
const lines = (response.TextDetections ?? [])
.filter((item) => item.Type === 'LINE')
.map((item) => ({
text: item.DetectedText,
confidence: item.Confidence,
boundingBox: item.Geometry?.BoundingBox,
polygon: item.Geometry?.Polygon,
}))
console.log(JSON.stringify({ source, lineCount: lines.length, lines }, null, 2))
}
main()
.catch((error) => {
const codes = [
'AccessDeniedException', 'CredentialsProviderError', 'InvalidImageFormatException',
'ImageTooLargeException', 'InvalidParameterException', 'InvalidS3ObjectException',
'ProvisionedThroughputExceededException', 'ThrottlingException', 'ENOENT', 'EACCES',
]
const name = error instanceof Error && 'code' in error ? error.code : error?.name
const code = codes.includes(name) ? name : 'RequestError'
console.error(error instanceof InputError ? error.message : `Text detection failed (${code}).`)
process.exitCode = 1
})
.finally(() => client.destroy())
Analyzing images from S3
The s3 mode sends the bucket and exact key to Rekognition; it does not download the object into
Node.js. Use an existing private object in a general purpose S3 bucket in the same region as
AWS_REGION. The calling identity needs s3:GetObject on that object, alongside
rekognition:DetectText. No public bucket or service role is needed for this synchronous operation.
See the S3 input contract
and image analysis procedure.
Supply the raw key, such as images/sign %20.png, rather than an S3 URL or a URL-encoded key.
This example reads the current object and does not specify a version. Our tested S3 objects use
SSE-S3 encryption; KMS-encrypted objects can require additional key permissions. The CLI creates,
changes, and deletes no S3 objects.
Practical example and code snippets
Put your image at rekognition-text/photo.png. From the parent directory, run:
(
cd rekognition-text &&
AWS_PROFILE=MY_PROFILE AWS_REGION=us-east-1 node detect-text.mjs local './photo.png'
)
For an image already uploaded to S3, replace MY_BUCKET and images/photo.png with your bucket and
key, and use the same region as that bucket:
(
cd rekognition-text &&
AWS_PROFILE=MY_PROFILE AWS_REGION=us-east-1 node detect-text.mjs s3 'MY_BUCKET' 'images/photo.png'
)
The JSON identifies the requested source and contains only detected lines. For a clear image with
one line, check that lineCount is one and that lines[0].text matches the image’s visible text.
A confidence score
is on a 0–100 scale; it is not an accuracy guarantee. The
bounding box
uses image-relative coordinates. For an upright image 1,000 pixels wide, a Left value of 0.1
means 100 pixels from the left edge. The polygon gives the detected region’s corners. Optional
fields that AWS omits are omitted from the JSON, rather than filled with invented values.
A successful analysis that finds no lines prints lineCount: 0 and an empty lines array and exits
zero. This can happen on a blank image or when text is not recognized. A missing file, invalid input,
credential problem, or failed AWS request instead writes a diagnostic to stderr and exits nonzero;
it does not print success JSON. Compare the text with the source image before using it for data
entry or accessibility.
AWS Rekognition service limits and pricing
AWS’s service limits documentation lists JPEG and PNG input, an 80-pixel minimum for both image dimensions, and a maximum of 100 detected words. It lists a 5 MB raw-byte limit and a 15 MB S3-object limit. This CLI caps local input at exactly 5,242,880 bytes, including any trailing data; the SDK’s base64 representation adds memory overhead. For S3 input, AWS applies its object limit. Check the stored object’s size and dimensions before calling, rather than assuming a small compressed file meets every constraint.
Use the API’s error reference
when troubleshooting. For InvalidS3ObjectException, check the exact key, region, and object read
permissions. For image errors, check the actual format, dimensions, and byte size. An access or
credential failure needs an authentication or permission fix. Throughput and throttling failures
can be retried with backoff after checking your usage; this example leaves that retry decision to
you instead of hiding repeated analyses.
Check AWS Rekognition Pricing for your region, API, volume, and account eligibility. S3 storage and requests have separate charges.
Conclusion
Use the returned lines as OCR candidates and retain the geometry when you need to locate text in an image. If you also need an image processing pipeline, see Transloadit’s Image Describe Robot for a managed image analysis step; its API and output differ from this CLI.
