Async polling flow
The ZenHire API is asynchronous: submit returns immediately, and you poll for results. This page explains why, and how to design your integration around it.
Why async?
Speech analysis runs through several steps — transcription, speaker diarization, candidate detection, then three independent ML scoring pipelines (vocabulary, fluency, accent). Typical end-to-end latency is 2–5 minutes, sometimes longer under load. A synchronous HTTP request would time out on most clients.
The async flow lets you:
- Submit thousands of audio files in parallel without blocking on any one.
- Survive client-side restarts — the run
idnever expires. - Safely retry a transient network error on the poll endpoint.
The lifecycle
queued → processing → success | partial | failed
queued— the request was accepted but no processing slot was available. The run starts automatically, in FIFO order, when a slot frees up. You seequeuePositionin the response.processing— actively running.Retry-Afteris set on the poll response.success— all three scores produced.partial— at least one score produced, others failed. Credits are charged proportionally.failed— no scores produced. No credits charged. Seeerror.codeanderror.message.
Recommended poll cadence
Use pollIntervalSeconds from the most recent response. If you want to
implement your own strategy:
| Elapsed since submit | Interval |
|---|---|
| 0–2 minutes | 15 seconds |
| 2–5 minutes | 30 seconds |
| 5+ minutes | 60 seconds |
Minimum interval per run id is 10 seconds. Faster polls return
429 POLL_RATE_LIMITED — respect the Retry-After header.
When to stop polling
Stop when the status is terminal (success, partial, or failed), or
when your budget expires.
The run id never expires. If your client crashes mid-poll, you can
pick up the same id hours later and continue polling.
Concurrency and queueing
Default 8 simultaneous processing runs per client (contact support
to extend). When you reach that limit, new submissions return
202 Accepted with status: "queued" — not an error. Queue
position is included in the response.
This means a successful 202 from submit doesn't guarantee your run has
started — check the poll endpoint to know when processing begins.
Explainability on the success response
Successful and partial responses include an optional explainability
object with plain-language, human-readable summaries of why each
score came out the way it did:
{
"explainability": {
"vocab": "Advanced vocabulary with consistent use of C1+ words...",
"fluency": "Fluid delivery at 156 words/min with a strong..."
}
}
Two sentences per dimension, safe to surface directly to end users
(hiring managers, candidates, audit reviewers) — no post-processing
required. Omitted on failed runs and in rare cases where a
per-dimension feature breakdown couldn't be produced, so branch on
presence before rendering.
Poll-only across services
This submit-and-poll pattern is the common shape across the platform. Speech,
CV DeepMatch, and CV DeepSearch all support a poll-only integration:
submit returns a run id, and you poll until a
terminal state. No public endpoint is required.
The same cadence rules apply to CV DeepMatch and CV DeepSearch — minimum
10 seconds between polls per id, with 429 + Retry-After on faster
polls. See the CV DeepMatch integration guide and
CV DeepSearch search guide for poll-vs-webhook
details.
Webhooks alternative?
- Speech API — webhooks are not yet supported. If you need them, let your ZenHire contact know; it's on the roadmap.
- CV DeepMatch and CV DeepSearch — webhooks are supported as an
optional push channel: supply a
webhook_urlon submit to also receive a signed HMAC callback. Omit it to integrate poll-only.
See in the API reference
- POST /api/v1/speech/analyze — submit audio, returns the run
id - GET /api/v1/speech/analyze/{id} — poll for results
- GET /api/v1/speech/runs — list historical runs