> For the complete documentation index, see [llms.txt](https://docs.swapkit.dev/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.swapkit.dev/swapkit-api/track-request-the-status-of-a-swap.md).

# /track - Request the status of a swap

The `/track` endpoint provides real-time status information for a specific transaction. It is particularly useful for tracking the progress and details of swaps, transfers, and other operations. To use this endpoint, you normally provide the **chain ID** and **transaction hash**.

For NEAR Intents swaps, you can also call the endpoint with **depositAddress**, which is the address the deposit transaction was sent to.

For deposit-channel routes you can also call it with **depositChannelId** — the `meta.providerDepositChannelId` returned by [`/v3/swap`](/swapkit-api/v3-swap-obtain-swap-transaction-details.md). This is the way to track a Chainflip swap whose deposit was broadcast outside your app, where you never see a transaction hash.\
\
For a complete list of chain IDs used by SwapKit you can [check the table here](/swapkit-api/providers-providers-status-and-identifiers-mapping.md#chain-ids-and-corresponding-names).

**Method:** `POST`\
**URL:** `https://api.swapkit.dev/track`

SwapKit has it's own transaction tracking interface here: <https://track.swapkit.dev/>. You can also directly fill the fields by directing users to `https://track.swapkit.dev/?hash={{hash}}` by populating it with the respective transaction hash.

#### Request Body:

<table><thead><tr><th>Field</th><th width="172">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>hash</code></td><td><code>string</code></td><td>Transaction hash (required if using <code>chainId</code>)</td></tr><tr><td><code>chainId</code></td><td><code>string</code></td><td>Chain ID of the transaction (required if using <code>hash</code>)</td></tr><tr><td><code>depositChannelId</code></td><td><code>string</code></td><td>Deposit channel ID, can be used instead of <code>hash</code> and <code>chainId</code>. Use the <code>meta.providerDepositChannelId</code> returned by <code>/v3/swap</code>. Needed for Chainflip when the deposit was broadcast without a wallet connection, so no hash is available.</td></tr><tr><td><code>depositAddress</code></td><td><code>string</code></td><td>Deposit address used to swap with NEAR Intents, can be used instead of <code>hash</code> and <code>chainId</code></td></tr><tr><td><code>block</code></td><td><code>number</code></td><td>Block number. Required for Polkadot.</td></tr><tr><td><code>routeId</code></td><td><code>string</code></td><td>The <code>routeId</code> of the <code>/v3/quote</code> route this transaction executed. Pass it on <code>/track</code> calls until the response contains a parsed transaction, to enable <a href="#realized-slippage">realized-slippage</a> reporting on completion. Best-effort: quote data expires. Send it on the first call, right after broadcast.</td></tr></tbody></table>

Provide exactly one of: hash + chainId, depositChannelId, or depositAddress. `routeId` is not one of those identifiers — it is additive, and is passed alongside whichever one you use.

#### Example Requests:

<details>

<summary><strong><code>hash</code> and <code>chainId</code></strong></summary>

```bash
curl -X 'POST' \
  'https://api.swapkit.dev/track' \
  -H 'Content-Type: application/json' \
  -H "x-api-key: YOUR_API_KEY_HERE" \
  -d '{
  "hash": "0x1890aba1c0b25126892af2ab09f5c1bba75adefc47918a96ea498764ab643ce9",
  "chainId": "1",
  "routeId": "eed91159-86bd-4674-9558-48f7e4f8bac0"
}'
```

</details>

<details>

<summary><strong><code>depositAddress</code></strong></summary>

```bash
curl -X 'POST' \
  'https://api.swapkit.dev/track' \
  -H 'Content-Type: application/json' \
  -H "x-api-key: YOUR_VARIABLE_HERE" \
  -d '{
  "depositAddress": "0x6f2B69c522031A7640b432172Ac84e57Dc0a3A63"
}'
```

</details>

<details>

<summary><strong><code>depositChannelId</code></strong></summary>

```bash
curl -X 'POST' \
  'https://api.swapkit.dev/track' \
  -H 'Content-Type: application/json' \
  -H "x-api-key: YOUR_API_KEY_HERE" \
  -d '{
  "depositChannelId": "1234567-Bitcoin-89"
}'
```

</details>

***

### Response

The response contains detailed information about the transaction status, type, and associated metadata. It also includes the array `"legs"` which represent the different stages or components of the transaction.

#### Response Fields:

<table><thead><tr><th width="219">Field</th><th width="119">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>chainId</code></td><td><code>string</code></td><td>The chain ID where the transaction occurred.</td></tr><tr><td><code>hash</code></td><td><code>string</code></td><td>The transaction hash.</td></tr><tr><td><code>block</code></td><td><code>number</code></td><td>The block number where the transaction was included.</td></tr><tr><td><code>type</code></td><td><code>string</code></td><td>The type of the transaction (e.g., <code>swap</code>, <code>token_transfer</code>).</td></tr><tr><td><code>status</code></td><td><code>string</code></td><td>The transaction status (e.g., <code>completed</code>).<br>See <a href="#transaction-status">below</a> for more details.</td></tr><tr><td><code>trackingStatus</code></td><td><code>string</code></td><td>The finer-grained phase of the transaction. Optional. See <a href="#tracking-status">Tracking status</a>.</td></tr><tr><td><code>fromAsset</code></td><td><code>string</code></td><td>The asset being sent.</td></tr><tr><td><code>fromAmount</code></td><td><code>string</code></td><td>The amount of <code>fromAsset</code> that was actually deposited.</td></tr><tr><td><code>fromAddress</code></td><td><code>string</code></td><td>The address sending the asset.</td></tr><tr><td><code>toAsset</code></td><td><code>string</code></td><td>The asset being received.</td></tr><tr><td><code>toAmount</code></td><td><code>string</code></td><td>The amount of <code>toAsset</code>.</td></tr><tr><td><code>toAddress</code></td><td><code>string</code></td><td>The recipient address.</td></tr><tr><td><code>finalisedAt</code></td><td><code>number</code></td><td>UNIX timestamp indicating when the transaction finalized.</td></tr><tr><td><code>slippageTolerance</code></td><td><code>number</code> (Optional)</td><td>Tolerance of the quote the swap committed to, in basis points. See <a href="#realized-slippage">Realized slippage</a>.</td></tr><tr><td><code>realizedSlippageBps</code></td><td><code>number</code> (Optional)</td><td>Realized slippage of the settled output vs the quoted expected output, in basis points. Positive means the swap settled for <em>less</em> than quoted. See <a href="#realized-slippage">Realized slippage</a>.</td></tr><tr><td><code>meta</code></td><td><code>object</code></td><td>Metadata including images, provider info, and <code>fees</code> — the fees actually charged. See <a href="#realized-fees">Realized fees</a>. And the USD valuations <code>amountInUsd</code> / <code>amountOutUsd</code> (see <a href="#usd-amounts">USD amounts</a>).</td></tr><tr><td><code>payload</code></td><td><code>object</code></td><td>Additional transaction specific data.</td></tr><tr><td><code>legs</code></td><td><code>array</code></td><td>Detailed breakdown of each transaction leg.</td></tr></tbody></table>

***

#### Example response:

```json
{
  "chainId": "43114",
  "hash": "0x18f6d7b91ceffcc6d70b5d73f324198d9847531b7fe53d9446b7e60a64fa44b9",
  "block": 57181100,
  "type": "swap",
  "status": "completed",
  "trackingStatus": "completed",
  "fromAsset": "AVAX.AVAX",
  "fromAmount": "9.58",
  "fromAddress": "0xC935B2397f0c6f85235ceFba2Eb714fb5F919Ca0",
  "toAsset": "THOR.RUNE",
  "toAmount": "0",
  "toAddress": "thor1mse4ysqpru6s7f6twlskt2yz963xz630mwtxqn",
  "finalisedAt": 1739313043,
  "slippageTolerance": 300,
  "realizedSlippageBps": 42,
  "meta": {
    "provider": "THORCHAIN",
    "providerAction": "swap",
    "amountInUsd": "312.44",
    "amountOutUsd": "311.13",
    "fees": [
      {
        "type": "inbound",
        "amount": "0.000041953000041953",
        "asset": "AVAX.AVAX",
        "chain": "AVAX",
        "protocol": "THORCHAIN"
      }
    ],
    "images": {
      "from": "https://storage.googleapis.com/token-list-swapkit/images/avax.avax.png",
      "to": "https://storage.googleapis.com/token-list-swapkit/images/thor.rune.png",
      "provider": "https://storage.googleapis.com/token-list-swapkit/images/thor.rune.png",
      "chain": "https://storage.googleapis.com/token-list-swapkit/avax.avax.png"
    }
  },
  "payload": {
    "memo": "=:r:thor1mse4ysqpru6s7f6twlskt2yz963xz630mwtxqn:0:-_/t:5/50"
  },
  "legs": [
    {
      "chainId": "43114",
      "hash": "0x18f6d7b91ceffcc6d70b5d73f324198d9847531b7fe53d9446b7e60a64fa44b9",
      "block": 57181100,
      "type": "swap",
      "status": "completed",
      "trackingStatus": "completed",
      "fromAsset": "AVAX.AVAX",
      "fromAmount": "9.58",
      "fromAddress": "0xC935B2397f0c6f85235ceFba2Eb714fb5F919Ca0",
      "toAsset": "AVAX.AVAX",
      "toAmount": "9.58",
      "toAddress": "0x8F66c4AE756BEbC49Ec8B81966DD8bba9f127549",
      "finalisedAt": 1739313038,
      "meta": {
        "fees": [
          {
            "type": "inbound",
            "amount": "0.000041953000041953",
            "asset": "AVAX.AVAX",
            "chain": "AVAX",
            "protocol": "THORCHAIN"
          }
        ],
        "images": {
          "from": "https://storage.googleapis.com/token-list-swapkit/images/avax.avax.png",
          "to": "https://storage.googleapis.com/token-list-swapkit/images/avax.avax.png",
          "chain": "https://storage.googleapis.com/token-list-swapkit/avax.avax.png"
        }
      },
      "payload": {
        "memo": "=:r:thor1mse4ysqpru6s7f6twlskt2yz963xz630mwtxqn:0:-_/t:5/50"
      }
    },
    {
      "chainId": "thorchain-1",
      "hash": "18f6d7b91ceffcc6d70b5d73f324198d9847531b7fe53d9446b7e60a64fa44b9",
      "block": 19828318,
      "type": "swap",
      "status": "completed",
      "trackingStatus": "completed",
      "fromAsset": "AVAX.AVAX",
      "fromAmount": "9.58",
      "fromAddress": "0xc935b2397f0c6f85235cefba2eb714fb5f919ca0",
      "toAsset": "THOR.RUNE",
      "toAmount": "0",
      "toAddress": "thor1mse4ysqpru6s7f6twlskt2yz963xz630mwtxqn",
      "finalisedAt": 1739313043,
      "meta": {
        "provider": "THORCHAIN",
        "providerAction": "swap",
        "images": {
          "from": "https://storage.googleapis.com/token-list-swapkit/images/avax.avax.png",
          "to": "https://storage.googleapis.com/token-list-swapkit/images/thor.rune.png",
          "provider": "https://storage.googleapis.com/token-list-swapkit/images/thor.rune.png",
          "chain": "https://storage.googleapis.com/token-list-swapkit/thor.rune.png"
        }
      },
      "payload": {
        "memo": "=:r:thor1mse4ysqpru6s7f6twlskt2yz963xz630mwtxqn:0:-_/t:5/50",
        "thorname": ""
      }
    }
  ]
}
```

***

#### Notes:

* The `legs` array provides a detailed view of each step in the transaction process.
* `meta` contains additional information, including images, the swap provider details, the fees actually charged (`meta.fees`), and the USD valuations (`meta.amountInUsd` / `meta.amountOutUsd`).
* The `payload` may include data like `evmCalldata` or `memo` for more complex transactions.

#### Realized fees

`meta.fees` reports what the swap actually settled, as opposed to the estimates returned by `/v3/quote`. It uses the same schema as the quote response's `fees`, so a quote and its settled result can be compared entry for entry.

It appears at transaction level and on each entry of `legs`, and is omitted entirely — never an empty array — whenever no fee has settled yet or the provider exposes none.

One exception: **NEAR**'s `outbound` echoes the quote-time withdrawal fee, since 1Click exposes no settled value. It appears only on a successful swap and is already reflected in the amount received.

**Fee entry fields**

<table><thead><tr><th width="140">Field</th><th width="110">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>type</code></td><td><code>string</code></td><td>One of <code>inbound</code>, <code>outbound</code>, <code>liquidity</code>, <code>network</code>, <code>affiliate</code>, <code>service</code>.</td></tr><tr><td><code>amount</code></td><td><code>string</code></td><td>Fee amount, in decimal units of <code>asset</code>.</td></tr><tr><td><code>asset</code></td><td><code>string</code></td><td>Asset the fee was charged in (e.g. <code>"BTC.BTC"</code>).</td></tr><tr><td><code>chain</code></td><td><code>string</code></td><td>Chain of the fee asset.</td></tr><tr><td><code>protocol</code></td><td><code>string</code></td><td>Provider that charged it (e.g. <code>"CHAINFLIP"</code>).</td></tr><tr><td><code>amountBps</code></td><td><code>number</code></td><td>Optional. Fee in basis points — present on <code>affiliate</code> and <code>service</code> entries.</td></tr></tbody></table>

It is an array, not one bucket per type, because a single swap can pay the same type more than once. Within one provider, entries sharing a type and asset are summed. Across legs they're concatenated, not summed: an exact repeat of type + asset + protocol is dropped, while the same type and asset from different protocols both appear. `inbound` is the exception — see below.

**Leg-level vs transaction-level**

Each leg reports what that leg paid. For `inbound`, the transaction level reports the sum of the wallet gas and any matching ingress. On a Chainflip BTC deposit, the miner fee sits on the deposit leg, Chainflip's ingress fee sits on the swap leg, and the transaction reports the total:

```json
{
  "meta": {
    "fees": [
      { 
        "type": "inbound", 
        "amount": "0.00001219", 
        "asset": "BTC.BTC", 
        "chain": "BTC", 
        "protocol": "CHAINFLIP" 
      }
    ]
  },
  "legs": [
    {
      "meta": {
        "fees": [
          { 
            "type": "inbound", 
            "amount": "0.00000861", 
            "asset": "BTC.BTC", 
            "chain": "BTC", 
            "protocol": "CHAINFLIP" 
          }
        ]
      }
    },
    {
      "meta": {
        "fees": [
          { 
            "type": "inbound", 
            "amount": "0.00000358", 
            "asset": "BTC.BTC", 
            "chain": "BTC", 
            "protocol": "CHAINFLIP" 
          }
        ]
      }
    }
  ]
}
```

Those two components merged because both were in BTC. When they're in different assets they stay separate — a Chainflip USDC deposit on Ethereum reports two inbound entries: the ingress fee in USDC and the wallet gas in ETH.

Plain transfers with no swap provider report no inbound fee — there is nothing to attribute it to. It is also omitted when the fee is zero, when the chain data doesn't expose the sender's fee (Zcash shielded sends, UTXO transactions with an unresolvable input, Tron when free bandwidth covered the cost).

#### USD amounts

`meta.amountInUsd` / `meta.amountOutUsd` value the amount deposited and received. Transaction-level only.

* Positive 2-decimal string or absent — never `"0"`. A sub-cent value rounds to zero and is therefore reported as absent, not as a misleading `"0.00"`. Absent means "not priced", not "worth nothing"; each field resolves independently.
* Priced at current price at track time; only live or recently-settled swaps (\~1h of `finalisedAt`), never backfilled.
* The first valid value stored is kept forever, even under `forceUpdate`. There is no settlement guard, so if you track while the swap is still pending, whatever `toAmount` the parser had at that moment is the value that gets frozen.

#### Realized slippage

On a completed swap, `/track` compares the settled output with the quote it was committed against. Both fields are in bps, transaction-level only (not per leg), and omitted when uncomputable.

| Field                 | Type                | Description                                                                           |
| --------------------- | ------------------- | ------------------------------------------------------------------------------------- |
| `slippageTolerance`   | `number` (optional) | Tolerance of the quote the swap committed to, in bps.                                 |
| `realizedSlippageBps` | `number` (optional) | (expected − actual) / expected in bps, rounded. Positive = received less than quoted. |

* Only on completed swaps. Either field may be absent; `slippageTolerance` in particular isn't available from every quote source.
* On a partially refunded swap the value measures against the full quoted amount, so it reads as a large positive number — check `trackingStatus` for `partially_refunded` before reading it as slippage.
* Automatic on Chainflip, NEAR, Garden, Flashnet, Jupiter and Harbor routes. On any other route, pass `routeId` on your `/track` calls until the response contains a parsed transaction — quotes expire \~5 minutes after quoting, so call right after broadcast.

### Status fields

Every transaction, and every leg in its `legs` array, carries two status fields:

* `status` — the coarse lifecycle state. Always present, a small stable set. Poll this for completion.
* `trackingStatus` — the finer phase. Varies more between providers, optional in the schema, and the only place some states are reported.

Both are taken from the leg currently in flight — the first leg not yet `completed`, `refunded` or `failed` — so they advance as the swap moves between legs, and report the last leg once all are terminal. `unknown` and `not_started` are not terminal.

Two exceptions:

* `partially_refunded` on any leg surfaces as the transaction's `trackingStatus`, whichever leg is in flight.
* When the transaction's `trackingStatus` is `refunded`, its `status` is reported as `refunded`. Legs keep their own values.

Poll `status` and treat `completed`, `refunded` and `failed` as terminal. Read `trackingStatus` for mid-swap progress, or for the states `status` cannot express.

| Route                                                                    | status sequence                      | Where the swap phase is reported                                          |
| ------------------------------------------------------------------------ | ------------------------------------ | ------------------------------------------------------------------------- |
| Chainflip                                                                | `pending` → `swapping` → `completed` | `status`, mapped from its `SWAPPING`/`SENDING` states                     |
| THORChain (incl. streaming), Maya, NEAR Intents, Mayan, Flashnet, Garden | `pending` → `completed`              | `trackingStatus`                                                          |
| Harbor                                                                   | `pending` → `completed`              | not reported — `trackingStatus` goes `inbound` → `outbound` → `completed` |
| Same-chain: 1inch and the other EVM aggregators, Jupiter on Solana       | `unknown` → `completed` / `failed`   | no middle state; never reports `pending`                                  |

Before the deposit is seen, a cross-chain `status` may read `not_started` or `unknown`.

So a THORChain or NEAR swap observably goes `pending` → `completed`, and that is correct. To display "swapping", read `legs[].trackingStatus`.

**Chainflip caveat.** The transaction reads `swapping` only once the deposit leg has confirmed. That leg's status comes from our own chain parsing, so while it is still settling the transaction reads `pending` — `trackingStatus` `mempool` or `broadcasted` on UTXO deposits, `starting` on EVM — even though `legs[1].status` already reads `swapping`.

### Tracking status

`trackingStatus` reports the specific phase of the transaction. Use it for progress display, and for what `status` cannot express (`swapping` off Chainflip, `partially_refunded`).

Which values a route passes through depends on the provider; the set is deliberately not uniform and may grow. Keep polling `status` for completion — don't read an unrecognized `trackingStatus` as "still progressing", since `dropped`, `reverted`, `replaced`, `retries_exceeded` and `parsing_error` all accompany a swap that has stopped.

{% hint style="info" %}
**Known issue:** a failed Flashnet swap currently reports `trackingStatus: "swapping"` instead of a failure state. `status` correctly reads `failed` — another reason to treat `status` as the source of truth for completion.
{% endhint %}

### Transaction status

An important part of the response is the transaction status from the `status` field, which can have multiple values:

<table><thead><tr><th width="154">Status value</th><th>Explanation</th></tr></thead><tbody><tr><td><code>not_started</code></td><td>The swap has not happened yet.</td></tr><tr><td><code>pending</code></td><td>Intermediate state. The transaction has been detected by the mempool but is pending block confirmation.</td></tr><tr><td><code>swapping</code></td><td>The swap is happening.</td></tr><tr><td><code>completed</code></td><td>The swap is finished.</td></tr><tr><td><code>refunded</code></td><td>The swap was refunded because of the slippage settings.</td></tr><tr><td><code>unknown</code></td><td>Catch all for other situations.</td></tr><tr><td><code>failed</code></td><td>The transaction failed in an inbound EVM contract.</td></tr></tbody></table>
