Rasel / Docs
EN
GET/api/v2/messages/bulk/{jobId}

Read a bulk job

Returns status, counts, validation progress, pause reason, and the job error code. Poll until the status is queued, running, rejected, paused, completed, or cancelled.

When to use it. Follow a job created with POST /api/v2/messages/bulk.

Code example

Correct: Correct: progress

curl --location 'https://raselsms.com/api/v2/messages/bulk/64b0c0ff0000000000000001' \--header 'X-API-Key: YOUR_API_KEY'

Correct: Correct: rejected, nothing sent

curl --location 'https://raselsms.com/api/v2/messages/bulk/64b0c0ff0000000000000001' \--header 'X-API-Key: YOUR_API_KEY'

Incorrect: Incorrect: job not found

curl --location 'https://raselsms.com/api/v2/messages/bulk/000000000000000000000000' \--header 'X-API-Key: YOUR_API_KEY'

Response example

200 — Correct: progress

{  "success": true,  "job": {    "id": "64b0c0ff0000000000000001",    "name": "Morning notices",    "status": "running",    "messagesPerSecond": 10,    "pauseReason": null,    "total": 2,    "validatedCount": 2,    "counts": {      "pending": 1,      "sent": 1,      "failed": 0,      "cancelled": 0    },    "errorCode": null,    "errorMessage": null,    "createdAt": "2026-09-24T12:00:00.000Z"  }}

200 — Correct: rejected, nothing sent

{  "success": true,  "job": {    "id": "64b0c0ff0000000000000001",    "name": "Morning notices",    "status": "rejected",    "messagesPerSecond": 10,    "pauseReason": null,    "total": 2,    "validatedCount": 0,    "counts": {      "pending": 0,      "sent": 0,      "failed": 0,      "cancelled": 0    },    "errorCode": "VALIDATION_FAILED",    "errorMessage": "One or more messages failed validation",    "createdAt": "2026-09-24T12:00:00.000Z"  }}

404 — Incorrect: job not found

{  "success": false,  "code": "JOB_NOT_FOUND",  "error": "Job was not found"}

401 — Missing or invalid API key.

{  "success": false,  "requestId": "msgreq_1739012345678_abc123",  "code": "UNAUTHORIZED",  "error": "Missing or invalid API key"}

403 — Phone is not verified or messages:read is missing. Code FORBIDDEN.

{  "success": false,  "code": "FORBIDDEN",  "error": "Phone not verified or missing permission"}

429 — Rate limit exceeded. Honor Retry-After when present.

{  "success": false,  "requestId": "msgreq_1739012345678_abc123",  "code": "RATE_LIMITED",  "error": "Rate limit exceeded",  "retryAfterSeconds": 30}

HTTP request

GET /api/v2/messages/bulk/{jobId}

Request Headers

HeaderTypeDescription
X-API-KeystringYour Rasel API key. Send it on every documented request except inbound webhooks. *

Path parameters

FieldTypeDescription
jobIdstringJob id from the create response. The job is visible only to that key's account. *

Response Fields

FieldTypeDescriptionExample
successenumTrue when the operation succeeded, or when at least one batch recipient succeeded. *
jobobjectJob details. The following fields belong to this job and are not visible to another account. *
job.idstringResource identifier. *
job.namestringDisplay name or lowercase template identifier, depending on the endpoint. *
job.statusenumDelivery or record status. On send: sent, queued, or scheduled. On other resources it is the row status filter or value. *
job.messagesPerSecondintegerRate ceiling: an integer from 1 to 10. Zero, fractions, and values above 10 reject the request before a job is stored. The ceiling is shared by the account's jobs in creation order. *
job.pauseReasonenumSet only while status is paused. insufficient_balance and provider_rate_limit stop the current window. user means the account requested pause. *
job.totalintegerTotal matching rows. *
job.validatedCountintegerHow many rows have been checked while status is validating. *
job.countsobjectRow counters. They stay separate even when a completed job still has failed rows. *
job.counts.pendingintegerRows not sent yet. *
job.counts.sentintegerRows accepted for delivery. They also appear in the normal message list. *
job.counts.failedintegerRows that failed. A completed job can still have failed rows. *
job.counts.cancelledintegerRows cancelled before send. *
job.errorCodestringJob-level code when status is rejected: VALIDATION_FAILED (one or more rows failed checks; nothing sent), INSUFFICIENT_BALANCE (the account cannot send even one message; nothing sent), INTAKE_INCOMPLETE (storage did not finish; nothing sent). *
job.errorMessagestringShort explanation safe to show the account owner. Match your logic on code, not on this text. *
job.createdAtstringCreation timestamp.

Status Codes

StatusDescription
200Success.
401Missing or invalid API key.
403Phone not verified, missing permission, or the account cannot perform this action.
404Resource not found for this API key owner.
429Rate limit exceeded, or the maximum number of verification attempts was reached.

Cases to handle

  • accepting: storage is not finished and nothing is sent. If storage stops, the job becomes rejected with INTAKE_INCOMPLETE.
  • validating: every row is checked. validatedCount shows progress. Nothing is sent in this state.
  • rejected: nothing was sent. errorCode is VALIDATION_FAILED, INSUFFICIENT_BALANCE, or INTAKE_INCOMPLETE.
  • queued: accepted and waiting for a send slot. running: sending is in progress.
  • paused: sending stopped. pauseReason is insufficient_balance, provider_rate_limit, or user.
  • completed: the job finished even when some rows failed. Counts stay separate.
  • cancelled: rows that were not sent became cancelled. The current window may finish a few sends after pause or cancel is requested.

Try it

Parameters

Job id from the create response. The job is visible only to that key's account.

Request

GET /api/v2/messages/bulk/

Response

Send a request to see the response.