Batch
A batch screens up to 100 addresses or transactions in one request. Use it to re-screen an existing customer base or to work through a backlog without sending a hundred separate requests; a batch counts as one request against the rate limit.
Submit a batch
POST /v1/checks/batch with the checks:write scope.
{
"items": [
{ "subject": "address", "asset": "TRX", "address": "…" },
{ "subject": "transaction", "asset": "ETH", "tx_hash": "…", "direction": "deposit", "address": "…" }
]
}
itemsholds 1 to 100 entries. An empty list, more than 100 items, or an unknown field answers400VALIDATION_ERROR.- Each item has the body of a single check: see Address checks and Transaction checks.
- Items always send
address, also transaction items. A batch is accepted without reading any chain, so the address cannot be derived from the transaction. An item without it is a validation error. - An item with an asset that is not supported answers
422UNSUPPORTED_ASSETand the whole batch is refused.
The API accepts the batch and answers 202 without screening anything inside the request:
{
"status": "ok",
"data": {
"batch_id": "c1e5a9d2-3f1b-4a67-b2c8-7d90e4f61a35",
"item_count": 2,
"status": "queued",
"items": [
{ "item_index": 0, "check_id": "8a2f6c10-5d4e-4b73-9a1c-0e3b7d52f984" },
{ "item_index": 1, "check_id": "2d7b1e90-6a3c-4f58-8c14-b5a09f3e7d62" }
]
},
"errors": []
}
item_index is the item's zero-based position in the items array and stays the same for the life of the batch. Every item is a full check with its own check_id.
- curl
- Node.js
- Python
curl -s -X POST https://api.leeri.ai/v1/checks/batch \
-H "Authorization: Bearer $LEERI_API_KEY" \
-H "Idempotency-Key: rescreen-2026-10" \
-H "Content-Type: application/json" \
-d '{"items":[
{"subject":"address","asset":"TRX","address":"'"$ADDRESS_1"'"},
{"subject":"address","asset":"BTC","address":"'"$ADDRESS_2"'"}
]}'
const response = await fetch('https://api.leeri.ai/v1/checks/batch', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.LEERI_API_KEY}`,
'Idempotency-Key': 'rescreen-2026-10',
'Content-Type': 'application/json',
},
body: JSON.stringify({
items: addresses.map(({ asset, address }) => ({ subject: 'address', asset, address })),
}),
});
const { data } = await response.json();
console.log(data.batch_id, data.item_count); // status is "queued"
import os
import requests
response = requests.post(
"https://api.leeri.ai/v1/checks/batch",
headers={
"Authorization": f"Bearer {os.environ['LEERI_API_KEY']}",
"Idempotency-Key": "rescreen-2026-10",
},
json={"items": [{"subject": "address", "asset": a["asset"], "address": a["address"]} for a in addresses]},
timeout=30,
)
data = response.json()["data"]
print(data["batch_id"], data["item_count"]) # status is "queued"
Admission is all or nothing
Every item is reserved and joined to the batch in one step. If anything cannot be admitted, nothing is accepted and nothing is charged:
| Status | Code | Meaning |
|---|---|---|
402 | INSUFFICIENT_BALANCE | Quota and prepaid balance cannot cover every item. |
429 | PROVIDER_CAPACITY_EXCEEDED | Lookup capacity is full right now. Retry after Retry-After. |
422 | PROVIDER_CAPACITY_REQUEST_TOO_LARGE | The batch alone needs more capacity than your account holds. It can never succeed and is never retryable: split it into smaller batches. |
Each live item can hold up to 10 lookup slots until its lookups are frozen, and the default account capacity always admits one full batch of 100 when nothing else is waiting. See Rate limits and quotas.
Read the results
Items are screened in the background, behind a rate ceiling on third-party lookups, so a large batch takes time. Read progress with GET /v1/checks/batch/{batch_id} (scope checks:read):
| Field | Meaning |
|---|---|
batch_id, item_count | The batch and how many items it holds. |
status | queued, running or completed. |
completed_count, failed_count | How many items finished with a verdict and how many failed. |
items | Every item, each with the same body as GET /v1/checks/{check_id} plus item_index. |
The batch becomes completed when completed_count + failed_count reaches item_count. A batch never fails as a whole because one item did: read each item's own status, which is completed, pending or failed as for any check, and see Pending and polling for what those mean.
Items are ordinary checks, so you can also read one by its check_id with GET /v1/checks/{check_id}. When a webhook is registered, each item that completes triggers its own check.completed delivery, one per item and not one per batch; see Webhooks.
- curl
- Node.js
- Python
curl -s https://api.leeri.ai/v1/checks/batch/$BATCH_ID \
-H "Authorization: Bearer $LEERI_API_KEY"
async function waitForBatch(batchId) {
for (;;) {
const response = await fetch(`https://api.leeri.ai/v1/checks/batch/${batchId}`, {
headers: { Authorization: `Bearer ${process.env.LEERI_API_KEY}` },
});
if (!response.ok) throw new Error(`poll failed with ${response.status}`);
const { data } = await response.json();
if (data.status === 'completed') return data;
await new Promise((resolve) => setTimeout(resolve, 10_000));
}
}
const batch = await waitForBatch(batchId);
for (const item of batch.items) console.log(item.item_index, item.status, item.band);
import os
import time
import requests
def wait_for_batch(batch_id):
while True:
response = requests.get(
f"https://api.leeri.ai/v1/checks/batch/{batch_id}",
headers={"Authorization": f"Bearer {os.environ['LEERI_API_KEY']}"},
timeout=30,
)
response.raise_for_status()
data = response.json()["data"]
if data["status"] == "completed":
return data
time.sleep(10)
batch = wait_for_batch(batch_id)
for item in batch["items"]:
print(item["item_index"], item["status"], item["band"])
Retrying a batch
Send an Idempotency-Key with the batch. Replaying the same key with the same body returns the same batch instead of creating a second one, and each item is keyed from the batch key and its position, so no item can be screened twice. The same key with a different body answers 422 IDEMPOTENCY_KEY_CONFLICT. See Idempotency.