Build a bounded queue for hCaptcha jobs
Coordinate task admission, weighted concurrency, pending observations, and recovery without using unlimited parallel submissions.
Limit accepted work, not just HTTP requests
A task can remain active after its creation response returns. Limiting the number of simultaneous POST requests does not necessarily limit active remote jobs. Track accepted task IDs until terminal status, and account for jobs whose observation temporarily failed.
Use an application queue to separate waiting local operations from remotely admitted work. Give each local operation a stable identifier so a worker restart does not accidentally schedule it twice.
Respect weighted account concurrency
Recognition tasks use one concurrency unit, while token tasks use ten. Limits apply across the account’s keys, not independently to every worker process. Read the concurrency and mode documentation before choosing a local admission budget.
For example, a local scheduler that assumes every job has weight one can admit too much token work while remaining below its own count limit. A scheduler should estimate weighted work and handle definitive server rejections even when its estimate says capacity exists.
Keep state transitions explicit
local waiting -> submitting -> accepted task ID -> observing -> terminal
-> submission outcome unknown
This is an application state model. The remote job still has the documented queued, running, completed, and failed states. Do not release accepted-work accounting just because a lookup returned a transport error.
A local timeout might return control to a user while a background observer continues. Preserve that distinction in storage and metrics. Otherwise a dashboard can show zero active jobs even while the server is still processing work.
Retry observation separately
Respect Retry-After on rate-limit responses and prevent all workers from waking at the same instant. Known IDs can be observed again; unknown creation outcomes need a reconciliation policy rather than another automatic POST.
Do not hold a database transaction open while waiting on a solver. Persist state in short operations and use worker ownership or leases for your own queue if needed. Test a process crash between remote acceptance and local persistence.
The recovery guide explains uncertain submissions. Queue-versus-solve timing shows which latency stages a scheduler can influence, and burst and fast covers optional admission and priority controls.