GET /apiv2/screening/history
Your screening history, newest first, with cursor pagination.
This is the version 2 contract. It replaces GET /apiv2/aml/history, which keeps working.
Endpoint URL
GET https://netts.io/apiv2/screening/historyRequest Headers
| Header | Required | Description |
|---|---|---|
| X-API-KEY | Yes | Your API key from the Netts dashboard |
Query Parameters
All filters are optional. Without any of them you get your whole history.
| Parameter | Type | Default | Description |
|---|---|---|---|
| address | string | — | Exact address, 10–128 characters |
| network | string | — | Network ticker |
| provider | string | — | elliptic or bitok |
| status | string | — | pending, processing, completed, skipped, failed |
| from | string | — | Only checks created at or after this moment, RFC 3339 |
| to | string | — | Only checks created at or before this moment, RFC 3339 |
| cursor | string | — | Where to continue from. Take it from next_cursor |
| limit | integer | 50 | Items per page, 1 to 200 |
In version 1 address and network were both required, so there was no way to ask "what have I checked lately".
Checks with status skipped are included. Version 1 hides them. A skipped check is a real order — the address had no blockchain activity, so it was never sent to the provider and never charged — and it belongs in the history.
Example Requests
cURL
curl "https://netts.io/apiv2/screening/history?limit=50" \
-H "X-API-KEY: your_api_key"Python — walk the whole history
import requests
headers = {"X-API-KEY": "your_api_key"}
params = {"limit": 200, "provider": "elliptic"}
while True:
page = requests.get("https://netts.io/apiv2/screening/history",
headers=headers, params=params).json()
for item in page["items"]:
print(item["order"]["client_order_id"],
item["order"]["status"],
item["risk"]["level"],
item["sanctions"]["verdict"])
if not page["next_cursor"]:
break
params = {"limit": 200, "provider": "elliptic", "cursor": page["next_cursor"]}Keep the filters identical while paging. Changing one while carrying the same cursor is an error, not a silent switch to a different set.
Response
{
"schema_version": 2,
"items": [
{
"order": {
"client_order_id": "A6F3221BAAE093A",
"status": "completed",
"api_version": "v2",
"cache_hit": false,
"created_at": "2026-09-13T08:24:19.838584Z",
"started_at": "2026-09-13T08:24:20.998619Z",
"completed_at": "2026-09-13T08:24:25.179967Z"
},
"request": {
"address": "YOUR_ADDRESS_HERE",
"network": "trx",
"provider": "elliptic"
},
"check": {
"provider": "elliptic",
"provider_check_id": "1cc2fd64-1483-4ce3-946f-23a614f41a12",
"checked_at": "2026-09-13T08:24:22.372000Z",
"status": "completed",
"provider_status": "complete"
},
"risk": {
"score": "0.12428176721891304",
"scale": { "min": 0, "max": 10 },
"level": "low",
"level_source": "computed",
"provider_level": null,
"policy": "netts-risk-v1",
"by_direction": { "source": "0.12428176721891304", "destination": null }
},
"sanctions": { "verdict": "linked" }
}
],
"next_cursor": "eyJmIjp7InN0YXR1cyI6ImNvbXBsZXRlZCJ9LCJpIjoxMzQyNywidCI6...",
"limit": 1
}| Field | Type | Description |
|---|---|---|
| items | array | The page, newest first |
| next_cursor | string | null | Pass it back to get the next page. null means you have reached the end |
| limit | integer | The limit that was applied |
The short form of an item
The order, request, check and risk blocks are identical to the ones in the full response of GET /apiv2/screening/{client_order_id}, field for field, so the same parser handles both.
What is left out: provider_data, exposure[], rules[], entities[], wallet, billing, precheck, and the full sanctions block. A single Elliptic result is around 150 KB, and a page of fifty would be seven megabytes. Fetch a single check when you need the detail.
sanctions.verdict
The sanctions analysis compressed to one word.
| Value | Meaning |
|---|---|
listed | The address itself is on a sanctions list |
linked | A sanctions link was found, but the address is not itself listed |
none | The analysis ran and found nothing |
null | There is no result to analyse yet |
The difference between listed and linked is the point of the field — see Sanctions in an AML result.
Pagination
Version 1 pages by number: ?page=2, 100 per page. The order is by creation time, newest first, so while you walk from page 1 to page 2 new checks arrive and push everything down. Records you already saw reappear, records you have not seen slide past you. With a busy account this is not a corner case.
A cursor points at a place in the set rather than at its ordinal number, so new checks arriving during the walk do not disturb it.
- the order is
created_at DESC, id DESC. Both fields are in the cursor, becausecreated_atis not unique — two checks created in the same microsecond would otherwise loop or skip; - the cursor is opaque. Its contents are an implementation detail; pass it back exactly as you received it;
- the filters are part of the cursor. Changing one while reusing the cursor returns
400, not a silent switch to a different set — otherwise you would believe you had read a set you never read; next_cursor: nullmeans the end. There is no total count: counting the whole set on every page costs more than it tells you.
Errors
RFC 9457, application/problem+json. The full code list is on the POST page.
| Code | HTTP | When |
|---|---|---|
4001 | 400 | limit outside 1…200, an unknown network, provider or status, a from/to that is not RFC 3339, a malformed cursor, or a cursor issued for different filters |
4010 / 4011 | 401 | No API key, or a key or IP that is not accepted |
{
"type": "https://doc.netts.io/api/v2/errors/validation-failed",
"title": "Request validation failed",
"status": 400,
"detail": "Cursor was issued for a different set of filters",
"instance": "/apiv2/screening/history",
"code": 4001
}Rate Limits
Shared with every other AML path: 5 requests per second, 150 per minute. With limit=200 a full history of ten thousand checks is fifty requests.