Skip to content
This translation is behind the English original, updated 2026-09-15. Read the English version for the current text.

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НетВаш собственный ключ для безопасных повторных попыток. См. Идемпотентность

Тело запроса

json
{
    "address": "YOUR_ADDRESS_HERE",
    "network": "trx",
    "provider": "elliptic",
    "wait_for_result": true
}

Параметры

ПараметрТипОбязательныйОписание
addressstringДаАдрес для проверки, от 10 до 128 символов
networkstringДаТикер сети. Тикеры, поддерживаемые каждым провайдером, возвращаются методом GET /apiv2/screening/providers; полная таблица сетей с их названиями находится здесь
providerstringДаelliptic или bitok. Значения по умолчанию нет
wait_for_resultbooleanНетtrue ожидает результат до 15 секунд. По умолчанию false
languagestringНетЯзык отчета. Только en

Неизвестные поля отклоняются. Тело запроса, содержащее поле, отсутствующее в таблице выше, возвращает 400 с кодом 4001. В версии 1 неизвестные поля молча игнорировались, и опечатка в wait приводила к тому, что вызывающая сторона ждала результат, который ни при каких условиях не вернулся бы синхронно.

Параметр provider обязателен и не имеет значения по умолчанию. В версии 1 пропущенный провайдер означал Elliptic, из-за чего клиент, не сделавший выбор, платил за провайдера, которого он явно не указывал.

provider в схеме является произвольной строкой, а не перечислением (enum). На сегодняшний день принимаются два значения; добавление третьего провайдера не должно ломать интеграцию у тех, кто валидирует ответы по схеме. Актуальный список, поддерживаемые каждым провайдером сети и шкалы их оценок возвращаются методом GET /apiv2/screening/providers.

Примеры запросов

cURL — ожидание результата

bash
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 — принятие в обработку и последующий опрос

bash
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

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 AcceptedLocation: /apiv2/screening/{client_order_id}
Результат возвращен в ответе (wait_for_result)200 OK
Результат переиспользован из недавней проверки, списания не было200 OK
У адреса нет активности в блокчейне, списания не было200 OK
Ошибкасм. ОшибкиContent-Type: application/problem+json

Ответы, содержащие результат проверки, отправляются с заголовком Cache-Control: private, no-store.

Ответ

json
{
  "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_idstringИдентификатор заказа, используется для получения результата позже
statusstringpending, processing, completed, skipped, failed
api_versionstringВерсия контракта, создавшего заказ
cache_hitbooleantrue, если был повторно использован недавний результат и списания средств не было
created_atstringRFC 3339, UTC, микросекунды
started_atstring | nullВремя начала вызова провайдера. null для skipped
completed_atstring | nullВремя получения результата
errorstringТолько для failed: причина сбоя
reasonstringТолько для skipped: address_inactive

Все временные метки представлены в UTC, по стандарту RFC 3339, с суффиксом Z и точностью до микросекунд.

billing

ПолеТипОписание
chargedbooleanБыло ли списание средств
price_usdtstringБазовая цена провайдера в USDT
base_amountstringСтоимость заказа в валюте списания, без наценки субаккаунта
markup_amountstringНаценка субаккаунта. "0" для прямого аккаунта
charged_amountstringСумма, фактически списанная с баланса
charged_currencystringTRX
exchange_ratestring | nullКурс, использованный для конвертации
payment_statusstringpaid, pending, failed, not_charged

payment_status имеет значение pending в течение короткого времени после успешной проверки: списание сначала холдируется и подтверждается в течение часа. failed означает, что средства были возвращены. not_charged означает, что списание даже не создавалось — при повторном использовании результата или пропуске адреса.

precheck

Перед платной проверкой адрес проверяется на наличие активности в блокчейне. Адрес без активности не отправляется провайдеру, и средства не списываются.

ПолеТипОписание
activity_checkedbooleanВыполнялась ли проверка. false для сетей, где она отсутствует
activity_statusstringactive, inactive, unknown
sourcestring | nullНазвание механизма

Значение unknown не останавливает платную проверку: если сервис проверки активности недоступен, адрес считается активным.

check

ПолеТипОписание
providerstringПровайдер, выполнивший проверку
provider_check_idstring | nullСобственный идентификатор провайдера — укажите его при оспаривании результата с ним
checked_atstring | nullВремя, когда провайдер сформировал результат
statusstringСм. таблицу ниже
provider_statusstring | nullИсходный статус от провайдера, без изменений
order.statuscheck.statusЗначение
pendingpendingЗаказ принят, еще не запущен
processingrunningПровайдер выполняет обработку
completedcompletedРезультат получен
failedfailedОтклонено до или во время вызова провайдера
skippednot_performedУ адреса нет активности; обращение к провайдеру не выполнялось, списания не было

risk

ПолеТипОписание
scorestring | nullСобственная оценка провайдера в виде десятичной строки
scaleobjectmin и max для шкалы этого провайдера
levelstringnone, low, medium, high, severe
level_sourcestringcomputed — уровень рассчитан нами на основе оценки; provider — уровень указан провайдером
provider_levelstring | nullИсходное обозначение уровня от провайдера, если оно возвращается
policystringНазвание политики пороговых значений, netts-risk-v1
by_directionobjectОценка с разделением на 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".

text
Elliptic reports 31.574732212596924 %  ->  "share_fraction": "0.31574732212596924"
BitOK reports    0.8488                ->  "share_fraction": "0.8488"

У провайдеров различаются единицы измерения: одна и та же треть связи приходит как 31.57 от одного и как 0.3157 от другого. Единое поле, содержащее оба варианта, было бы невозможно корректно разобрать без знания конкретного провайдера. Исходное число провайдера в его собственных единицах измерения сохраняется в provider_data.

Числа передаются строками

Каждое число, полученное от провайдера — оценки, доли, объемы в USD и каждая сумма в блоке billing — передается в виде десятичной строки.

json
"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". Идентификатор заказа, из которого был взят результат, не раскрывается — он может принадлежать другому аккаунту.

Повторное использование действует только в рамках одного аккаунта. Результат проверки, выполненной кем-то другим, вам никогда не вернется.

Идемпотентность

Передавайте заголовок X-Idempotency-Key с собственным значением для безопасных повторных попыток: тот же ключ с тем же телом запроса возвращает сохраненный ответ вместо создания второго заказа на проверку.

СитуацияКодОтвет
Первый запрос с этим ключом еще выполняется4094090
Тот же ключ, но другое тело запроса4094093
Тот же ключ, то же тело запроса, уже завершенсохраненный кодсохраненный ответ

Если заголовок не передан, ключ генерируется автоматически на основе API-ключа, адреса, провайдера и вашего IP-адреса в рамках двухсекундного окна. Это защищает от двойного клика и повторных запросов шлюза, но не от повторного запроса через минуту: такой запрос будет считаться новым полноценным заказом и будет тарифицирован.

Область действия ключей ограничена конкретным эндпоинтом. Одно и то же значение, отправленное в POST /apiv2/aml и в этот эндпоинт — это два независимых условия для двух разных запросов: тела различаются, как и ответы. Повторное использование вашего ключа при переводе интеграции с версии 1 на версию 2 безопасно: оно не вернет ответ версии 1 и не будет расценено как использование того же ключа с другим телом запроса.

Ошибки

Все ошибки приложения формируются по стандарту RFC 9457 с заголовком Content-Type: application/problem+json:

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Значение
4000400Тело запроса не является валидным JSON
4001400Поле не прошло валидацию или передано неизвестное поле
4002403Провайдер недоступен для вашего аккаунта
4003400Некорректный формат идентификатора заказа
4004400Провайдер не поддерживает запрошенную сеть
4010401API-ключ не передан
4011401API-ключ или IP-адрес не приняты
4040404Заказ не найден
4041404Аккаунт не найден
4090409Запрос с этим ключом идемпотентности все еще выполняется
4091409Дублирующийся запрос
4093409Этот ключ идемпотентности был использован с другим телом запроса
1004403Недостаточно средств на балансе
5000500Внутренняя ошибка
5001500Не удалось провести списание
5002500Заказ не был создан
5030503Провайдер недоступен

Ошибки другого формата

Некоторые сбои происходят на уровне шлюза до того, как запрос достигнет приложения, и они сохраняют исходную структуру шлюза. Любой ответ, у которого 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 не имеет юридической силы, кто бы его ни создал: его можно отредактировать в текстовом редакторе за минуту. Для проверяемого документа требуется цифровая подпись или публичная страница верификации, а это отдельная функциональность. Если вам необходимо такое решение, сообщите нам о требованиях вашего контрагента.

В панели управления Netts доступны человекочитаемые PDF-отчеты по тем же самым проверкам на семнадцати языках.

См. также