How Amazon SQS and Amazon EventBridge differ on delivery model, backpressure, retries, throughput quotas and billing, when to put an SQS queue behind an EventBridge rule, and how Encore gives each consumer its own queue from code.

Amazon SQS and Amazon EventBridge both sit between a service that produces something and a service that acts on it, and AWS architecture diagrams often show them side by side, which is how teams end up asking which one to use. SQS holds messages until a consumer asks for them, while EventBridge pushes each event to whatever targets its rules match, and that difference decides what happens when the consumer is slow, down or rate limited by an API it depends on.
This comparison covers the two models, the limits that matter in practice, how they combine, and how Encore gives every consumer its own SQS queue from a subscription declared in Go or TypeScript.
Encore brings infrastructure from code to TypeScript and Go backends: a service declares the topic it publishes and the subscriptions it consumes, and the Encore platform creates the matching AWS resources in your own account when you deploy. According to the AWS infrastructure docs, Pub/Sub runs on SNS and SQS with a dead-letter queue for each subscription, and each service gets IAM permissions to publish and consume. A topic with one queue per consumer, where many SQS-versus-EventBridge comparisons end up, is what a subscription compiles to.

Run in your terminal to get started locally.
The orders service declares the event, and its Place endpoint publishes it with orders.Placed.Publish(ctx, &orders.OrderPlaced{...}):
// orders/events.go
package orders
import "encore.dev/pubsub"
type OrderPlaced struct {
OrderID string `json:"order_id"`
SKU string `json:"sku"`
Qty int `json:"qty"`
}
// On AWS this becomes an SNS topic, with an SQS queue per subscription.
var Placed = pubsub.NewTopic[*OrderPlaced]("order-placed", pubsub.TopicConfig{
DeliveryGuarantee: pubsub.AtLeastOnce,
})
The fulfillment service reserves stock through a warehouse API that only accepts a few parallel calls. The subscription caps how many messages each instance handles at once and backs off on failures, which is the queue behavior you would otherwise tune with SQS polling code and a redrive policy:
// fulfillment/reserve.go
package fulfillment
import (
"bytes"
"context"
"encoding/json"
"fmt"
"net/http"
"time"
"encore.app/orders"
"encore.dev/pubsub"
"encore.dev/storage/sqldb"
)
var db = sqldb.NewDatabase("fulfillment", sqldb.DatabaseConfig{Migrations: "./migrations"})
var secrets struct {
WarehouseAPIKey string // set with `encore secret set --type prod WarehouseAPIKey`
}
// The warehouse API allows a handful of parallel calls, so the subscription
// takes messages from its queue at that pace instead of the publish rate.
var _ = pubsub.NewSubscription(orders.Placed, "reserve-stock",
pubsub.SubscriptionConfig[*orders.OrderPlaced]{
Handler: reserve,
MaxConcurrency: 5,
AckDeadline: 2 * time.Minute,
RetryPolicy: &pubsub.RetryPolicy{
MinBackoff: 30 * time.Second,
MaxBackoff: 15 * time.Minute,
MaxRetries: 20, // then the message moves to this subscription's DLQ
},
},
)
func reserve(ctx context.Context, e *orders.OrderPlaced) error {
// Skip orders that were already reserved, so redelivery is harmless.
var done bool
err := db.QueryRow(ctx,
`SELECT EXISTS (SELECT 1 FROM reservations WHERE order_id = $1)`, e.OrderID,
).Scan(&done)
if err != nil || done {
return err
}
body, _ := json.Marshal(map[string]any{"sku": e.SKU, "qty": e.Qty})
req, err := http.NewRequestWithContext(ctx, http.MethodPost,
"https://warehouse.example.com/v1/reservations", bytes.NewReader(body))
if err != nil {
return err
}
req.Header.Set("Authorization", "Bearer "+secrets.WarehouseAPIKey)
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Idempotency-Key", e.OrderID) // warehouse dedupes retries
resp, err := http.DefaultClient.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode >= 300 {
return fmt.Errorf("warehouse returned %d", resp.StatusCode) // retried with backoff
}
_, err = db.Exec(ctx,
`INSERT INTO reservations (order_id, sku, qty) VALUES ($1, $2, $3)
ON CONFLICT (order_id) DO NOTHING`, e.OrderID, e.SKU, e.Qty)
return err
}
MaxConcurrency applies per instance of the service, and AckDeadline is how long the handler has before the message returns to the subscription for redelivery. After MaxRetries failures the message goes to the subscription's dead-letter queue, where it waits until the bug is fixed and it is released, as the Pub/Sub docs describe. A second consumer, such as one that emails the customer, is another pubsub.NewSubscription call in its own service and gets its own queue, so a slow email provider never delays stock reservations.
encore run delivers events locally and shows the publish and the handler in one trace, which lets a coding agent add a consumer, publish a test order and read the result before anything is deployed. The warehouse key is set per environment with encore secret set and stays out of the repository, per the secrets docs, and an SNS topic that other systems already publish to can be imported instead of recreated.
Best fit: Teams on AWS who want each consumer of an event to work through its own durable queue at its own pace, with retries and a dead-letter queue configured next to the handler that needs them.
SQS stores a message until a consumer receives it and deletes it. A received message is hidden for the visibility timeout, 30 seconds by default and up to 12 hours, and reappears for another consumer if it is not deleted in time, per the SQS message quotas. The same page lists retention of 4 days by default and 14 days at most, messages up to 1 MiB, delays of up to 15 minutes, and nearly unlimited API calls per second on standard queues.
With pull delivery, consumers take work when they have capacity, a spike in orders becomes a longer queue rather than a flood of calls to a downstream API, and a consumer that is down for an hour picks up where it left off. FIFO queues add ordering and deduplication per message group, at 300 transactions per second per partition without batching, with a high throughput mode that raises that ceiling.
SQS has no routing and no fan-out on its own: one queue serves one logical consumer, so a second consumer means SNS or EventBridge in front, a queue policy that lets that topic or bus write to the queue, a redrive policy for the dead-letter queue, and polling code or a Lambda event source mapping in every consumer. Billing counts every API action as a request, with each 64 KB chunk of payload billed as one request, according to the SQS pricing page, so sends, receives, deletes and empty polls all add up.
Best fit: Work queues and buffers in front of anything that has to process at its own pace, such as third-party API calls, document processing or payment captures.
EventBridge receives events from your applications, AWS services and SaaS partners, matches them against rules written as JSON event patterns, and pushes matches to targets such as Lambda functions, Step Functions, API destinations and SQS queues, with up to five targets per rule according to the EventBridge targets docs. Archive and replay, cross-account buses and the schema registry have no SQS equivalent.
With push delivery, a struggling consumer becomes a retry problem: if a target is unavailable, EventBridge retries for 24 hours and up to 185 times by default, and once those attempts run out the event is dropped unless the target has a dead-letter queue, as the retry policy docs explain. Throughput is bounded by regional quotas: the EventBridge quotas page lists a default PutEvents limit of 10,000 requests per second in us-east-1 and 2,400 in eu-central-1, with lower limits in smaller regions, while rule invocations above their own quota are delayed rather than rejected.
Routing logic lives in rules, which are usually written in Terraform or CloudFormation next to the bus. A pattern that decides which service gets which event is tested by deploying it, and someone reading a consumer's code cannot tell which events reach it without opening that configuration. Billing is per event published, with each 64 KB chunk counted as one event, which makes the bill track publish volume rather than how often consumers poll.
Best fit: Reacting to AWS service events, such as an S3 object being created or an ECS task stopping, SaaS integrations, scheduled invocations and routing events across accounts.
With an SQS queue as the target of an EventBridge rule, each push from the bus lands in a backlog that the consumer drains at its own pace, and events survive a consumer outage longer than the 24-hour retry window. In the other direction, EventBridge Pipes can use an SQS queue as a source, with optional filtering and enrichment, to forward messages to another target. A common split is EventBridge for events that start in AWS or a SaaS tool, and topics with a queue per consumer for events your own services send each other.
Pick Encore when the events move between your own services and you want a queue per consumer with concurrency, retry and dead-letter settings in code, running locally under encore run and provisioned as SNS and SQS in your own AWS account. Pick SQS directly for a single work queue that a platform team already manages, or when a non-Encore system needs to poll it. Pick EventBridge when the source is an AWS service or SaaS partner, when routing depends on fields inside the event, or when you need archive and replay, and put an SQS queue behind the rule when the consumer cannot keep up with pushes.
SQS is a queue that stores messages until a consumer pulls and deletes them, for up to 14 days, so a slow consumer builds a backlog instead of losing work. EventBridge is an event bus that matches each event against rules and pushes it to up to five targets per rule, retrying for up to 24 hours by default before dropping the event or sending it to a dead-letter queue.
Use SQS when a consumer needs to process work at its own pace, with a visibility timeout and a dead-letter queue. Use EventBridge when the event comes from an AWS service or SaaS partner, or when routing depends on fields inside the event. An EventBridge rule can target an SQS queue, so the bus routes the event and the consumer then pulls from the queue at its own pace, with a visibility timeout and a dead-letter queue for failed deliveries.
SQS standard, fair and FIFO queues are all supported EventBridge targets, and the queue then buffers events for a consumer that polls it. EventBridge Pipes can also read from an SQS queue as a source and forward messages to another target.
With Encore, declare a topic with pubsub.NewTopic and a consumer with pubsub.NewSubscription, setting MaxConcurrency, AckDeadline and RetryPolicy in its SubscriptionConfig. On AWS, Encore provisions an SNS topic and an SQS queue with a dead-letter queue for each subscription, and grants the publishing and consuming services the permissions they need.
Deploy to your cloud on AWS and Google Cloud
without managing infrastructure
Encore lets developers and agents define application infrastructure directly in code, then automatically provisions it from local development to production in your AWS or GCP account.