The exact poll flow for background work on the Poptin API: read object_id and object_name from the 202 response, poll GET /v1/bulk-jobs, and handle terminal states.
Some write operations on the Poptin API do not finish inline. They return HTTP 202 Accepted along with a bulk_job object, run in the background, and require your client to poll until the job reaches a terminal state. This page documents that poll flow precisely - the endpoint to call, the fields to read, and how to interpret the result.
For the conceptual overview, see Async jobs. All operations are served under https://api.popt.in/v1 and authenticated with a pk_live_ bearer key from My Account → API & Connections → API Keys.
Operations that queue
The following endpoints respond with 202 Accepted and a bulk_job:
- Bulk import contacts (JSON)
- Bulk import contacts (CSV)
- Bulk delete contacts (JSON)
- Send template email
Single-item operations (create/update contact, delete contact, update status) complete inline and do not use this flow.
Poll flow
Every async job follows the same four steps:
-
Call the queuing endpoint. On success, the server accepts the work and returns
HTTP 202with the standard response envelope. Reading the envelope'sdata.bulk_jobgives you the identifiers you need. -
Read
object_idandobject_namefromdata.bulk_job. Together they uniquely identify this run:data.bulk_job.object_id- the job's identifier.data.bulk_job.object_name- the job type (for examplejson_bulk,csv_import,json_bulk_delete,template_email_send).
-
Poll job status. Call Show bulk job status at:
GET /v1/bulk-jobs?object_id={object_id}&object_name={object_name}Pass both identifiers from step 2 exactly as returned. Choose a sensible polling interval (for example a few seconds, growing over time). Do not tight-loop.
-
Wait for a terminal state. The response's
data.statusfield will eventually reach one of the terminal values below. Then read the job'sprogressfor counts and itserrorsfor any per-row failures.
Treat the initial 202 as "the request landed," not "the work is done." Only the final job state answers that.
Terminal states
A job's status field advances through in-progress values and settles on one of two terminal outcomes:
done- the work finished. Inspectprogressfor per-row counts (for import jobs:created,updated,failed,skipped,percent; for delete jobs:deleted,not_found,failed,percent) anderrorsfor any per-row failure detail.failed- the work stopped and the outcome is not usable. Readerrorsandmessageto decide whether to fix input and re-queue.
Any other value means the job is still running - continue polling.
Concurrency conflicts
Async jobs are subject to concurrency limits, and the exact limits may vary by account and can change over time. If a new job cannot start because concurrency is exhausted, the queuing endpoint returns HTTP 409 Conflict. When that happens:
- Wait for an in-progress job to reach a terminal state, or otherwise free capacity.
- Retry the queuing call once capacity is available.
- Do not tight-loop the queuing endpoint. Aggressive retries won't create capacity and can trip rate limits unnecessarily.
Design your client to react to 409 responses rather than to a hardcoded concurrency number.
Stats refresh exception
Requesting a poptin stats refresh is also asynchronous, but it does not use the bulk-jobs endpoint. Instead, track the refresh on the poptin resource itself:
- Read
refresh_statusandlast_updated_atfrom List poptins or Show poptin. - Poll those endpoints on an interval until
refresh_statusreturns toidle. - Requesting another refresh while one is already in progress may return
HTTP 200idempotently - no new work is queued.
Treat this as an object-level state change on the poptin, not a bulk job.
