Amazon SQS via SDK: Your First Queue
Amazon SQS is a managed message queue that buffers work between a producer and a worker.
Search across all documentation pages
Amazon SQS is a managed message queue that buffers work between a producer and a worker.
This page runs the full loop from code: create a queue, send a message, receive it, then delete it once processed. It is the pull half of AWS messaging - SNS is the push half.
Quick-reference recipe card - copy-paste ready.
# --- Python (boto3) ---
import boto3
sqs = boto3.client("sqs", region_name="us-east-1")
url = sqs.get_queue_url(QueueName="jobs")["QueueUrl"]
sqs.send_message(QueueUrl=url, MessageBody="job-1")
msgs = sqs.receive_message(QueueUrl=url, WaitTimeSeconds=10).get("Messages", [])
print([m["Body"] for m in msgs])// --- TypeScript (AWS SDK v3) ---
import { SQSClient, GetQueueUrlCommand, SendMessageCommand, ReceiveMessageCommand } from "@aws-sdk/client-sqs";
const sqs = new SQSClient({ region: "us-east-1" });
const { QueueUrl } = await sqs.send(new GetQueueUrlCommand({ QueueName: "jobs" }));
await sqs.send(new SendMessageCommand({ QueueUrl, MessageBody: "job-1" }));
const { Messages } = await sqs.send(new ReceiveMessageCommand({ QueueUrl, WaitTimeSeconds: 10 }));
console.log((Messages ?? []).map((m) => m.Body));When to reach for this:
Create a queue, send a message, receive it with long polling, process it, then delete it with its receipt handle - and clean up the queue.
# --- Python (boto3) ---
import boto3
sqs = boto3.client("sqs", region_name="us-east-1")
# 1. Create the queue (idempotent for the same name/attributes).
url = sqs.create_queue(
QueueName="jobs",
Attributes={"VisibilityTimeout": "30"},
)["QueueUrl"]
# 2. Send a message.
sqs.send_message(QueueUrl=url, MessageBody="job-1")
# 3. Receive with long polling (up to 10 messages, wait up to 20s).
resp = sqs.receive_message(
QueueUrl=url, MaxNumberOfMessages=10, WaitTimeSeconds=20)
for m in resp.get("Messages", []):
print("processing", m["Body"])
# 4. Delete only AFTER successful processing, using the receipt handle.
sqs.delete_message(QueueUrl=url, ReceiptHandle=m["ReceiptHandle"])
# 5. Clean up.
sqs.delete_queue(QueueUrl=url)// --- TypeScript (AWS SDK v3) ---
import {
SQSClient, CreateQueueCommand, SendMessageCommand,
ReceiveMessageCommand, DeleteMessageCommand, DeleteQueueCommand,
} from "@aws-sdk/client-sqs";
const sqs = new SQSClient({ region: "us-east-1" });
// 1. Create the queue (idempotent for the same name/attributes).
const { QueueUrl } = await sqs.send(new CreateQueueCommand({
QueueName: "jobs",
Attributes: { VisibilityTimeout: "30" },
}));
// 2. Send a message.
await sqs.send(new SendMessageCommand({ QueueUrl, MessageBody: "job-1" }));
// 3. Receive with long polling (up to 10 messages, wait up to 20s).
const resp = await sqs.send(new ReceiveMessageCommand({
QueueUrl, MaxNumberOfMessages: 10, WaitTimeSeconds: 20,
}));
for (const m of resp.Messages ?? []) {
console.log("processing", m.Body);
// 4. Delete only AFTER successful processing, using the receipt handle.
await sqs.send(new DeleteMessageCommand({ QueueUrl, ReceiptHandle: m.ReceiptHandle }));
}
// 5. Clean up.
await sqs.send(new DeleteQueueCommand({ QueueUrl }));What this demonstrates:
ReceiptHandle from a receive - not the message id - is what DeleteMessage needs.WaitTimeSeconds) waits for messages instead of returning empty immediately.ReceiveMessage returns a copy of the message and starts its visibility timeout. During that window the message is hidden from other consumers.Short polling (the default when WaitTimeSeconds is 0) returns immediately, often empty, and bills a request each time.
# --- Python (boto3) ---
# Long polling: wait up to 20s for a message before returning.
resp = sqs.receive_message(QueueUrl=url, WaitTimeSeconds=20)// --- TypeScript (AWS SDK v3) ---
// Long polling: wait up to 20s for a message before returning.
const resp = await sqs.send(new ReceiveMessageCommand({ QueueUrl, WaitTimeSeconds: 20 }));Long polling cuts empty responses and cost, and lowers latency when a message does arrive. Prefer it almost always.
A message that repeatedly fails processing can loop forever. Attach a dead-letter queue via a redrive policy so that after a set number of receives, the poison message is moved aside for inspection instead of retrying endlessly.
DeleteMessage with the receipt handle after successful processing.DeleteMessage needs the ReceiptHandle, not the MessageId. Fix: pass the receipt handle from the receive response.WaitTimeSeconds=0 returns many empty responses and wastes requests. Fix: set WaitTimeSeconds (up to 20) to enable long polling.ChangeMessageVisibility.| Alternative | Use When | Don't Use When |
|---|---|---|
| SQS standard (this page) | High-throughput buffered work, order not critical | You need strict ordering or exactly-once |
| SQS FIFO | Strict ordering and exactly-once processing | You need maximum throughput over ordering |
| SNS | Push fan-out to many subscribers | One worker should pull each message |
| SNS + SQS fan-out | Many durable consumers of one event | A single simple queue suffices |
| EventBridge | Rich routing and many AWS event sources | Simple point-to-point work queues |
No. Receiving returns a copy and hides it via the visibility timeout. The message stays in the queue until you explicitly delete it with its receipt handle.
A token returned by a specific receive that identifies your hold on that message. DeleteMessage and ChangeMessageVisibility use it - not the message id, which is stable across receives.
It hides an in-flight message from other consumers while you process it. If you delete it in time, it is gone; if you crash, it reappears after the timeout for another attempt.
Long polling (WaitTimeSeconds up to 20) waits for a message instead of returning empty immediately. It cuts wasted requests and cost and reduces latency when messages arrive.
The message is not deleted, so once its visibility timeout expires it becomes visible again and another consumer can process it. This is why SQS is reliable under worker failure.
On standard queues, yes - delivery is at-least-once, so duplicates are possible. Make your processing idempotent, or use a FIFO queue for exactly-once processing.
A separate queue that receives messages which failed processing a configured number of times. It stops poison messages from retrying forever and lets you inspect them.
SQS is pull: a worker receives and processes messages one group at a time. SNS is push: it delivers a copy to every subscriber. They are often combined in the fan-out pattern.
Either set a longer VisibilityTimeout on the queue, or call ChangeMessageVisibility to extend the timeout for that message while you keep working, so it is not redelivered.
Yes. Set MaxNumberOfMessages up to 10 on ReceiveMessage to batch receives, and use DeleteMessageBatch to delete them together for fewer requests.
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 24, 2026