Decode a Base transaction by hash
Overview
GET /api/v1/decode/{txHash} turns a confirmed Base transaction into
a human-readable action plus normalized asset movements — each with token,
amount and USD value. Pass the transaction hash as a path parameter; the
endpoint resolves the receipt, decodes the logs and returns structured JSON.
Add ?verbose=true for the full per-log breakdown. The price is
the same with or without it: a flat $0.005 in USDC on Base via x402, with no
account and no API key.
Request
One required path parameter and one optional query parameter. Unknown query parameters are ignored for forward-compatibility.
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
txHash | path | ^0x[0-9a-fA-F]{64}$ | Yes | Hash of the confirmed Base transaction to decode. |
verbose | query | enum true | false | No |
Include the full per-log decoding breakdown. Defaults to
false; the price is unchanged.
|
Response
A successful call returns 200 with the decoded transaction as
JSON. Example for transaction
0x7fa442ef…be90:
{
"decoder": {
"version": "1.0.0",
"schema": "v1",
"decoded_at": "2026-06-13T09:12:04Z"
},
"tx": {
"hash": "0x7fa442ef978286a2436b82a92dca545c3578c15cd5097aa31fa1831ee13ebe90",
"chain_id": 8453,
"network": "eip155:8453",
"status": "success",
"block_number": 47246941,
"block_hash": "0x226592780c490ebfd3a72625a3fff67b0232a956eb24d4afaebf7b9c36cb183c",
"block_timestamp": 1781283229,
"block_timestamp_iso": "2026-06-12T16:53:49Z",
"finality": "finalized",
"from": "0x7bfd6606628a520888be12431d2d896e116f5c6d",
"to": "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913",
"to_label": "USDC token contract",
"value": {
"raw": "0",
"formatted": "0"
},
"nonce": 134,
"transaction_index": 31,
"gas": {
"gas_used": 45047,
"effective_gas_price_wei": "6000000",
"l1_fee_wei": "3313208105",
"total_fee_wei": "273595208105",
"total_fee_eth": "0.000000273595208105",
"fee_usd": 0.000457099,
"fee_pricing": {
"status": "ok",
"source": "defillama",
"price_usd": 1670.7138540734327,
"priced_at": 1781283244,
"confidence": 0.99
}
},
"explorer_url": "https://basescan.org/tx/0x7fa442ef978286a2436b82a92dca545c3578c15cd5097aa31fa1831ee13ebe90"
},
"principal": {
"address": "0x7bfd6606628a520888be12431d2d896e116f5c6d",
"basis": "tx-sender"
},
"action": {
"type": "token-transfer",
"family": "transfer",
"protocols": [
"erc20"
],
"executed": true,
"description": "ERC-20 token transfer",
"details": {
"sender": "0x7bfd6606628a520888be12431d2d896e116f5c6d",
"recipient": "0xe8ea93eb0947ec6bedb8fa6396cb3a168276ddb3",
"amount": {
"asset": {
"type": "erc20",
"address": "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913",
"symbol": "USDC",
"name": "USD Coin",
"decimals": 6,
"token_id": null
},
"raw": "400",
"formatted": "0.0004",
"value_usd": 0.000399916,
"pricing": {
"status": "ok",
"source": "defillama",
"price_usd": 0.9997890644178156,
"priced_at": 1781283244,
"confidence": 0.99
},
"inferred": false
}
},
"secondary": []
},
"summary": "Sent 0.0004 USDC ($0.0004) to 0xe8ea…ddb3",
"movements": [
{
"index": 0,
"kind": "transfer",
"asset": {
"type": "erc20",
"address": "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913",
"symbol": "USDC",
"name": "USD Coin",
"decimals": 6,
"token_id": null
},
"amount": {
"raw": "400",
"formatted": "0.0004"
},
"from": "0x7bfd6606628a520888be12431d2d896e116f5c6d",
"from_label": null,
"to": "0xe8ea93eb0947ec6bedb8fa6396cb3a168276ddb3",
"to_label": null,
"direction": "out",
"value_usd": 0.000399916,
"pricing": {
"status": "ok",
"source": "defillama",
"price_usd": 0.9997890644178156,
"priced_at": 1781283244,
"confidence": 0.99
},
"log_index": 368,
"inferred": false,
"inferred_from": null
}
],
"approvals": [],
"net_flows": [
{
"address": "0x7bfd6606628a520888be12431d2d896e116f5c6d",
"address_label": null,
"is_principal": true,
"deltas": [
{
"asset": {
"type": "erc20",
"address": "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913",
"symbol": "USDC",
"name": "USD Coin",
"decimals": 6,
"token_id": null
},
"amount": {
"raw": "-400",
"formatted": "-0.0004"
},
"value_usd": -0.000399916,
"price_status": "ok"
}
]
},
{
"address": "0xe8ea93eb0947ec6bedb8fa6396cb3a168276ddb3",
"address_label": null,
"is_principal": false,
"deltas": [
{
"asset": {
"type": "erc20",
"address": "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913",
"symbol": "USDC",
"name": "USD Coin",
"decimals": 6,
"token_id": null
},
"amount": {
"raw": "400",
"formatted": "0.0004"
},
"value_usd": 0.000399916,
"price_status": "ok"
}
]
}
],
"usd": {
"total_value_usd": 0.000399916,
"basis": "transfer-amount",
"total_price_status": "ok",
"priced_movements": 1,
"unpriced_movements": 0
},
"completeness": {
"engine": "logs-only",
"native_eth_visibility": "tx-value-and-weth-events-only",
"decoded_logs": 1,
"interpreted_logs": 1,
"uninterpreted_logs": 0,
"level": "full",
"flags": []
}
} Top-level fields
These ten fields are present on every 200 response:
| Field | Description |
|---|---|
decoder | Decoder metadata: version, schema version and the decoded_at timestamp. |
tx | On-chain transaction facts: hash, chain_id, status, block, from/to, value and gas (with USD-priced fee). |
principal | The address the decode is framed around, plus the basis used to pick it (e.g. tx-sender). |
action | The primary interpreted action: type, family, protocols, a human description and structured details. |
summary | One-line natural-language sentence describing what the transaction did. |
movements | Normalized asset movements, one per relevant log: asset, amount, from/to, direction and USD value. |
approvals | ERC-20/ERC-721 approvals granted in the transaction (empty array when none). |
net_flows | Per-address net deltas after netting all movements, with USD value and price status per asset. |
usd | Aggregate USD accounting: total value, basis, price status and the priced/unpriced movement counts. |
completeness | Decode coverage signal: engine mode, native-ETH visibility, log counts, a level and any limitation flags. |
With ?verbose=true the body is a superset: the same ten fields
plus a per-log decoding breakdown. No field is removed, so a verbose response
is safe to parse with the default schema.
Response headers
| Header | Meaning |
|---|---|
ETag | Content hash of the decoded response. A confirmed transaction is immutable, so the ETag is stable for conditional requests. |
Cache-Control: private | The response is bound to your paid request and must not be stored by shared caches. |
X-Decoder-Version | The decoder version that produced the response; mirrors decoder.version in the body. |
Examples
The endpoint is pay-per-call: a plain request returns 402 with
the payment requirements. The curl tab surfaces that 402; the JavaScript and
Python tabs pay and retry through an x402 client.
# The endpoint is pay-per-call: a plain request returns HTTP 402 with the
# payment requirements (price, network, asset). Paying requires an x402 client
# (see the JS/Python tabs); curl alone surfaces the 402, it does not pay.
curl -i https://txdecode.com/api/v1/decode/0x7fa442ef978286a2436b82a92dca545c3578c15cd5097aa31fa1831ee13ebe90 import { wrapFetchWithPayment, x402Client, decodePaymentResponseHeader } from "@x402/fetch";
import { ExactEvmScheme } from "@x402/evm";
import { privateKeyToAccount } from "viem/accounts";
// EVM account that funds the call (USDC on Base). Keep the key server-side.
const account = privateKeyToAccount(process.env.PRIVATE_KEY);
// Register the exact-EVM client scheme for Base mainnet.
const client = new x402Client();
client.register("eip155:8453", new ExactEvmScheme(account));
// wrapFetchWithPayment handles the full flow: 402 -> sign EIP-3009 -> retry -> 200.
const fetchWithPay = wrapFetchWithPayment(fetch, client);
const res = await fetchWithPay("https://txdecode.com/api/v1/decode/0x7fa442ef978286a2436b82a92dca545c3578c15cd5097aa31fa1831ee13ebe90");
const decoded = await res.json();
console.log(decoded.summary);
// The settlement tx hash comes back in the PAYMENT-RESPONSE header.
const header = res.headers.get("PAYMENT-RESPONSE");
const settle = header ? decodePaymentResponseHeader(header) : null;
console.log("payment:", settle?.transaction); # Python: use the official Coinbase x402 client (pip install x402).
# A plain request returns HTTP 402 with the payment requirements; the x402
# client signs the USDC payment on Base and retries automatically. The exact
# client API tracks the package version — see https://x402.org and
# https://github.com/coinbase/x402 (examples/python/clients/requests).
import requests
res = requests.get("https://txdecode.com/api/v1/decode/0x7fa442ef978286a2436b82a92dca545c3578c15cd5097aa31fa1831ee13ebe90")
print(res.status_code) # 402 until paid
print(res.headers["PAYMENT-REQUIRED"]) # price, network, asset Errors
Every error uses a JSON envelope with a stable code. The table
below lists each code, its HTTP status, whether it is retryable and the
suggested Retry-After.
| HTTP | Code | Retryable | Retry-After | When |
|---|---|---|---|---|
| 400 | invalid-tx-hash | no | — | Path param does not match `^0x[0-9a-fA-F]{64}$`. |
| 400 | invalid-parameter | no | — | Invalid query param (e.g. `verbose` ≠ true/false). Unknown params are ignored (forward-compat). |
| 404 | tx-not-found | no | — | Well-formed hash unknown to the RPC fallback chain. May be another chain or a just-broadcast tx — see `details.note`. |
| 422 | tx-pending | retryable | 3 | Transaction seen in mempool but no receipt yet: nothing to decode. Best-effort. |
| 429 | rate-limited | retryable | from header | Pre-payment rate limit (30 requests per minute per IP per endpoint). |
| 503 | upstream-unavailable | retryable | 30 | All RPCs in the fallback chain are down, or the circuit breaker is open. |
| 500 | internal-error | retryable | — | A bug on our side; `details.request_id` helps support. |
No-settle guarantee. Non-200 responses are never settled — errors never cost $0.005.
See the full error reference for example bodies and per-code detail.
Rate limits
Before payment, requests are rate-limited to 30 requests per minute per IP per endpoint. Exceeding it
returns 429 rate-limited with a
Retry-After header. Paid calls are not subject to this
pre-payment limit.
Related
- Error reference — every code, status and meaning.
- Pricing — the flat $0.005 per-call model in USDC on Base.
- Playground — run the full pay-per-call flow live.