Skip to content

Task Status

GET/v1/tasks/{token}

Poll the task until status is completed or failed. The recommended interval is 3–5 seconds; a 5,000-row task typically settles in well under a minute.

The route parameter is {token}, and it takes the value the submission returned as task_id.

{
"task_id": "xKj9aP3qLm7v",
"status": "completed",
"progress": 100,
"product": "YOUR_PRODUCT_SLUG",
"country": "CN",
"submitted": 5000,
"prefiltered": 0,
"deduped": 0,
"total": 5000,
"processed": 5000,
"valid": 3800,
"invalid": 1200,
"unknown": 0,
"unknown_processed": 0,
"unknown_unprocessed": 0,
"stage": "completed",
"stage_seconds": 12,
"last_progress_at": "2026-09-05T10:00:12+00:00",
"failure_message": null,
"cost_deducted": "0.500000",
"cost_refunded": "0.000000",
"result_available": true,
"result_expires_at": "2026-05-02T10:00:00+00:00",
"created_at": "2026-05-01T10:00:00+00:00"
}
statusDescription
pendingQueued and waiting for resources.
processingDetection is in progress.
completedCompleted. The result is ready to download.
failedExecution failed. failure_message says why.
FieldTypeDescription
task_idstringThe task token, echoed back.
statusstringpending, processing, completed or failed.
progressintegerPercentage complete, 0 to 100.
productstringProduct slug the task was submitted with — see the bulk product list.
countrystring | nullCountry the task was submitted with; null for email products.
submittedintegerRows received in the submission.
prefilteredintegerRows dropped before detection — malformed lines, for example.
dedupedintegerDuplicate rows removed.
totalintegerRows actually queued for detection, after prefiltering and deduplication.
processedintegerRows the upstream channel has finished.
validintegerRows confirmed as registered.
invalidintegerRows confirmed as not registered.
unknownintegerRows the provider could not resolve.
unknown_processedintegerUnknown rows that were retried and settled.
unknown_unprocessedintegerUnknown rows still unresolved. These are refunded.
stagestring | nullInternal stage name while the task is running. Diagnostic only — do not branch on it.
stage_secondsinteger | nullSeconds spent in the current stage.
last_progress_atstring (ISO 8601) | nullWhen progress last advanced. A timestamp that stops moving means the task is stuck, not merely slow.
failure_messagestring | nullWhy the task failed, or null.
cost_deductedstringAmount deducted for the task, as a string.
cost_refundedstringAmount refunded for rows that were never checked, as a string.
result_availablebooleantrue when the result file can be downloaded.
result_expires_atstring (ISO 8601) | nullWhen the result file stops being downloadable.
created_atstring (ISO 8601)When the task was created.

submitted is what you sent; total is what was actually checked. They differ whenever rows were malformed or duplicated, and the gap is exactly prefiltered + deduped.

cost_deducted is charged on total, and cost_refunded covers rows that never resolved — the unknown_unprocessed bucket. Only valid and invalid verdicts are billed.

Terminal window
# Poll until the task leaves pending/processing. `last_progress_at` going
# stale is the real failure signal, not elapsed time.
for i in $(seq 1 120); do
body=$(curl -s -X GET "https://api.zelnum.com/api/v1/tasks/xKj9aP3qLm7v" \
-H "X-API-Key: YOUR_KEY_HERE")
status=$(printf '%s' "$body" | jq -r '.status')
printf '%s %s%%\n' "$status" "$(printf '%s' "$body" | jq -r '.progress')"
case "$status" in
completed) printf '%s' "$body" | jq .; break ;;
failed) printf '%s' "$body" | jq .; exit 1 ;;
esac
sleep 5
done