FlexiQ documentation

Guides, API reference and architecture for the task queue. Pick your language and the snippet, the links and the sidebar all follow it — or pick the server, if the process enqueuing has no binding at all.

tasks.py
worker · live
from flexiq import Queue

queue = Queue(db_path="tasks.db")

@queue.task(max_retries=3)
def add(a: int, b: int) -> int:
    return a + b

job = add.delay(2, 3)
print(job.result())   # → 5
$flexiq worker --app tasks:queue
→scheduler online · 6 workers ready
✓add(2, 3) =512 ms
How it works

From .delay() to result

Your application code enqueues a job. The Rust scheduler hands it to a worker. The result lands back in the shared store — same core, same queue, no broker in the middle, whichever SDK you called it from.

your codeenqueuepython · node · java · rust
job = add.delay(2, 3)
returns a handle, not the answer
nothing to point at a broker
one write
the queuejobsSQLite · PG
task
add
status
pending
one row, whichever SDK wrote it
claim
schedulerdispatchRust · Tokio
claim_execution_batch()
a batch per poll, not a job
max_in_flight caps the claim
run
workersexecute6 · pool
add(2, 3) → 5
your function, in your process
retries and dead-letter on failure
the result is written back to that same row — your handle reads it there
Server mode

Enqueue from curl,
not just an SDK

flexiq-server puts the producer API on the network: gRPC, and the same calls as plain HTTP with JSON bodies on the same listener. A shell script enqueues onto the queue your workers already drain — no binding to install, and no database credential of its own.

Loading demo…

Scrub the trace, or flip the token's scope. Every string in it — the 200, the 403, the SCOPE_DENIED reason — came off a live door, not out of the proto.

flexiq-server
holds the credential
FLEXIQ_DSN=sqlite:///tmp/flexiq.db \
FLEXIQ_NAMESPACE=default \
FLEXIQ_GRPC_LISTEN=127.0.0.1:50051 \
flexiq-server
→[flexiq] gRPC listener on tcp://127.0.0.1:50051
any client
no SDK · no CBOR
curl -X POST http://localhost:50051/v1/jobs \
  -H "authorization: Bearer $FLEXIQ_TOKEN" \
  -H "content-type: application/json" \
  -d '{"taskName": "send_email",
       "structured": {"args": [{"to": "ada@example.com"}]}}'
→HTTP/1.1 200 OK
✓job.id01a08003-3a94-74d0-89b7-f1d5d0ca829e
✓job.statusJOB_STATUS_PENDING

Embedded mode stays the default — one process, straight to the database — and for a single-language deployment that is still the better door.

New here?

Tell us what's going wrong

Not sure you need a task queue? Pick the problem that sounds like yours — flexiq shows you how it handles it, with the exact code and a live demo you can try.

?Which sounds like you?
Pick a problem — see how flexiq handles it.
flexiq handles thisHeavy processing

Hand it off, return instantly, stream the progress back

Never make a user watch a spinner. Push the work into flexiq, respond in milliseconds, and report a live percentage as it runs.

  • Respond now.delay() queues the job and your endpoint returns immediately
  • Report progresscall progress.update() from inside the task
  • User sees it moveyour UI subscribes and shows a live % bar
tasks.py
@queue.task
def process(file_id):
    for i, chunk in enumerate(chunks):
        crunch(chunk)
        progress.update((i+1)/total)  # ← live %
Read the guide
Benchmarks

Numbers you can re-run

One scenario — 500 jobs, a fixed payload, fixed concurrency, measured to completion — against Celery, Dramatiq, RQ and BullMQ. Enqueue throughput, latency percentiles and idle cost are reported separately, because an engine that wins one can lose another.

Jobs completed per secondhigher is better

Same backend — every entrant on one Redis38.64 ms away

  • BullMQNode23.6jobs/s
  • DramatiqPython23.4jobs/s
  • CeleryPython14.2jobs/s
  • RQPython4.5jobs/s
  • FlexiQPython1.1jobs/s
  • FlexiQNode0.8jobs/s

No broker — FlexiQ on a local SQLite filea different deployment, not a faster entrant

  • FlexiQNode772.2jobs/s
  • FlexiQPython143.3jobs/s
500 jobs · 256-byte payload · concurrency 4 · 12 cores, 13.5 GB · run 2026-09-21T19:56:33Z. Produced by bench/run.py.

FlexiQ does not win every axis, and the losses are published with the wins. Redis in this run is 38.64 ms away, which every Redis-backed entrant pays on every command — a colocated server moves all of these numbers.