£29 / month
60 days trial — no card required
- 1 spool
- 250 req/s
- 7 days retention
Per spool:
- 1,000 queues
- 100,000 stored messages
- 2 GiB stored data
- 64 KiB per message
- 1 replica
Spooler is a simple, fully managed message queue.
So you can build your next big thing.
# The body is the message: bytes in, bytes out, no envelope. # 201 means stored; the queue "orders" was created beforehand.$ curl -si -X POST "$SPOOLER_API/spools/default/queues/orders/send" \ > -H "Authorization: Bearer $SPOOLER_KEY" \ > --data-binary 'hello, spooler!'HTTP/1.1 201 Created Spooler-Message-Id: 1-1 Date: Wed, 21 Oct 2026 18:19:16 GMT Content-Length: 0 $
# The CLI: go install github.com/spoolersh/spooler-cli/cmd/spooler@latest # --data is the message, bytes as given, sent to the queue "orders" # created beforehand. The reply is its id.$ spooler send --queue orders --data 'hello, spooler!'{ "id": "1-1", "duplicate": false }$
import "github.com/spoolersh/spooler-go" // A client for the public API. c := spooler.Client{ APIKey: "...", } // The body is the message: bytes in, bytes out, no envelope. // A successful send stores the message and returns its ID. res, err := c.Send(ctx, spooler.SendRequest{ Spool: "default", Queue: "orders", Data: []byte("hello, spooler!"), }) if err != nil { // Handle the error. } fmt.Println(res.ID) // 1-1
# A receive does not delete the message: it leases it, and no other # receiver gets it while the lease is live. -w captures it in $LEASE.$ LEASE=$(curl -si -X POST "$SPOOLER_API/spools/default/queues/orders/recv" \ > -H "Authorization: Bearer $SPOOLER_KEY" \ > -o /dev/stderr -w '%header{spooler-lease}')HTTP/1.1 200 OK Cache-Control: no-store Content-Length: 15 Content-Type: application/octet-stream Spooler-Lease: AYUBAQIBAwEEAQUCI-tmOl2DyXcr3uUO Spooler-Lease-Expires-At: 2026-10-21T18:24:16.875316003Z Spooler-Message-Created-At: 2026-10-21T18:19:16.866018087Z Spooler-Message-Id: 1-1 Spooler-Message-Retries: 0 Date: Wed, 21 Oct 2026 18:19:16 GMT hello, spooler!$
# A receive does not delete the message: it leases it, and no other # receiver gets it while the lease is live. -i shows the attributes # ahead of the payload; --lease-file keeps the lease for the ack.$ spooler recv --queue orders -i --lease-file lease.json{ "id": "1-1", "retries": 0, "createdAt": "2026-10-21T18:19:17.925683254Z", "lease": "AYUBAQIBAwEEAQUCfW9LB8QM8hLV7onb", "leaseExpiresAt": "2026-10-21T18:24:17.950929754Z", "dataSize": 15 } hello, spooler!$
import "github.com/spoolersh/spooler-go" // A client for the public API. c := spooler.Client{ APIKey: "...", } // A receive does not delete the message: it leases it, and no other // receiver gets it while the lease is live; Ack presents it back. msg, ok, err := c.Recv(ctx, spooler.RecvRequest{ Spool: "default", Queue: "orders", }) if err != nil { // Handle the error. } if !ok { // The wait elapsed with nothing to receive. } fmt.Printf("%q", msg.Data) // "hello, spooler!"
# Present the lease back. 204 means the message is acknowledged and # removed from the queue. That is the whole loop: send, receive, ack.$ curl -si -X POST "$SPOOLER_API/spools/default/ack" \ > -H "Authorization: Bearer $SPOOLER_KEY" \ > -H "Spooler-Lease: $LEASE"HTTP/1.1 204 No Content Date: Wed, 21 Oct 2026 18:19:16 GMT $
# Present the lease back. ack prints nothing: exit 0 means the message # is acknowledged and removed. That is the whole loop: send, receive, ack.$ spooler ack --lease-file lease.json$
import "github.com/spoolersh/spooler-go" // A client for the public API. c := spooler.Client{ APIKey: "...", } // Present the lease back: the message is acknowledged and removed // from the queue. That is the whole loop: send, receive, ack. err := c.Ack(ctx, spooler.AckRequest{ Spool: "default", Lease: msg.Lease, }) if err != nil { // Handle the error. }
Spooler gives your agent the tools and the skills to work the queue. If your agent can run a command, it can use Spooler.
spooler help documents the commands, flags and exit codes, with links to the docs. Your agent reads the same help you do, and the skills explain how to use Spooler.
Your agent is a worker like any other: it leases messages and acks them when done. A crash costs a redelivery, not the message. You or your agent can inspect failed messages, then recover or discard them.
You and your agent use the same commands: data on stdout, diagnostics on stderr and documented exit codes. You can list messages, inspect a payload or describe a spool with the same CLI.
Your queues can model a state machine: each queue a state, each message an object in that state, and ack-and-send the transition. It acks the current message and appends the next in one atomic call. Both changes happen, or neither does.
┌─ Spooler ───────────────────────────────────┐ │░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░│ │░░┌─ textify ─┐░┌─ clarify ─┐░┌─ ready ───┐░░│ │░░│ message A │░│ │░│ │░░│ │░░│ │░│ │░│ │░░│ │░░└───────────┘░└───────────┘░└───────────┘░░│ │░░░░░░░░│░░░░░░░░░░░░│░│░░░░░░░░░░░░│░░░░░░░░│ └───────╴│╶──────────╴│ │╶──────────╴│╶───────┘ │ │ │ │ ┌─────────┐ │ │ ┌─────────┐ │ Worker1 │───────┘ └───────│ Worker2 │ └─────────┘ └─────────┘ │ │ │ │ ▼ ▼ Text extraction API LLM API
Your handler sends each webhook to the queue and returns 200 once the send succeeds. Bursts wait in the queue while your workers process them at their own pace.
GitHub / Stripe / ...
╷
│
▼
Webhook Handler
╷
│
▼
┌─ Spooler ─────────────────┐
│░░┌─ webhooks ──────────┐░░│
│░░│ │░░│
│░░│ │░░│
│░░│ │░░│
│░░│ │░░│
│░░└─────────────────────┘░░│
└────────────╴│╶────────────┘
│
│
┌─────────┐
│ Workers │
└─────────┘Your queue paces work to match a third-party API's limits. Its receive rate and burst size control how fast your workers receive messages. All workers share that limit.
┌─ in ───────────────────────────────── 58/s ─┐ │ ▁▁ │ │ ▁▄▆██▇▄▂ │ │█████▇████████▇▇▇▇▇▇▇▇██▇█▇▇▇▇▇▇▇▇▇▇▇▇▇█▇▇▇▇▆│ └─────────────────────────────────────────────┘ ┌─ out ──────────────────────────────── 40/s ─┐ │ │ │ │ │▅▅▅▅▅▅▅▅▅▅▅▅▅▅▅▅▅▅▅▅▅▅▅▅▅▅▅▅▅▅▅▅▅▅▅▅▅▅▅▅▅▅▅▅▅│ └─────────────────────────────────────────────┘ ┌─ queue ─────────────────────── 189 (5s) ─┐ │ ▁▁▁▂▂▂▂▂▃▃▃▃▄▄▄▅▅▅▅▅▆▆▆▆▇▇│ │ ▁▂▄▅▆▇▇▇▇███████████████████████████████│ │▇████████████████████████████████████████████│ └─────────────────────────────────────────────┘ ┌─ recvRateLimit ──────────────────────────┐ │ │ │ rate 40/s │ │ burst 8 │ │ │ └─────────────────────────────────────────────┘ ┌─ input ──────────────────────────────────┐ │ │ │ rate 60/s │ │ burst 40 │ │ │ └─────────────────────────────────────────────┘
Your workers take background jobs from one queue. Each job goes to one worker at a time, under a lease. If that worker crashes, the lease expires and the job becomes available to another worker.
┌─ Spooler ───────────────────────┐ │░░┌─ jobs ────────────────────┐░░│ │░░│ message-A │░░│ │░░│ message-B │░░│ │░░└───────────────────────────┘░░│ └───╴│╶─────────╴│╶─────────╴│╶───┘ │ │ │ │ │ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ Worker1 │ │ Worker2 │ │ WorkerN │ └─────────┘ └─────────┘ └─────────┘
A spool is the storage unit for your queues. It has its own log, write buffers and replication, and handles its I/O independently. A full spool refuses new sends rather than letting a backlog increase your bill.
Your send succeeds only after the message is written and synced to disk and the required replicas have confirmed it. Acks and atomic ack-and-send follow the same durability rule.
A receive leases a message rather than removing it. You ack when the work is done, or renew if it takes longer. Delivery is at-least-once, so a crash mid-work costs a redelivery, not a message.
When a connection drops, you retry the send with the same dedup key. Within your queue's dedup window, that retry adds no second message.
Your worker acks one message and appends the next in one atomic call: both happen, or neither does. If it retries a call that already committed, nothing changes, so the hop happens exactly once.
A message that keeps failing doesn't block your queue. When its retries run out, or your handler marks it failed, it enters the failed state with its payload intact. You can inspect it, then recover or discard it.
One price a month. If your spool fills up, Spooler rejects new sends until your workers make room. Stalled workers do not mean a surprise invoice.
£29 / month
60 days trial — no card required
Per spool:
£149 / month
45 days trial — card required
Per spool:
£299 / month
30 days trial — card required
Per spool: