# Transaction traces: debug_traceTransaction and the Data API trace endpoints

> Original page: https://docs.blockvectra.com/en/guides/transaction-traces/

## Two ways to reconstruct a call tree [#two-ways-to-reconstruct-a-call-tree]

A transaction trace is the reconstructed call tree of an execution: which contract was called, with which input, how much gas it consumed, and which sub-calls it made. BlockVectra exposes it through two surfaces:

* **JSON-RPC `debug_trace*`** — runs against the chain's node through the JSON-RPC endpoint, so it can trace recent state the node still has.
* **Data API traces** — `GET /{chain}/transactions/{hash}/trace` and `GET /{chain}/blocks/{number}/traces` return stored, indexed call trees over REST.

Both use the same API key and are metered in CU by method weight (see the weights below). Which one fits depends on whether you need a single transaction or a whole block, how recent the target is, and whether you want to walk a full block without pagination.

## Limits that apply to debug\_trace\* [#limits-that-apply-to-debug_trace]

`debug_trace*` requests are accepted only for methods and tracers the chain's method policy allows:

* **Allowed tracers**: the `tracer` parameter accepts only the built-in native tracers — `callTracer`, `flatCallTracer`, `prestateTracer`, `4byteTracer`, `noopTracer`, or omitting it to use the default struct logger. Any other value is rejected with JSON-RPC error `-32602 tracer not allowed`, not forwarded, and not billed.
* **Trace timeout**: the `timeout` parameter must be a valid duration and at most 30s; otherwise the request is rejected with `-32602 trace timeout not allowed`, not forwarded, and not billed.
* **Node sync guard**: while a chain's node is not synced, every method except `eth_chainId` — including `debug_trace*` and the hash/height history lookups — returns `-32010`; the call is not forwarded and not billed.
* **State window**: `debug_traceCall`, `debug_traceBlockByNumber`, `debug_traceTransaction`, and `debug_traceBlockByHash` target a block that must be inside the chain's state window. A target earlier than the window, or one that uses the `safe`, `finalized`, or `earliest` tag, returns `-32011`. The node's own out-of-window error is not billed. Block and receipt data are not limited by the state window, but they are limited by the node's retained history.
* **Hash pre-resolution**: `debug_traceTransaction` and `debug_traceBlockByHash` first resolve the hash to a block height, then apply the state window. A hash that is not `0x` plus 64 hex digits returns `-32000 transaction not found` / `block not found` without querying the node; a well-formed hash the node does not know returns the same `-32000`, and a failed lookup is `-32603 upstream unavailable` (retryable). None of these are forwarded or billed.
* **Per-chain method policy**: which `debug_trace*` methods a chain allows is published by the public `GET /v1/chains` response. Read it at runtime instead of hardcoding a method list; chains are listed on [Supported Chains](/en/chains/), and the method reference is in the [JSON-RPC methods](/en/api/json-rpc/methods/) page.

### Requesting debug\_traceTransaction with callTracer [#requesting-debug_tracetransaction-with-calltracer]

The specification's example request carries only the transaction hash. The call below adds the `tracer` parameter to request a call tree; the value comes from the tracer allowlist above, so no response body is invented here.

<Tabs groupId="code-lang" items="['cURL', 'TypeScript', 'Python']">
  <Tab value="cURL">
    ```bash
    export BLOCKVECTRA_API_KEY=rgw_your_api_key

    # The specification example (reqTraceTx) passes only the transaction hash:
    #   {"jsonrpc":"2.0","id":1,"method":"debug_traceTransaction",
    #    "params":["0xcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd"]}
    # Add "tracer" to request a call tree with one of the allowed native tracers.
    curl -s "https://dev-api.blockvectra.network/v1/robinhood_mainnet" \
      -H 'Content-Type: application/json' \
      -H "x-api-key: $BLOCKVECTRA_API_KEY" \
      -d '{
        "jsonrpc": "2.0",
        "id": 1,
        "method": "debug_traceTransaction",
        "params": [
          "0xcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd",
          { "tracer": "callTracer" }
        ]
      }'
    ```
  </Tab>

  <Tab value="TypeScript">
    ```ts
    // The spec example keeps the endpoint chain-scoped: …/v1/{chain}.
    const RPC_ENDPOINT = "https://dev-api.blockvectra.network/v1/robinhood_mainnet";

    const res = await fetch(RPC_ENDPOINT, {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        "x-api-key": process.env.BLOCKVECTRA_API_KEY!,
      },
      body: JSON.stringify({
        jsonrpc: "2.0",
        id: 1,
        method: "debug_traceTransaction",
        params: [
          "0xcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd",
          { tracer: "callTracer" },
        ],
      }),
    });

    const body = (await res.json()) as {
      result?: unknown;
      error?: { code: number; message: string };
    };

    if (body.error) {
      throw new Error(`debug_traceTransaction error ${body.error.code}: ${body.error.message}`);
    }
    console.log(body.result);

    // npx tsx example.mts
    ```
  </Tab>

  <Tab value="Python">
    ```python
    import os
    import requests

    # The spec example keeps the endpoint chain-scoped: …/v1/{chain}.
    RPC_ENDPOINT = "https://dev-api.blockvectra.network/v1/robinhood_mainnet"

    res = requests.post(
        RPC_ENDPOINT,
        headers={
            "Content-Type": "application/json",
            "x-api-key": os.environ["BLOCKVECTRA_API_KEY"],
        },
        json={
            "jsonrpc": "2.0",
            "id": 1,
            "method": "debug_traceTransaction",
            "params": [
                "0xcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd",
                {"tracer": "callTracer"},
            ],
        },
    )
    res.raise_for_status()
    body = res.json()

    if "error" in body:
        err = body["error"]
        raise RuntimeError(f"debug_traceTransaction error {err.get('code')}: {err.get('message')}")
    print(body["result"])
    ```
  </Tab>
</Tabs>

## What the Data API trace endpoints provide [#what-the-data-api-trace-endpoints-provide]

The Data API returns stored call trees for two scopes. Neither is paginated: `next_cursor` is never present.

* `GET /{chain}/transactions/{hash}/trace` — the call frame of one transaction, looked up by transaction hash.
* `GET /{chain}/blocks/{number}/traces` — one call tree per transaction in a block, in `tx_index` order. A real, finalized block with zero transactions returns `data: []`.

The response envelope is:

* `TxTraceEnvelope`: `data` is a `CallFrame` directly, plus `meta`.
* `BlockTracesEnvelope`: `data` is an array of `BlockTraceItem`, each with `txHash` and the `result` `CallFrame`, plus `meta`.

Both trace endpoints return the standard Ethereum `callTracer` format. This is an exception to the Data API's money-safety encoding: elsewhere, a value that can exceed `2^53` is serialized as a decimal string; on these two endpoints, `value`, `gas`, and `gasUsed` are `0x`-prefixed hex quantities, not decimal strings. Every `CallFrame` carries `type`, `from`, `gas`, `gasUsed`, and `input`; `type` is one of `CALL`, `DELEGATECALL`, `STATICCALL`, `CREATE`, `CREATE2`, or `SELFDESTRUCT`. `to` is absent for a `CREATE`/`CREATE2` frame's target, and `value` is absent for a `STATICCALL` frame. Optional members are `output` (absent when the call returned no data), `error` (absent on success), `revertReason` (present only for an `Error(string)` revert), and `calls` (nested sub-calls in call order). Additional members of the frame are preserved.

To make the shape concrete, here is the `CallFrame` field skeleton — annotated, not a captured response. The locked specification has no response example for either trace endpoint, so no concrete values are shown:

```jsonc
{
  "type": "CALL | DELEGATECALL | STATICCALL | CREATE | CREATE2 | SELFDESTRUCT",
  "from": "0x…",                      // 20-byte address
  "to": "0x…",                        // absent for a CREATE/CREATE2 target
  "value": "0x…",                     // 0x-prefixed hex quantity; absent for STATICCALL
  "gas": "0x…",                       // 0x-prefixed hex quantity
  "gasUsed": "0x…",                   // 0x-prefixed hex quantity
  "input": "0x…",
  "output": "0x…",                    // absent when the call returned no data
  "error": "…",                       // absent on success
  "revertReason": "…",                // absent unless the call reverted with Error(string)
  "calls": []                         // nested sub-calls in call order; absent for a leaf frame
}
```

### Parameters [#parameters]

* `{chain}` (path parameter, required): chain identifier, the `chain` value of an entry in `GET /chains`. Matching is exact and case-sensitive; aliases and numeric chain IDs are not accepted.
* `{hash}` (path parameter, required for the transaction trace): 32-byte transaction hash, `0x` prefix optional, either digit case.
* `{number}` (path parameter, required for the block traces): non-negative block height.

### Coverage and finality [#coverage-and-finality]

* Both endpoints belong to the `traces` capability. A chain without it returns `422 no_coverage`. Chains that provide this dataset are subject to the [Supported Chains](/en/chains/) page and the dataset directory.
* Trace data can start later than the rest of a chain's indexed history. A block before the chain's first traced block, inside a recorded gap, or one whose transactions were indexed but never traced and that is already too far behind the indexed head returns `422 no_coverage`; `GET /chains` reports the boundary as `coverage.traces_from_block`, and a trace request before it, or inside a range that could not be traced, returns `422 no_coverage`.
* The transaction trace resolves the hash to a block, then checks the block against `finalized_block`: a resolved block above it returns `409 finality_exceeded`. Hash lookups have no watermark to check "not indexed yet" against, so a just-submitted transaction whose indexing has not caught up also returns `404 not_found`; retry briefly before treating it as permanent.
* The block traces endpoint takes a block number. A `{number}` above `as_of_block` returns `409 not_indexed_yet` with `indexed_through`; a `{number}` at or below `as_of_block` but above `finalized_block` returns `409 finality_exceeded`.
* A recent block with transactions but no trace data yet returns `503 unavailable` with a `Retry-After` header. Once it is further behind the indexed head than the chain's trace window, it is `422 no_coverage` instead.

### Requesting a trace from the Data API [#requesting-a-trace-from-the-data-api]

<Tabs groupId="code-lang" items="['cURL', 'TypeScript', 'Python']">
  <Tab value="cURL">
    ```bash
    export BLOCKVECTRA_API_KEY=rgw_your_api_key

    # One transaction's call frame.
    curl -s "https://dev-api.blockvectra.network/v1/data/robinhood_mainnet/transactions/0xcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd/trace" \
      -H "x-api-key: $BLOCKVECTRA_API_KEY"

    # One call tree per transaction in a finalized block.
    curl -s "https://dev-api.blockvectra.network/v1/data/robinhood_mainnet/blocks/1/traces" \
      -H "x-api-key: $BLOCKVECTRA_API_KEY"
    ```
  </Tab>

  <Tab value="TypeScript">
    ```ts
    const hash = "0xcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd";

    // Read meta.finalized_block from any other chain-scoped response first: the
    // block traces endpoint only serves blocks at or below it.
    const txRes = await fetch(
      `https://dev-api.blockvectra.network/v1/data/robinhood_mainnet/transactions/${hash}/trace`,
      { headers: { "x-api-key": process.env.BLOCKVECTRA_API_KEY! } },
    );
    const txBody = await txRes.json();
    console.log(txBody.data, txBody.meta);

    const blockRes = await fetch("https://dev-api.blockvectra.network/v1/data/robinhood_mainnet/blocks/1/traces", {
      headers: { "x-api-key": process.env.BLOCKVECTRA_API_KEY! },
    });
    const blockBody = await blockRes.json();
    console.log(blockBody.data.map((item: { txHash: string }) => item.txHash));

    // npx tsx example.mts
    ```
  </Tab>

  <Tab value="Python">
    ```python
    import os
    import requests

    hash_ = "0xcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd"
    headers = {"x-api-key": os.environ["BLOCKVECTRA_API_KEY"]}

    # Read meta.finalized_block from any other chain-scoped response first: the
    # block traces endpoint only serves blocks at or below it.
    tx = requests.get(
        f"https://dev-api.blockvectra.network/v1/data/robinhood_mainnet/transactions/{hash_}/trace",
        headers=headers,
    )
    tx.raise_for_status()
    tx_body = tx.json()
    print(tx_body["data"], tx_body["meta"])

    block = requests.get(
        "https://dev-api.blockvectra.network/v1/data/robinhood_mainnet/blocks/1/traces",
        headers=headers,
    )
    block.raise_for_status()
    block_body = block.json()
    print([item["txHash"] for item in block_body["data"]])
    ```
  </Tab>
</Tabs>

## Which one to use [#which-one-to-use]

| Typical task                                                      | Better fit                               | Why                                                                                                                                                     |
| ----------------------------------------------------------------- | ---------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Reconstruct a single transaction right after it lands             | `debug_traceTransaction`                 | It runs against the node's current state, so it is not limited to blocks at or below `finalized_block`; availability follows the chain's method policy. |
| Read a single transaction's stored call tree                      | `GET /{chain}/transactions/{hash}/trace` | Returns the transaction's `CallFrame` directly over REST; the resolved block must be at or below `finalized_block`.                                     |
| Read every call tree in one block in one request                  | `GET /{chain}/blocks/{number}/traces`    | Returns the whole block unpaginated, in `tx_index` order; the block must be at or below `finalized_block`.                                              |
| Trace state the node still has but the dataset has not stored yet | `debug_trace*`                           | The Data API serves stored data only up to `finalized_block`; the node can answer for more recent blocks.                                               |

## CU per call [#cu-per-call]

Every method is billed by its CU weight. The weights below are read from the platform plans API at build time:

<TransactionTracesCu lang="en" />

Rejected requests are not billed. For the full billing rules, see [What is not billed: error codes and billing rules](/en/guides/billing-rules/).

## Next steps [#next-steps]

* [Browse the datasets directory](https://blockvectra.com/en/data/) to see every dataset BlockVectra indexes.
* [See the free plan and pricing](https://blockvectra.com/en/pricing/#free) to check what your account includes.
* [Log in to the console](https://console.blockvectra.com/en/login/?next=%2Fen%2Fkeys%2F) to create an API key.
