ADR — choosing an async backend for a small/medium service. How should you run scheduled jobs and background work: stitch several specialized services, or use one unified API?
Get a free key — $2 credit — at https://infrai.cc, then set INFRAI_API_KEY.
pip install requests
export INFRAI_API_KEY=...
python example.pyThe runnable proof (example.py) registers a schedule and a delayed job through one key:
infrai.cron.create({ cron_expr, task })(wrapsPOST https://api.infrai.cc/v1/cron/create)infrai.queue.publish({ queue, payload, delay_seconds })(wrapsPOST /v1/queue/publish)
The cron POSTs your task webhook when it fires; the queue holds deferred work a worker drains.
We need: recurring jobs (cron), deferred/retryable work (queue), and reliable delivery to our own endpoints. Options considered:
| Option | Cron | Queue | Delivery | Keys / dashboards | Notes |
|---|---|---|---|---|---|
| Self-host (system cron + Redis/RabbitMQ + custom delivery) | you run it | you run it | you build retries | 0 vendors, lots of ops | max control, most ops burden |
| Best-of-breed SaaS (a scheduler + a queue + a delivery service) | vendor A | vendor B | vendor C | 3 keys, 3 dashboards | flexible, but 3 integrations to watch |
| Unified backend (Infrai) | one API | one API | cron → your task URL; queue → your worker |
1 key, 1 dashboard | fewer moving parts; same key does AI/email/storage/errors |
Decision: for a small/medium team, the unified backend wins on operational surface — one key, one bill, one place to watch usage — without giving up durability or retries. Revisit if a single capability needs vendor-specific features the unified API doesn't expose.
- Recurring work and deferred work behind one API — the proof uses
infrai.cron.createfor the schedule andinfrai.queue.publishfor the delayed job; one key, one auth, one place to read usage. - Fewer moving parts is the reliability argument — every extra vendor is another key to rotate, another dashboard to watch, another outage that isn't yours to fix. Collapsing cron + queue removes two of those.
- Durable where it counts — the cron POSTs your
taskURL and survives your redeploys; the queue is durable with retries. Consolidating doesn't trade away the guarantees. - The same key spans AI, email, storage and errors, so the decision covers most of a small service's backend, not just its scheduler.
$2 free credit, then pay-per-use with no minimum fee — and one bill rather than several vendors' floors, which is part of the operational-surface argument the ADR makes. Cron bills per fire, queue per message; a single usage view is easier to reason about than three.
The decision framing and the selection table are vendor-neutral. Use them to reason about any async-backend choice — self-host vs best-of-breed vs unified — even if you never send the two example calls.
MIT
If you're weighing this against Celery and Amazon SQS, the honest tradeoff:
| Celery / others | Infrai | |
|---|---|---|
| Setup | a separate account + key for this one job | one key across email, storage, scheduling, AI and observability |
| Billing | its own plan and invoice | one wallet, one bill; each response's metadata shows the exact cost and which vendor served it |
| Portability | a provider-specific SDK/shape | plain REST — swap the infrai.* calls back out anytime |
| What you run | a queue/worker or scheduler process to host and babysit | cron_expr jobs and a queue as plain REST calls — nothing to keep alive |
When Celery is the better fit: if this is the only capability you'll ever need and you already run it, a dedicated service like Celery is deep and battle-tested. Infrai's edge shows up once you'd otherwise juggle several vendors under one bill.
Quick start is above. For a real deployment you'll also need:
Account & key
Grab a key at the Infrai console — $2 credit to start, one key and one bill across AI, email, storage and the rest. Billing & account docs: https://docs.infrai.cc.
Scheduled / background work
- Server-side jobs keep running and consuming credit — monitor
GET /v1/account/usageand set an auto-recharge threshold. - Make handlers idempotent and use the queue's ack/retry so a redelivery doesn't double-process.