---
title: "Did this wallet pay a specific venue, and how much"
method: POST
path: "/v1/entity-transfers"
tags: ["Graph"]
---

# Did this wallet pay a specific venue, and how much

`POST /v1/entity-transfers`

## Request

- Base URL: `https://intelapi.publicaml.org`
- URL: `https://intelapi.publicaml.org/v1/entity-transfers`
- Auth: one of:
  - API key in header `X-API-Key`
  - none

Answers one question well: 'has this wallet sent money to <venue>'. Returns the verdict over the whole window (`found`), per-asset totals, and the individual transfers.

**A `found: false` is only conclusive when you passed `since`/`until`.** Without a window the counterparty list comes from an index that can lag, and the response says so in `scan.negative_conclusive`. Read that field before reporting a wallet as clean of a venue.

`entity` matches EXACTLY after lowercasing, stripping punctuation and stripping a trailing parenthetical: '1xbet' matches '1xBet' and '1xBet (deposit)'; 'satoshi' does NOT match 'Satoshibet'.

## Request body

- object
  - `address` string, required: The wallet under investigation. The address to look up, in its normal display form. * bitcoin — base58 (`1…`, `3…`) or bech32/bech32m (`bc1…`, `bc1p…`). Case is SIGNIFICANT for base58; do not lowercase it. A 64-character lowercase TRANSACTION HASH is also accepted here: on bitcoin the graph is bipartite (address → transaction → address), so a transaction is a node with its own neighbours and its own findings. CoinJoin is classified on the transaction, not on the participants, and this is how you read it. * ethereum, bsc — `0x` + 40 hex characters, any case. * tron — base58 (`T…`, 34 characters); the `0x` 20-byte form is accepted too and is what the API stores and echoes.
  - `chain` 'bitcoin' | 'ethereum' | 'bsc' | 'tron', required: Which blockchain the address belongs to. Send one of the four values in the enum. The short forms used by the older clients (ETH, BTC, TRX, BNB) and any capitalisation are still accepted, but responses always echo the canonical spelling. Getting this wrong is the single commonest integration bug: the same 0x-prefixed address exists on ethereum, bsc and tron and means a different wallet on each, so the chain is part of the identity, never a hint. If it does not match the address format the call fails with `invalid_address_format`, which names the chain the address does look like.
  - `entity` string: Venue name. Exact match — see the endpoint description.
  - `category` string: Entity type: gambling, cex, mixer, … One of `entity`/`category` is required.
  - `direction` 'out' | 'in' | 'both': 'out' = money leaving the wallet.
  - `min_amount` number: Inclusive bound PER TRANSFER, in the asset's own units (1 = 1 BTC, 1 USDT). Not a threshold on the total.
  - `max_amount` number
  - `since` union: Epoch seconds or ISO-8601. Passing a window is what makes a negative answer conclusive.
    - integer
    - string
  - `until` union
    - integer
    - string
  - `token` string: EVM only; rejected on bitcoin.
  - `limit` integer
  - `offset` integer

## Response `200`

The verdict, the totals and the transfers.

- object
  - `found` boolean: The verdict over the whole window, not just this page.
  - `entity` object: How the name resolved: `matched_names` are the canonical spellings hit, `suggestions` appear when it resolved to nothing.
  - `totals` object[]: Per-asset sums over the transfers that passed the filters.
    - `symbol` string, nullable
    - `amount` number
    - `amount_raw` string: Stays a string: a float cannot hold 18-decimal token units.
    - `transfer_count` integer
    - `tx_count` integer
  - `transfers_below_min` integer: Non-zero next to found: false means the wallet DID pay, outside the bounds you asked for.
  - `transfers_above_max` integer
  - `matched_counterparties` object[]
    - `address` string
    - `label` string, nullable: Display name of the entity, e.g. 'Binance 14'.
    - `category` string, nullable: Entity type: cex, dex, mixer, gambling, bridge, payment, …
    - `source` string, nullable: The feed behind the claim, named as it is stored — e.g. `ofac:sdn_advanced`, `arkham:sanctioned`, `chainabuse:chainabuse`. Every source is named: address answers withheld non-public feeds behind the literal `publicaml` until 2026-09-11, and no longer do. How much the claim is worth is `source_confidence`, published alongside. A null means the stored claim never carried a source at all — not that one is being hidden.
    - `source_confidence` number, nullable: How much we trust whoever made this claim, 0..1. Derived from the source's tier: an official list answers 1.0, a vendor blocklist 0.5, a community report 0.25. Absent when no tier prices the source — that is a gap in our policy table, not a statement of trust, so it is not reported as confidence.
    - `confidence` number, nullable: The source's OWN stated certainty, untouched. Not the same as source_confidence, which is ours about the source.
    - `risk_score` number
    - `sanctioned` boolean
    - `sanction` object
      - `label` string, nullable
      - `source` string, nullable
  - `transfers` object[]
  - `has_more` boolean
  - `scan` object: How the answer was reached — read before trusting a negative.
    - `basis` 'edges_window' | 'counterparty_index': 'edges_window' = the whole window was read. 'counterparty_index' = no window given, so the list came from an index that can lag.
    - `negative_conclusive` boolean: false means 'nothing known', NOT 'nothing happened'.
    - `truncated` boolean

## Other responses

- `400`: Malformed request. A numeric parameter that is not a number answers `invalid_parameters`: `invalid[]` names every bad field with the reason and the accepted range, and `limits` lists the ranges of all numeric fields of this endpoint. A number OUTSIDE its range is not an error: it is pinned to the range and named in `clamped` (where the response has one).
- `429`: One of two things, told apart by `error`: * `rate limit exceeded` — your hourly quota (anonymous callers, and keys issued with a quota of their own). Wait `retry_after` seconds; authenticating removes the limit. * `server_busy` — this endpoint is a graph walk or a range scan, and the server is already running as many of those at once as it admits. Nothing was charged for the work. Retry the SAME request after `Retry-After` seconds (a few); do not split it into smaller calls, that multiplies the load that caused the refusal.
- `500`: Server error.

---

[API](https://skmtc.dev/publicaml/apis/publicaml-intelligence-api.md) · [All operations](https://skmtc.dev/publicaml/apis/publicaml-intelligence-api/llms.txt) · [OpenAPI document](https://skmtc.dev/publicaml/apis/publicaml-intelligence-api/revisions/7c0c6315088c?raw)
