POST /apiv2/screening
Замовте AML-перевірку блокчейн-адреси. Це контракт версії 2: однакова структура відповіді для кожного провайдера та будь-якого стану замовлення, десяткові числа у вигляді рядків і єдиний формат помилок.
Він замінює POST /apiv2/aml, який продовжує працювати і не буде вимкнений без попередження.
URL кінцевої точки
POST https://netts.io/apiv2/screeningЗаголовки запиту
| Заголовок | Обов'язковий | Опис |
|---|---|---|
| Content-Type | Так | application/json |
| X-API-KEY | Так | Ваш API-ключ із панелі керування Netts |
| X-Idempotency-Key | Ні | Ваш власний ключ для безпечних повторних спроб. Див. Ідемпотентність |
Тіло запиту
{
"address": "YOUR_ADDRESS_HERE",
"network": "trx",
"provider": "elliptic",
"wait_for_result": true
}Параметри
| Параметр | Тип | Обов'язковий | Опис |
|---|---|---|---|
| address | string | Так | Адреса для перевірки, від 10 до 128 символів |
| network | string | Так | Тікер мережі. Тікери, які підтримує кожен провайдер, наведено у списку GET /apiv2/screening/providers; повна таблиця мереж з їхніми назвами доступна тут |
| provider | string | Так | elliptic або bitok. Значення за замовчуванням відсутнє |
| wait_for_result | boolean | Ні | true очікує на результат до 15 секунд. За замовчуванням false |
| language | string | Ні | Мова звіту. Лише en |
Невідомі поля відхиляються. Тіло запиту, що містить поле, якого немає в таблиці вище, повертає 400 з кодом 4001. У версії 1 невідомі поля мовчки ігнорувалися, і помилка в написанні wait означала, що клієнт чекав на результат, який ніколи не міг надійти синхронно.
Поле provider є обов'язковим і не має значення за замовчуванням. У версії 1 пропущений провайдер означав Elliptic, тому клієнт, який не зробив вибір, сплачував за провайдера, якого взагалі не вказував.
provider є довільним рядком у схемі, а не переліком (enum). Наразі приймаються два значення; додавання третього провайдера не повинно стати критичною зміною (breaking change) для тих, хто валідує відповіді за схемою. Актуальний перелік, мережі, які підтримує кожен провайдер, та шкали їхніх оцінок повертаються у GET /apiv2/screening/providers.
Приклади запитів
cURL — очікування результату
curl -X POST https://netts.io/apiv2/screening \
-H "Content-Type: application/json" \
-H "X-API-KEY: your_api_key" \
-d '{
"address": "YOUR_ADDRESS_HERE",
"network": "trx",
"provider": "elliptic",
"wait_for_result": true
}'cURL — прийняття в обробку та опитування
curl -X POST https://netts.io/apiv2/screening \
-H "Content-Type: application/json" \
-H "X-API-KEY: your_api_key" \
-d '{
"address": "YOUR_ADDRESS_HERE",
"network": "trx",
"provider": "bitok"
}'Відповідь — 202 Accepted із заголовком Location, що вказує на замовлення.
Python
import requests
from decimal import Decimal
url = "https://netts.io/apiv2/screening"
headers = {
"Content-Type": "application/json",
"X-API-KEY": "your_api_key",
}
payload = {
"address": "YOUR_ADDRESS_HERE",
"network": "trx",
"provider": "elliptic",
"wait_for_result": True,
}
response = requests.post(url, headers=headers, json=payload)
if response.status_code in (200, 202):
body = response.json()
print("Order:", body["order"]["client_order_id"])
print("Status:", body["order"]["status"])
if body["risk"]["score"] is not None:
# Parse provider numbers as Decimal, never as float
print("Score:", Decimal(body["risk"]["score"]), "of", body["risk"]["scale"]["max"])
print("Level:", body["risk"]["level"], "-", body["risk"]["level_source"])
print("Charged:", body["billing"]["charged_amount"], body["billing"]["charged_currency"])
else:
problem = response.json()
print(problem["code"], problem["title"], "-", problem["detail"])Коди відповідей
| Ситуація | Код | Заголовки |
|---|---|---|
| Замовлення створено, перевірка виконується у фоновому режимі | 202 Accepted | Location: /apiv2/screening/{client_order_id} |
Результат міститься у відповіді (wait_for_result) | 200 OK | — |
| Результат використано повторно з нещодавньої перевірки, кошти не списувалися | 200 OK | — |
| Адреса не має активності в блокчейні, кошти не списувалися | 200 OK | — |
| Помилка | див. Помилки | Content-Type: application/problem+json |
Відповіді, що містять результат перевірки, надсилаються з Cache-Control: private, no-store.
Відповідь
{
"schema_version": 2,
"order": {
"client_order_id": "A90D21F68C9AEA2",
"status": "completed",
"api_version": "v2",
"cache_hit": false,
"created_at": "2026-09-13T08:14:29.614988Z",
"started_at": "2026-09-13T08:14:30.288251Z",
"completed_at": "2026-09-13T08:14:34.993174Z"
},
"request": {
"address": "YOUR_ADDRESS_HERE",
"network": "trx",
"provider": "elliptic"
},
"billing": {
"charged": true,
"price_usdt": "0.98",
"base_amount": "2.882421",
"markup_amount": "0",
"charged_amount": "2.882421",
"charged_currency": "TRX",
"exchange_rate": "0.33999200",
"payment_status": "pending"
},
"precheck": {
"activity_checked": true,
"activity_status": "active",
"source": "tron-address-checker"
},
"check": {
"provider": "elliptic",
"provider_check_id": "b4903a5d-9f22-4075-b51b-beb40b419b97",
"checked_at": "2026-09-13T08:14:32.144000Z",
"status": "completed",
"provider_status": "complete"
},
"risk": {
"score": "0.9634087310611608",
"scale": { "min": 0, "max": 10 },
"level": "low",
"level_source": "computed",
"provider_level": null,
"policy": "netts-risk-v1",
"by_direction": {
"source": "0.12332156899576921",
"destination": "0.9634087310611608"
}
},
"sanctions": {
"self": false,
"self_entities": null,
"exposure": {
"entity": "HTX (formerly Huobi) - Post-Sanctions - UK Sanctions List - 26 May 2026",
"category": "Exchange",
"share_fraction": "0.004409451392423414",
"proximity": "indirect",
"hops": 3,
"direction": "source",
"rule_name": "Sanctioned, TF & CSAM"
},
"items": []
},
"exposure": [
{
"category": "Exchange",
"share_fraction": "0.1888065152442461",
"direction": "source",
"hops": 2,
"is_screened_address": false,
"entities": [
{ "name": "Binance", "category": "Exchange", "is_vasp": true,
"is_primary": true, "is_after_sanction_date": null }
]
}
],
"rules": [
{ "name": "Sanctioned, TF & CSAM", "type": "exposure", "level": null,
"score": "0.061426483114820525", "share_fraction": null, "category": null,
"proximity": null, "direction": "source", "detected_at": null }
],
"entities": [
{ "name": "Unknown", "category": "Unknown", "is_vasp": null,
"is_primary": true, "is_after_sanction_date": false }
],
"primary_entity": {
"name": "Unknown", "category": "Unknown", "is_vasp": null,
"is_primary": true, "is_after_sanction_date": false
},
"sanctioned_entities": [],
"wallet": {
"inflow_usd": "354663.6116669158",
"outflow_usd": "80248.63081808022"
},
"provider_data": { }
}Набір полів ніколи не змінюється
Кожен блок, наведений вище, присутній у будь-якій відповіді, незалежно від провайдера та стану замовлення. Дані, які провайдер не надає, повертаються як null; список, у якому немає елементів, повертається як [], а не null; блок, для якого ще немає даних, заповнюється значеннями null, а не опускається. Один і той самий парсер обробляє перевірку, яка щойно була прийнята, і ту саму перевірку після її завершення.
Два важливих правила для вашого коду:
- ігноруйте незнайомі поля. Нові поля додаються до цих блоків без випуску нової версії API. Відхилення невідомого поля — це помилка на вашому боці, а не на нашому;
provider_dataне є частиною контракту. Його структура відповідає даним провайдера і змінюється зі зміною провайдера. Усе, що гарантується контрактом, міститься у вищенаведених блоках.
order
| Поле | Тип | Опис |
|---|---|---|
| client_order_id | string | Ідентифікатор замовлення, використовується для подальшого отримання результату |
| status | string | pending, processing, completed, skipped, failed |
| api_version | string | Контракт, за допомогою якого було створено замовлення |
| cache_hit | boolean | true, якщо було повторно використано нещодавній результат і кошти не списувалися |
| created_at | string | RFC 3339, UTC, мікросекунди |
| started_at | string | null | Час початку виклику провайдера. null для skipped |
| completed_at | string | null | Час отримання результату |
| error | string | Тільки для failed: причина невдачі |
| reason | string | Тільки для skipped: address_inactive |
Усі часові мітки наведено в UTC, форматі RFC 3339, із суфіксом Z та точністю до мікросекунд.
billing
| Поле | Тип | Опис |
|---|---|---|
| charged | boolean | Чи було списано кошти |
| price_usdt | string | Базова вартість перевірки провайдера в USDT |
| base_amount | string | Вартість замовлення у валюті списання без націнки субкористувача |
| markup_amount | string | Націнка субкористувача. "0" для прямого акаунта |
| charged_amount | string | Сума, фактично списана з балансу |
| charged_currency | string | TRX |
| exchange_rate | string | null | Курс, використаний для конвертації |
| payment_status | string | paid, pending, failed, not_charged |
payment_status має значення pending протягом короткого часу після успішної перевірки: сума списання спочатку блокується і остаточно списується протягом години. failed означає, що кошти було повернено. not_charged означає, що списання взагалі не створювалося — через повторне використання результату або пропуск неактивної адреси.
precheck
Перед платною перевіркою адреса перевіряється на наявність активності в блокчейні. Адреса без активності не надсилається провайдеру, і кошти за неї не списуються.
| Поле | Тип | Опис |
|---|---|---|
| activity_checked | boolean | Чи запускалася попередня перевірка. false для мереж, де вона не підтримується |
| activity_status | string | active, inactive, unknown |
| source | string | null | Назва механізму перевірки |
Статус unknown не зупиняє платну перевірку: якщо сервіс визначення активності недоступний, адреса вважається активною.
check
| Поле | Тип | Опис |
|---|---|---|
| provider | string | Провайдер, який виконав перевірку |
| provider_check_id | string | null | Власний ідентифікатор провайдера — вказуйте його під час оскарження результату перед провайдером |
| checked_at | string | null | Час формування результату провайдером |
| status | string | Див. таблицю нижче |
| provider_status | string | null | Оригінальний статус провайдера без змін |
order.status | check.status | Значення |
|---|---|---|
pending | pending | Замовлення прийнято, обробку ще не розпочато |
processing | running | Провайдер обробляє запит |
completed | completed | Результат отримано |
failed | failed | Запит відхилено до або під час звернення до провайдера |
skipped | not_performed | Адреса не має активності; запит до провайдера не виконувався, кошти не списувалися |
risk
| Поле | Тип | Опис |
|---|---|---|
| score | string | null | Власна оцінка провайдера у вигляді десяткового рядка |
| scale | object | min та max шкали відповідного провайдера |
| level | string | none, low, medium, high, severe |
| level_source | string | computed — рівень розраховано нашою системою на основі оцінки; provider — рівень надано провайдером |
| provider_level | string | null | Власне позначення провайдера, якщо воно повертається |
| policy | string | Назва політики порогових значень, netts-risk-v1 |
| by_direction | object | Оцінка, розділена на source та destination, якщо провайдер надає такий поділ |
Оцінка ніколи не перераховується під іншу шкалу. Elliptic використовує діапазон 0–10, а BitOK — 0–1, і значення 7 за однією шкалою не є еквівалентним 0.7 за іншою в практичному сенсі. Шкала повертається у відповіді для того, щоб інтеграція, налаштована на одного провайдера, не інтерпретувала хибно дані іншого після звичайної зміни конфігурації.
Рівень ризику уніфіковано в межах усього API. Якщо провайдер повертає власний рівень, ми передаємо його без змін і позначаємо це в level_source; якщо ні, ми вираховуємо рівень на основі оцінки за пороговими значеннями netts-risk-v1 і вказуємо відповідне джерело. Одне й те саме слово відображається у відповіді API, панелі керування та PDF-звіті для однієї і тієї ж перевірки.
exposure[], rules[], entities[]
exposure[] розподіляє кошти за категоріями контрагентів. rules[] містить перелік спрацьованих правил провайдера. entities[] містить організації, до яких належить сама адреса; primary_entity обирає одну з них за фіксованим правилом — організація, яку провайдер позначив як основну, інакше перша зі списку, інакше null. sanctioned_entities[] містить ті сутності з entities[], які позначено як активні після дати введення санкцій.
Частки виражаються дробами, а не відсотками
Кожна частка у відповіді міститься в єдиному полі share_fraction — десятковому рядку зі значенням від "0" до "1".
Elliptic reports 31.574732212596924 % -> "share_fraction": "0.31574732212596924"
BitOK reports 0.8488 -> "share_fraction": "0.8488"Провайдери використовують різні одиниці вимірювання: одна й та сама третина зв'язків надходить як 31.57 від одного та як 0.3157 від іншого. Одне поле, що повертало б такі різні формати, неможливо було б коректно інтерпретувати без знання конкретного провайдера. Початкове число в одиницях вимірювання провайдера залишається в provider_data.
Числа повертаються як рядки
Кожне числове значення від провайдера — оцінки, частки, обсяги в USD, а також будь-яка сума в блоці billing — передається як десятковий рядок.
"score": "0.9634087310611608"Його парсинг як числового типу JSON у JavaScript, Go чи будь-якій іншій мові з двійковою плаваючою комою повертає наближене значення, через що виведене число перестає збігатися зі значенням, яке повернув провайдер. Використовуйте для обробки цих полів типи для точних десяткових чисел: Decimal у Python, BigDecimal у Java, decimal.Decimal або звичайний рядок у JavaScript.
Поля, що формуються нашою платформою, а не провайдером — scale.min, scale.max, hops — є звичайними числами JSON.
Повторне використання нещодавнього результату
Якщо ви повторно перевіряєте ту саму адресу, мережу та провайдера протягом 60 секунд, вам повертається попередній результат, і кошти не списуються.
Кожен запит усе одно створює власне замовлення зі своїм client_order_id; повторно використане замовлення позначається "cache_hit": true, а його блок billing повертає "charged": false зі значенням "payment_status": "not_charged". Ідентифікатор початкового замовлення, з якого взято результат, не розголошується — він може належати іншому акаунту.
Повторне використання діє лише в межах одного акаунта. Результат перевірки, виконаної кимось іншим, ніколи не повернеться до вас.
Idempotency
Надсилайте заголовок X-Idempotency-Key із власним унікальним значенням для безпечного повторення запитів: однаковий ключ з однаковим тілом запиту повертає збережену відповідь замість запуску другого замовлення.
| Ситуація | Код | Відповідь |
|---|---|---|
| Перший запит із цим ключем ще виконується | 409 | 4090 |
| Той самий ключ, але інше тіло запиту | 409 | 4093 |
| Той самий ключ, те саме тіло, виконання вже завершено | збережений код | збережена відповідь |
Якщо ви не надсилаєте цей заголовок, ключ генерується автоматично на основі API-ключа, адреси, провайдера та вашої IP-адреси з 2-секундним вікном. Це захищає від подвійного натискання та повторних запитів шлюзу, але не від повторного запиту через хвилину: такий запит вважатиметься новим замовленням і буде тарифікований.
Ключі діють виключно в межах окремого ендпоінта. Одне й те саме значення, надіслане до POST /apiv2/aml і до цього ендпоінта, розглядається як дві незалежні дії для двох різних запитів — тіла запитів відрізняються, як і відповіді на них. Повторне використання ключа під час міграції інтеграції з версії 1 на версію 2 безпечне: воно не поверне вам відповідь версії 1 і не буде розцінено як використання того самого ключа з іншим тілом запиту.
Помилки
Кожна помилка, згенерована сервісом, повертається відповідно до RFC 9457 із заголовком Content-Type: application/problem+json:
{
"type": "https://doc.netts.io/api/v2/errors/insufficient-funds",
"title": "Insufficient funds",
"status": 403,
"detail": "Insufficient funds. Required: 2.888742 TRX, Available: 1.203000 TRX",
"instance": "/apiv2/screening",
"code": 1004,
"required_trx": "2.888742",
"available_trx": "1.203000"
}type, title, status, detail та instance є стандартними полями. Числовий code збережено як розширення, щоб інтеграції, розроблені під версію 1, могли продовжувати перевіряти помилки за ним. Додаткові поля залежать від помилки і мають ігноруватися, якщо вони вам невідомі.
| Код | HTTP | Значення |
|---|---|---|
4000 | 400 | Тіло запиту не є коректним JSON |
4001 | 400 | Поле не пройшло валідацію або було передано невідоме поле |
4002 | 403 | Провайдер недоступний для вашого акаунта |
4003 | 400 | Некоректний ідентифікатор замовлення |
4004 | 400 | Провайдер не підтримує запитану мережу |
4010 | 401 | API-ключ відсутній |
4011 | 401 | API-ключ або IP-адресу не прийнято |
4040 | 404 | Замовлення не знайдено |
4041 | 404 | Акаунт не знайдено |
4090 | 409 | Запит із цим ключем ідемпотентності ще обробляється |
4091 | 409 | Дублікат запиту |
4093 | 409 | Цей ключ ідемпотентності вже використовувався з іншим тілом запиту |
1004 | 403 | Недостатньо коштів на балансі |
5000 | 500 | Внутрішня помилка |
5001 | 500 | Не вдалося виконати списання коштів |
5002 | 500 | Замовлення не було створено |
5030 | 503 | Провайдер недоступний |
Помилки, які не використовують цей формат
Деякі помилки трапляються на рівні шлюзу (gateway), до того, як запит досягне застосунку, і вони зберігають власний формат шлюзу. Вважайте будь-яку відповідь, чий Content-Type відрізняється від application/problem+json, однією з таких:
| Ситуація | HTTP | Тіло відповіді |
|---|---|---|
| API-ключ відсутній або недійсний | 401 | {"detail":{"code":-1,"msg":"Invalid or missing API key"}} |
| Перевищено ліміт запитів | 429 | {"message":"API rate limit exceeded"} |
| Невідомий шлях або метод, який не підтримується маршрутом | 404 / 405 | {"detail":"Method Not Allowed"} |
Ліміти частоти запитів
Ліміт є спільним із POST /apiv2/aml та іншими AML-маршрутами: 5 запитів на секунду та 150 на хвилину. Перехід на цей ендпоінт не надає окремого ліміту.
Примітки
- Ціноутворення: Elliptic $0.98, BitOK $0.50 за перевірку, списується з балансу в TRX за курсом на момент списання.
- Час обробки: більшість перевірок завершується за кілька секунд; перевірка адреси з великою історією може тривати до трьох хвилин. Використовуйте асинхронний режим і перевіряйте результат за допомогою GET /apiv2/screening/{client_order_id}.
- Неактивні адреси повертають статус
skippedі не тарифікуються. - Оригінальна відповідь провайдера ніколи не повертається напряму.
provider_dataє адаптованою вибіркою; поля, що стосуються нашого облікового запису в системі провайдера, а не перевіреної адреси, нікому не розкриваються.
Звіти
Ендпоінт повертає виключно JSON. Формати PDF та Markdown не надаються.
Усе, з чого складається звіт, уже присутнє у відповіді: уніфікований блок та provider_data. Рендеринг даних на вашому боці дає змогу отримати документ у потрібному вам вигляді — з вашим брендингом, мовою та оформленням — що особливо актуально, якщо ви перепродаєте послуги перевірки, оскільки звіт з нашим логотипом не підходить для надання вашим кінцевим клієнтам.
Якщо вам потрібен звіт як підтвердження для третіх сторін — банку, регулятора або контрагента — зверніть увагу, що непідписаний PDF-файл не є юридичним доказом незалежно від того, хто його згенерував: його можна змінити в текстовому редакторі за хвилину. Документ, що має юридичну силу, потребує електронного підпису або публічної сторінки верифікації, а це окремий функціонал. Якщо у вас саме така потреба, повідомте нам вимоги вашого контрагента.
Готові звіти у форматі PDF для цих самих перевірок доступні в панелі керування Netts сімнадцятьма мовами.
Див. також
- GET /apiv2/screening/{client_order_id} — перегляд перевірки
- GET /apiv2/screening/history — ваші перевірки з курсорною пагінацією
- GET /apiv2/screening/providers — провайдери, ціни, мережі, шкали
- GET /apiv2/screening/price — ціна конкретного провайдера
- Sanctions in an AML result — значення поля
sanctionsі значення прапорця провайдера