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

Solicite uma triagem de AML para um endereço de blockchain. Este é o contrato da versão 2: um único formato de resposta para cada provedor e cada estado da ordem, números decimais como strings e um formato de erro unificado.

Ele substitui o POST /apiv2/aml, que continua funcionando e não será descontinuado sem aviso prévio.

URL do endpoint

POST https://netts.io/apiv2/screening

Cabeçalhos da requisição

HeaderRequiredDescription
Content-TypeSimapplication/json
X-API-KEYSimSua chave de API do painel da Netts
X-Idempotency-KeyNãoSua própria chave para repetições seguras. Veja Idempotência

Corpo da requisição

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

Parâmetros

ParameterTypeRequiredDescription
addressstringSimEndereço a ser analisado, de 10 a 128 caracteres
networkstringSimTicker da rede. Os tickers cobertos por cada provedor são listados por GET /apiv2/screening/providers; a tabela completa de redes com seus nomes está aqui
providerstringSimelliptic ou bitok. Não há valor padrão
wait_for_resultbooleanNãotrue aguarda o resultado por até 15 segundos. Padrão false
languagestringNãoIdioma do relatório. Apenas en

Campos desconhecidos são rejeitados. Um corpo que contenha um campo não presente na tabela acima retorna 400 com o código 4001. Na versão 1, campos desconhecidos eram ignorados silenciosamente, e um erro de digitação em wait fazia com que o chamador esperasse por um resultado que nunca chegaria de forma síncrona.

provider é obrigatório e não possui valor padrão. Na versão 1, um provedor omitido significava Elliptic, de modo que um chamador que não escolhesse acabava pagando por um provedor que nunca especificou.

provider é uma string de formato livre no esquema, não uma enumeração. Hoje, dois valores são aceitos; a adição de um terceiro provedor não deve ser uma alteração drástica (breaking change) para qualquer pessoa que valide as respostas contra o esquema. A lista atual, as redes que cada provedor cobre e a escala em que cada um pontua vêm de GET /apiv2/screening/providers.

Examples de Requisições

cURL — aguardar pelo resultado

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 — aceitar e consultar status (polling)

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"
  }'

A resposta é 202 Accepted com um cabeçalho Location apontando para a ordem.

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"])

Códigos de Resposta

SituaçãoCódigoHeaders
Ordem criada, triagem em execução em segundo plano202 AcceptedLocation: /apiv2/screening/{client_order_id}
O resultado está na resposta (wait_for_result)200 OK
Resultado reutilizado de uma checagem recente, nada cobrado200 OK
O endereço não possui atividade na blockchain, nada cobrado200 OK
Erroconsulte ErrosContent-Type: application/problem+json

Respostas que contêm um resultado de triagem são enviadas com Cache-Control: private, no-store.

Resposta

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": { }
}

O conjunto de campos nunca muda

Cada bloco listado acima está presente em todas as respostas, independentemente do provedor e do estado da ordem. O que um provedor não fornece fica como null; uma lista vazia é [], não null; um bloco que ainda não possui dados é preenchido com valores nulos em vez de ser omitido. Um único parser lida com uma checagem recém-aceita e com a mesma checagem após sua conclusão.

Duas consequências para o seu código:

  • ignore campos que você não conhece. Novos campos são adicionados a estes blocos sem uma nova versão. Rejeitar um campo desconhecido é um bug do seu lado, não nosso;
  • provider_data não faz parte do contrato. Sua estrutura acompanha o provedor e muda quando o provedor muda. Tudo o que o contrato garante está nos blocos acima.

order

FieldTypeDescription
client_order_idstringIdentificador da ordem, usado para consultar o resultado posteriormente
statusstringpending, processing, completed, skipped, failed
api_versionstringContrato que criou a ordem
cache_hitbooleantrue quando um resultado recente foi reutilizado e nada foi cobrado
created_atstringRFC 3339, UTC, microssegundos
started_atstring | nullQuando a chamada ao provedor foi iniciada. null para skipped
completed_atstring | nullQuando o resultado chegou
errorstringApenas para failed: motivo da falha
reasonstringApenas para skipped: address_inactive

Todos os timestamps estão em UTC, RFC 3339, com sufixo Z e precisão de microssegundos.

billing

FieldTypeDescription
chargedbooleanSe houve cobrança de valores
price_usdtstringPreço de tabela do provedor em USDT
base_amountstringPreço da ordem na moeda cobrada, sem margem de subusuário
markup_amountstringMargem de subusuário. "0" para uma conta direta
charged_amountstringValor efetivamente debitado do saldo
charged_currencystringTRX
exchange_ratestring | nullTaxa utilizada para a conversão
payment_statusstringpaid, pending, failed, not_charged

payment_status fica como pending por um breve período após uma checagem bem-sucedida: a cobrança é retida primeiro e liquidada dentro de uma hora. failed significa que o valor foi estornado. not_charged significa que nenhuma cobrança foi gerada — um resultado reutilizado ou um endereço ignorado.

precheck

Antes de uma triagem paga, o endereço é verificado quanto a atividade na blockchain. Um endereço sem atividade não é enviado ao provedor e não é cobrado.

FieldTypeDescription
activity_checkedbooleanSe a verificação foi executada. false em redes onde ela não existe
activity_statusstringactive, inactive, unknown
sourcestring | nullNome do mecanismo

unknown não impede a triagem paga: se o serviço de verificação de atividade estiver indisponível, o endereço é tratado como ativo.

check

FieldTypeDescription
providerstringProvedor que realizou a checagem
provider_check_idstring | nullO identificador próprio do provedor — mencione-o ao contestar um resultado com eles
checked_atstring | nullQuando o provedor produziu o resultado
statusstringVeja a tabela abaixo
provider_statusstring | nullA redação original do próprio provedor, sem alterações
order.statuscheck.statusMeaning
pendingpendingOrdem aceita, ainda não iniciada
processingrunningO provedor está processando
completedcompletedResultado recebido
failedfailedRecusado antes ou durante a chamada ao provedor
skippednot_performedO endereço não possui atividade; o provedor nunca foi chamado e nada foi cobrado

risk

FieldTypeDescription
scorestring | nullA pontuação original do provedor, como uma string decimal
scaleobjectmin e max da escala daquele provedor
levelstringnone, low, medium, high, severe
level_sourcestringcomputed — derivamos o nível a partir da pontuação; provider — o provedor informou diretamente
provider_levelstring | nullO termo utilizado pelo próprio provedor, quando ele o retorna
policystringNome da política de limites, netts-risk-v1
by_directionobjectPontuação dividida em source e destination, quando o provedor a divide

A pontuação nunca é recalculada em outra escala. A Elliptic opera de 0 a 10 e a BitOK opera de 0 a 1, e 7 em uma escala não equivale a 0.7 na outra em nenhum sentido prático. A escala vem na resposta para que uma integração desenvolvida para um provedor não interprete erroneamente outro após uma simples alteração de configuração.

O nível utiliza um vocabulário único em toda a API. Onde o provedor define um nível próprio, nós o repassamos e indicamos isso em level_source; onde não define, derivamos o nível a partir da pontuação com os limites de netts-risk-v1 e indicamos essa condição. A mesma palavra aparece na resposta da API, no painel e no relatório em PDF para a mesma checagem.

exposure[], rules[], entities[]

exposure[] detalha os fundos por categoria de contraparte. rules[] lista as regras do provedor que foram acionadas. entities[] lista as entidades às quais o próprio endereço pertence; primary_entity seleciona uma delas seguindo uma regra fixa — a entidade que o provedor marcou como primária, caso contrário a primeira, caso contrário null. sanctioned_entities[] contém as entidades de entities[] sinalizadas como ativas após a data de sanção.

Proporções são frações, nunca porcentagens

Cada proporção na resposta é um campo único, share_fraction, uma string decimal entre "0" e "1".

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

Os provedores divergem nas unidades: o mesmo terço de exposição chega como 31.57 de um e 0.3157 do outro. Um único campo comportando ambos seria impossível de interpretar sem conhecer o provedor. O valor original do provedor em suas próprias unidades permanece em provider_data.

Números são strings

Todos os números fornecidos por um provedor — pontuações, proporções, volumes em USD e todos os valores em billing — são strings decimais.

json
"score": "0.9634087310611608"

Fazer o parse disso como um número JSON em JavaScript, Go ou qualquer outra linguagem com ponto flutuante binário resulta em uma aproximação, e o valor exibido deixa de corresponder ao valor emitido pelo provedor. Faça o parse desses campos com um tipo decimal: Decimal em Python, BigDecimal em Java, decimal.Decimal ou string em JavaScript.

Campos que são nossos e não do provedor — scale.min, scale.max, hops — são números JSON comuns.

Reutilizando um resultado recente

Quando você analisa o mesmo endereço, rede e provedor novamente dentro de 60 segundos, o resultado anterior é retornado e nada é cobrado.

Cada requisição ainda cria sua própria ordem com seu respectivo client_order_id; a ordem reutilizada é marcada com "cache_hit": true e seu bloco billing informa "charged": false com "payment_status": "not_charged". O identificador da ordem de onde o resultado se originou não é divulgado — ele pode pertencer a outra conta.

A reutilização só ocorre dentro de uma mesma conta. Um resultado analisado por outra pessoa nunca será retornado para você.

Idempotency

Envie X-Idempotency-Key com um valor próprio para tornar uma nova tentativa segura: a mesma chave com o mesmo corpo retorna a resposta armazenada em vez de solicitar uma segunda checagem.

SituaçãoCódigoResponse
A primeira requisição com esta chave ainda está em execução4094090
A mesma chave, um corpo de requisição diferente4094093
A mesma chave, o mesmo corpo, já finalizadao código armazenadoa resposta armazenada

Se você não enviar o cabeçalho, uma chave será gerada automaticamente para você a partir da chave de API, do endereço, do provedor e do seu endereço IP, em uma janela de dois segundos. Isso protege contra cliques duplos e repetições de gateway, mas não contra uma repetição um minuto depois: esta última será uma ordem nova autêntica e será cobrada.

As chaves têm escopo definido por endpoint. O mesmo valor enviado para POST /apiv2/aml e para este endpoint representa duas garantias independentes sobre duas requisições diferentes — os corpos diferem, assim como as respostas. Reutilizar sua chave durante a migração de uma integração da versão 1 para a versão 2 é seguro: isso não retorna uma resposta da versão 1 nem é considerado como a mesma chave usada com um corpo diferente.

Respostas de erro

Todo erro gerado pela aplicação utiliza o padrão RFC 9457 com 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 e instance são os campos padrão. O code numérico é mantido como uma extensão para que integrações desenvolvidas para a versão 1 possam continuar correspondendo por ele. Campos adicionais dependem do erro e devem ser ignorados caso você não os conheça.

CódigoHTTPMeaning
4000400O corpo não é um JSON válido
4001400Um campo falhou na validação ou um campo desconhecido foi enviado
4002403O provedor não está disponível para a sua conta
4003400Identificador de ordem malformado
4004400O provedor não suporta a rede solicitada
4010401Chave de API ausente
4011401Chave de API ou endereço IP não aceitos
4040404Ordem não encontrada
4041404Conta não encontrada
4090409Uma requisição com esta chave de idempotência ainda está em execução
4091409Requisição duplicada
4093409Esta chave de idempotência foi usada com um corpo diferente
1004403Saldo insuficiente
5000500Erro interno
5001500A cobrança não foi processada
5002500A ordem não foi criada
5030503O provedor está indisponível

Erros que não usam este formato

Algumas falhas ocorrem no gateway, antes de atingir a aplicação, e mantêm o formato próprio do gateway. Trate qualquer resposta cujo Content-Type não seja application/problem+json como uma destas:

SituaçãoHTTPBody
Chave de API ausente ou não aceita401{"detail":{"code":-1,"msg":"Invalid or missing API key"}}
Limite de taxa excedido429{"message":"API rate limit exceeded"}
Caminho desconhecido ou método não atendido pela rota404 / 405{"detail":"Method Not Allowed"}

Limites de taxa

O limite é compartilhado com o POST /apiv2/aml e as demais rotas de AML: 5 requisições por segundo e 150 por minuto. A migração para este endpoint não concede uma cota adicional.

Notas

  • Preços: Elliptic $0.98, BitOK $0.50 por checagem, cobrados do saldo em TRX com base na taxa no momento da cobrança.
  • Tempo de processamento: a maioria das checagens termina em poucos segundos; um endereço com longo histórico pode levar até três minutos. Utilize o modo assíncrono e consulte o resultado com GET /apiv2/screening/{client_order_id}.
  • Endereços inativos retornam skipped e não são cobrados.
  • A resposta bruta do provedor nunca é retornada. provider_data é uma projeção revisada; campos pertencentes à nossa conta com o provedor, e não ao endereço analisado, não são divulgados a ninguém.

Relatórios

O endpoint retorna JSON e nada mais. Não há PDF e não há Markdown.

Tudo o que compõe um relatório já está presente na resposta: o bloco unificado e provider_data. Renderizá-lo do seu lado permite gerar o documento que você realmente deseja — sua marca, seu idioma, seu layout — o que é particularmente relevante se você revende checagens, pois um relatório com o nosso nome não é o documento correto para entregar ao seu próprio cliente.

Se você precisa de um relatório como comprovante para terceiros — um banco, um regulador, uma contraparte — observe que um PDF sem assinatura digital não serve como prova, independentemente de quem o gere: ele pode ser editado em um editor de texto em um minuto. Um artefato verificável requer uma assinatura ou uma página pública de validação, e essa é uma funcionalidade diferente. Se este for o seu caso, informe-nos sobre as exigências da sua contraparte.

Relatórios em PDF legíveis por humanos existem para as mesmas checagens no painel da Netts, em dezessete idiomas.

Consulte também