Many production ML workloads are not interactive. Nightly churn scores, weekly demand forecasts and back-catalog document classification do not need a real-time endpoint that bills around the clock. SageMaker Batch Transform spins up instances, scores an S3 dataset, writes results back to S3 and shuts everything down. This tutorial shows how to run it well.
It assumes you already have a SageMaker model (a trained artifact plus inference container) and an execution role. For help choosing between inference types, see our comparison of real-time, serverless, async and batch.
1. Create the model and transformer
import sagemaker
from sagemaker.estimator import Estimator
session = sagemaker.Session()
bucket = session.default_bucket()
# Attach to a finished training job (or build a Model object from model_data)
estimator = Estimator.attach("my-xgboost-training-job")
transformer = estimator.transformer(
instance_count=2,
instance_type="ml.m5.xlarge",
strategy="MultiRecord", # batch many records per request
assemble_with="Line",
accept="text/csv",
max_payload=6, # MB per request
max_concurrent_transforms=4, # parallel requests per instance
output_path=f"s3://{bucket}/scores/2026-01-01/",
)
strategy="MultiRecord" packs as many records as fit into max_payload; SingleRecord sends one per request and is much slower. Keep max_concurrent_transforms near the number of worker processes your container runs.
2. Run the job with input and output filters
Tabular models usually need features only, but you want an ID next to each prediction. Batch Transform can select input columns and join the output with the original record using JSONPath expressions.
transformer.transform(
data=f"s3://{bucket}/features/2026-01-01/",
content_type="text/csv",
split_type="Line",
input_filter="$[1:]", # drop column 0 (the record ID) before inference
join_source="Input", # append the prediction to the original row
output_filter="$[0,-1]", # keep the ID and the prediction only
wait=True,
logs=False,
)
The result is one output file per input file, with the ID and prediction on each line, ready for Athena or a downstream load. Note: if your input has a header row, strip it first, because Batch Transform treats every line as a record.
3. Tune throughput
- Instance count: files are distributed across instances, so you get no benefit from more instances than input files. Shard large inputs into many roughly equal files.
- Payload and concurrency: raise
max_payloaduntil memory or latency per request degrades, then raise concurrency until CPU or GPU saturates. WatchCPUUtilizationandMemoryUtilizationin the/aws/sagemaker/TransformJobsCloudWatch metrics. - Timeouts: set
invocations_timeout_in_secondsfor slow models; the default can be too short for large payloads. - Instance type: use GPU instances only if the model needs them. Many tabular and classical models are cheaper on CPU.
4. Handle failures
One malformed record should not kill a six-hour job. Set the MaxPayloadInMB conservatively and make your inference handler return a clear error for bad rows. Check the job status and failure reason programmatically:
import boto3
sm = boto3.client("sagemaker")
d = sm.describe_transform_job(TransformJobName=transformer.latest_transform_job.name)
print(d["TransformJobStatus"], d.get("FailureReason"))
For retries, rerun only the shards that failed rather than the whole dataset.
5. Schedule it
Wrap the job in a SageMaker Pipeline TransformStep, and trigger the pipeline with Amazon EventBridge Scheduler on your cadence. This gives you lineage, parameterised input paths, and a clean place to add a quality check step before results are published.
6. Cost notes
- You pay for instance time only while the job runs, plus S3 storage.
- Use Spot-friendly patterns elsewhere in the pipeline, but note Batch Transform itself uses on-demand instances.
- Compress or use Parquet upstream so that less time is spent reading input.
- Choose Batch Transform over an endpoint whenever the latency requirement is minutes or more.
When to choose something else
If individual requests are large (long audio, big documents) and need results within minutes, asynchronous inference is a better fit. If you need sub-second responses, use a real-time endpoint. For everything scheduled and bulk, Batch Transform is the simplest and cheapest option.
Need help moving a bulk scoring workload onto SageMaker? Get in touch with our team.