API documentation
Upload numbers, get quotes, place orders and download results over HTTP. Built for teams that process in bulk and automate. The rules match web ordering, and prices follow your account.
Get API access
- Ask support to open an account for you.
- Send support the services you need, your account and your server egress IPs.
- We then issue an API key and the full integration guide. Keys are valid for 1–730 days, and each key allows up to 32 egress IPs.
Basics
Base URL
https://api.checkim.ai/openapi/v1- Authentication
- Send Authorization: Bearer <API key> with every request
- User-Agent
- Required. Requests without one are blocked by Cloudflare (error 1010)
- Format
- JSON requests and responses in UTF-8, except file uploads
- Time
- UTC Unix timestamps in milliseconds
- Amounts
- Up to eight decimal places. Use a decimal type and do not round to two places
Integration flow
- List services and check your balance. Keep the productId you need.
- Read the data-use statement and accept it if you have not yet.
- Upload the number file and keep the returned fileId.
- Get a quote with the productId and fileId. Check the count, unit price and total.
- Create and store an idempotency key, then place the order. This charges your balance.
- Set up a webhook for order status pushes, and reconcile with updatedSince every 5–10 minutes so nothing is missed.
- When the order completes, list the result files, download every one and check its sha256. Download the unsuccessful files too.
curl example
Replace the placeholders. The full quote and order parameters are in the guide you receive with access.
Set your key and list services
export CHECKIM_TOKEN='replace with the API key from support' export CHECKIM_BASE='https://api.checkim.ai/openapi/v1' curl -H "Authorization: Bearer $CHECKIM_TOKEN" "$CHECKIM_BASE/products"Upload a number file
curl -H "Authorization: Bearer $CHECKIM_TOKEN" \ -F 'file=@./numbers.csv;type=text/csv' \ "$CHECKIM_BASE/files"Get a quote
curl -H "Authorization: Bearer $CHECKIM_TOKEN" \ -H 'Content-Type: application/json' \ -d '{"productId":"<productId>","name":"batch-001","fileId":"<fileId>"}' \ "$CHECKIM_BASE/orders/check"Place the order
# Charges your balance. Keep the key and body identical when retrying. curl -H "Authorization: Bearer $CHECKIM_TOKEN" \ -H 'Content-Type: application/json' \ -H 'Idempotency-Key: partner-request-001' \ -d '{"quoteId":"<quoteId>","clientOrderId":"partner-batch-001"}' \ "$CHECKIM_BASE/orders"Read an order, or reconcile by change time
curl -H "Authorization: Bearer $CHECKIM_TOKEN" "$CHECKIM_BASE/orders/<orderId>" curl -H "Authorization: Bearer $CHECKIM_TOKEN" \ "$CHECKIM_BASE/orders?updatedSince=<UTC ms>&size=100"List and download results, then check the sha256
curl -H "Authorization: Bearer $CHECKIM_TOKEN" "$CHECKIM_BASE/orders/<orderId>/results" curl -H "Authorization: Bearer $CHECKIM_TOKEN" -o result.csv \ "$CHECKIM_BASE/orders/<orderId>/results/<fileId>/download" shasum -a 256 result.csv
Endpoints
Account and statement
| Endpoint | Purpose |
|---|---|
GET/compliance | Read the data-use statement and whether you accepted it |
POST/compliance | Accept the current statement version |
GET/balance | Read the account balance |
GET/products | List orderable services and your unit prices |
Files and quotes
| Endpoint | Purpose |
|---|---|
POST/files | Upload a number file (multipart, field "file") |
POST/orders | Validate numbers and get a quote, valid for 180 seconds |
Orders
| Endpoint | Purpose |
|---|---|
POST/orders | Place the order for a quote; needs an Idempotency-Key header |
GET/orders | List orders: cursor paging, filters by status and creation time, reconcile with updatedSince |
GET/orders | Read an order status and counts |
POST/orders | Cancel an order: queued and undispatched orders are fully refunded at once, dispatched ones move to cancelling, running ones cannot be cancelled |
Results
| Endpoint | Purpose |
|---|---|
GET/orders | List the result files, each with its sha256 |
GET/orders | Download one result file, with Range resume and If-None-Match |
Webhook
| Endpoint | Purpose |
|---|---|
GET/webhook | Read the webhook settings and delivery health |
PUT/webhook | Set the receiving URL and the events to send |
DELETE/webhook | Turn the webhook off |
GET/webhook | Read the signing secret |
POST/webhook | Rotate the signing secret; old and new both sign for 24 hours |
POST/webhook | Send a test event |
GET/webhook | List recent deliveries |
Listing and reconciling orders
- Orders are listed newest first. When a page has nextCursor, pass it back as cursor for the next page; cursor paging never skips or repeats an order while new ones arrive.
- Filter by status and by creation time (createdFrom, createdTo, UTC milliseconds), or look one order up by its clientOrderId.
- updatedSince returns only orders whose status changed after that time, oldest change first. Pull with it regularly and a missed webhook never loses a status.
Downloading results
- Every file in the result list has a sha256. The download ETag is the same value, so compare it after downloading.
- Range requests resume a download. Check the sha256 has not changed first; if it has, the file was rebuilt and you need the whole file again.
- When you already have the complete file, send If-None-Match and an unchanged file returns 304 with no body.
- Large orders with batch delivery list finished batches while still running; the final file after completion is the authoritative one.
- For services with avatars (Telegram and WhatsApp extended or gender and age checks), the avatar column in the result CSV contains a signed image URL that can be fetched directly with GET while results are retained (do not send the Authorization header; If-None-Match is supported).
Webhooks
- When the status of an order placed through the API changes, we POST an event to your HTTPS URL. Needs the webhook:manage permission.
- Each event carries a CheckIM-Signature header (t=timestamp,v1=signature). Verify it with HMAC-SHA256 of "t.raw request body" using your signing secret.
- Any 2xx counts as delivered; failures are retried 9 times over about 2 days. You may receive an event more than once, so deduplicate by its id.
- Requests come from the fixed egress IP 34.21.195.36, so you can allow only that address.
| Event | Sent when |
|---|---|
order.completed | The order is settled and results can be downloaded |
order.cancelled | The order was cancelled and fully refunded |
order.cancel_pending | The order is cancelling and settles once execution stops |
order.needs_attention | The order went to after-sales or needs manual handling |
order.partial_results | A new batch of results is ready to download |
Order statuses
| Status | Meaning |
|---|---|
inQueue | Queued. Keep polling |
orderTaken | Accepted. Keep polling |
executing | Running. Large orders with batch delivery already offer finished batches |
completed | Done. Download the results and check the counts |
waitAfterSale | An after-sales request is being handled |
pendingCancel | Cancelling. Keep polling for the final status |
cancelled | Cancelled. Fully refunded if still queued, otherwise billed for the work done |
Limits and notes
- By default, at most 1,200 requests per minute per egress IP, 240 per account and 120 per key. The RateLimit-Limit, RateLimit-Remaining and RateLimit-Reset headers tell you how many are left this minute.
- Quotes and uploads are slow, so each account can run at most 3 of them at once. More return 429 TOO_MANY_CONCURRENT_REQUESTS.
- On 429 or 503, wait the seconds given in Retry-After, then retry the same request.
- There is no sandbox. Every order is a real charge.
- A polling timeout does not mean the order failed. Look the order up by orderId or clientOrderId before ordering again.
Common errors
Errors carry code, requestId, message and an optional detail.field. Handle them by code; message comes in one of 9 languages by Accept-Language and is for display only.
| Code | HTTP | What to do |
|---|---|---|
INVALID_REQUEST | 400 | Fix the parameter named in detail.field |
INVALID_CURSOR | 400 | The cursor is invalid; start from page one |
INVALID_API_TOKEN | 401 | Check the key is complete, not expired and not revoked |
PERMISSION_DENIED | 403 | Ask support to check your services and egress IPs |
QUOTE_EXPIRED | 409 | Confirm no order was placed, then quote again |
INSUFFICIENT_BALANCE | 409 | Top up, then quote and order again |
ORDER_NOT_CANCELLABLE | 409 | The order has started or finished and cannot be cancelled |
RETRY_SAME_REQUEST | 409 | It clashed with another operation; retry the same request with backoff |
COMPLIANCE_ACCEPTANCE_REQUIRED | 422 | Read and accept the data-use statement first |
RATE_LIMITED | 429 | Wait for Retry-After and send less often |
TOO_MANY_CONCURRENT_REQUESTS | 429 | Wait for an earlier quote or upload to finish |
DATABASE_UNAVAILABLE | 503 | Temporarily unavailable; retry after Retry-After |
Need API access?
Send support the services you need and your egress IPs. We will issue a key and the full guide, with a Python sample client.