Authentication
Every /v1 request carries one API key in the Authorization header. The key identifies your account and the environment (sandbox or live) and carries the list of operations it may perform.
Authorization: Bearer <your API key>
API keys
A key looks like ak_live_ or ak_sandbox_ followed by 22 characters. The prefix selects the environment, so a sandbox key never touches live data or billing and a live key never runs in sandbox; see Sandbox and live. A request whose key does not match this shape is rejected before anything else happens.
- Keys are issued by Leeri on request. Write to hello@leeri.ai and say which scopes you need. There is no self-service key screen yet.
- A key is a server-side secret. Keep it in your secret store or environment, never in source control, a mobile app or a web page. We store only a hash of it, so a lost key cannot be shown again: ask for a new one.
- Rotation overlaps. To rotate, we issue a new key and give the old one an expiry date; both authenticate until the old key expires, after which it answers
401. Move your services to the new key inside that window. - Revoke on suspicion. If a key may have leaked, tell us and we disable it. Revocation and changes to a key's scopes reach the API within about a minute.
Scopes
Each key carries an explicit list of scopes. A route requires one of them, and a key with no scopes is admitted nowhere: every route answers 403. Ask only for the scopes an integration uses.
| Scope | Allows |
|---|---|
checks:write | Create checks and batches (POST /v1/checks, POST /v1/checks/batch) and submit address reports (POST /v1/address-reports). |
checks:read | Read a check or a batch by id (GET /v1/checks/{check_id}, GET /v1/checks/batch/{batch_id}) and rederive a verdict (POST /v1/verdicts/{verdict_id}/rederive). |
webhooks:manage | Create, list, rotate and disable webhook endpoints. |
telegram:manage | Issue one-time Telegram link codes and list or revoke the chats linked to your account. |
A common split is a key with checks:write and checks:read for the service that screens, and a separate key with webhooks:manage for deployment tooling that registers endpoints.
Make an authenticated request
The call below uses a sandbox key and the sandbox test address, which always answers pending; see Sandbox and live. The key is read from the environment, never written into the code.
- curl
- Node.js
- Python
curl -s -X POST https://api.leeri.ai/v1/checks \
-H "Authorization: Bearer $LEERI_API_KEY" \
-H "Content-Type: application/json" \
-d '{"subject":"address","asset":"BTC","address":"1PENDNGCHECKADDRESSFRSANDBXTEST99"}'
const response = await fetch('https://api.leeri.ai/v1/checks', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.LEERI_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ subject: 'address', asset: 'BTC', address: '1PENDNGCHECKADDRESSFRSANDBXTEST99' }),
});
if (response.status === 401) throw new Error('API key missing, malformed, unknown or expired');
if (response.status === 403) throw new Error('API key lacks the checks:write scope');
console.log((await response.json()).data.status);
import os
import requests
response = requests.post(
"https://api.leeri.ai/v1/checks",
headers={"Authorization": f"Bearer {os.environ['LEERI_API_KEY']}"},
json={"subject": "address", "asset": "BTC", "address": "1PENDNGCHECKADDRESSFRSANDBXTEST99"},
timeout=30,
)
if response.status_code == 401:
raise RuntimeError("API key missing, malformed, unknown or expired")
if response.status_code == 403:
raise RuntimeError("API key lacks the checks:write scope")
print(response.json()["data"]["status"])
401 and 403
| Status | Code | Meaning | What to do |
|---|---|---|---|
401 | AUTH_ERROR | The header is missing or malformed, or the key is unknown, revoked or expired. | Check the header and the key. Do not retry the same request. |
403 | SCOPE_ERROR | The key is valid but does not carry the scope the route needs. | Ask for the missing scope, or use the key that has it. |
Failed authentication is rate limited per source IP address: after five failed attempts in a minute, further requests from that address answer 429 RATE_LIMIT_EXCEEDED until the minute ends. A retry loop that keeps sending a bad key therefore locks out your own server, so stop on 401. See Rate limits and quotas.
An id that belongs to another account, or to the other environment, answers 404 exactly like an id that does not exist, so a key can never be used to find out whether someone else's resource exists.
Call from your backend
The API is meant for server-to-server use. It does not allow cross-origin requests from third-party websites, so a browser cannot call it, and putting a key into a web or mobile client would expose it. Send checks from your backend and give your front end only the result you want it to show.