---
title: "Follow value through a DEX or aggregator"
method: POST
path: "/v1/swap-through"
tags: ["Screening"]
---

# Follow value through a DEX or aggregator

`POST /v1/swap-through`

## Request

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

Net balance change per address and asset inside one transaction, with the beneficiaries of any swap flagged.

This is how a trace continues through a decentralised exchange without decoding the exchange. An address that ends the transaction having sent one asset and received a DIFFERENT one is the party the swap was for, whatever router or aggregator sat in between, so the walk resumes from it. Pools, wrapper contracts and relayers fall out of the same arithmetic without being named anywhere.

`delta_raw` is a signed integer in the asset's smallest unit, as a string. `asset` is null for the chain's native coin, which covers both plain transfers and internal calls.

Ethereum and BSC only. Some chains need `block_number` alongside the hash to resolve it at all; the response says so with a 422.

## Request body

- object
  - `tx_hash` string, required: 0x-prefixed EVM transaction hash.
  - `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.
  - `block_number` integer: Required on chains that cannot resolve a bare hash — see the 422.

## Response `200`

Net deltas for the transaction.

- object
  - `tx_hash` string
  - `chain` string
  - `found` boolean
  - `block_number` integer
  - `edge_count` integer
  - `participant_count` integer
  - `swap_beneficiaries` string[]: Addresses that sent one asset and received another.
  - `participants` object[]: Each carries address, swap_beneficiary, assets_sent, assets_received and deltas[] of {asset, symbol, decimals, delta_raw}.
  - `warnings` string[]: Present when the edge set hit its cap and the deltas are partial.

## 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).
- `404`: Transaction not found in the indexed data.
- `422`: block_number is required for this chain.
- `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)
