# Send a raw transaction

Source: https://docs.graphops.xyz/rpc/guides/send-raw-transaction

Submit a payment, swap, or contract call that you signed in your wallet, signer service, or
application. GraphOps RPC broadcasts the signed payload with `eth_sendRawTransaction` and does not
custody private keys. You store the returned hash, poll for the receipt, and decide when the
transaction is final enough to report success.

## Prerequisites

* An API key that includes RPC.
* Ethereum mainnet endpoint access.
* A locally signed transaction for the same chain ID as the endpoint.
* Application-side nonce, gas, and fee handling.

## Prepare the transaction

Before signing, check the chain and plan fees against the same endpoint you will submit to:

1. Call `eth_chainId` and confirm it matches the chain ID in the transaction. This prevents a
wrong-chain submission.
2. Estimate gas with `eth_estimateGas`, and set fees from `eth_feeHistory` and
`eth_maxPriorityFeePerGas`.
3. Sign the transaction locally and keep the signed bytes until the receipt confirms it.

## Request

Replace `0x<SIGNED_TRANSACTION>` with your signed bytes. The recording comes from a local Anvil test
chain.

CLI · curl

Prerequisites

1. Set GRAPHOPS\_API\_KEY to your GraphOps API key.

2. Replace `<SIGNED_TRANSACTION>` with your signed, hex-encoded transaction.

[Example project](/examples/rpc.guides.send-raw-transaction.sh-1.tar.gz)

```sh
curl --fail-with-body --silent --show-error "https://rpc.graphops.xyz/v1/evm/1" \
  -H "Authorization: Bearer ${GRAPHOPS_API_KEY}" \
  -H "Content-Type: application/json" \
  --data '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "eth_sendRawTransaction",
    "params": ["0x<SIGNED_TRANSACTION>"]
  }'

```

Output

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x536129e5bd563374f67d3131b7edb31cd9b95eaa89526b23f3de05ca11592dc5"
}
```

Transaction hash returned by the submitted operation

TypeScript · viem

Prerequisites

1. Install Node.js.

2. Install tsx and viem. `npm install tsx viem`

3. Set GRAPHOPS\_API\_KEY to your GraphOps API key.

4. Replace `<SIGNED_TRANSACTION>` with your signed, hex-encoded transaction.

Tested with Node.js 24.22.1, tsx 4.23.15, viem 2.57.10.

[Example project](/examples/rpc.guides.send-raw-transaction.ts-2.tar.gz)

```typescript
import { createPublicClient, http } from "viem";
import { mainnet } from "viem/chains";

const client = createPublicClient({
  chain: mainnet,
  transport: http("https://rpc.graphops.xyz/v1/evm/1", {
    fetchOptions: {
      headers: {
        Authorization: `Bearer ${process.env.GRAPHOPS_API_KEY}`,
      },
    },
  }),
});

const hash = await client.request({
  method: "eth_sendRawTransaction",
  params: ["0x<SIGNED_TRANSACTION>"],
});

console.log(JSON.stringify(hash));

```

Output

```json
"0x536129e5bd563374f67d3131b7edb31cd9b95eaa89526b23f3de05ca11592dc5"
```

Transaction hash returned by the submitted operation

Go · ethclient

Prerequisites

1. Install Go.

2. Install github.com/ethereum/go-ethereum. `go get github.com/ethereum/go-ethereum`

3. Set GRAPHOPS\_API\_KEY to your GraphOps API key.

4. Replace `<SIGNED_TRANSACTION>` with your signed, hex-encoded transaction.

Tested with Go 1.26.0, github.com/ethereum/go-ethereum v1.17.7.

[Example project](/examples/rpc.guides.send-raw-transaction.go-7.tar.gz)

```go
package main

import (
	"context"
	"encoding/json"
	"fmt"
	"log"
	"os"

	"github.com/ethereum/go-ethereum/common/hexutil"
	"github.com/ethereum/go-ethereum/core/types"
	"github.com/ethereum/go-ethereum/ethclient"
	"github.com/ethereum/go-ethereum/rpc"
)

func main() {
	ctx := context.Background()
	rpcClient, err := rpc.DialOptions(ctx, "https://rpc.graphops.xyz/v1/evm/1",
		rpc.WithHeader("Authorization", "Bearer "+os.Getenv("GRAPHOPS_API_KEY")))
	if err != nil {
		log.Fatal(err)
	}
	client := ethclient.NewClient(rpcClient)
	defer client.Close()

	raw, err := hexutil.Decode("0x<SIGNED_TRANSACTION>")
	if err != nil {
		log.Fatal(err)
	}
	tx := new(types.Transaction)
	if err := tx.UnmarshalBinary(raw); err != nil {
		log.Fatal(err)
	}
	if err := client.SendTransaction(ctx, tx); err != nil {
		log.Fatal(err)
	}

	txHash, err := json.Marshal(tx.Hash())
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(string(txHash))
}

```

Output

```json
"0x536129e5bd563374f67d3131b7edb31cd9b95eaa89526b23f3de05ca11592dc5"
```

Transaction hash returned by the submitted operation

Rust · alloy

Prerequisites

1. Install Rust.

2. Install alloy, reqwest, tokio, and serde\_json. `cargo add alloy reqwest tokio serde_json -F alloy/provider-ws -F alloy/sol-types -F alloy/contract -F reqwest/json -F tokio/macros -F tokio/rt-multi-thread`

3. Set GRAPHOPS\_API\_KEY to your GraphOps API key.

4. Replace `<SIGNED_TRANSACTION>` with your signed, hex-encoded transaction.

Tested with Rust 1.98.1, alloy 2.5.0, reqwest 0.13.5, tokio 1.48.0, serde\_json 1.0.145.

[Example project](/examples/rpc.guides.send-raw-transaction.rust-8.tar.gz)

```rust
use alloy::hex;
use alloy::providers::{Provider, ProviderBuilder};
use alloy::rpc::client::RpcClient;
use alloy::transports::http::Http;
use reqwest::header::{HeaderMap, AUTHORIZATION};
use std::{env, error::Error};

#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
    let mut headers = HeaderMap::new();
    headers.insert(
        AUTHORIZATION,
        format!("Bearer {}", env::var("GRAPHOPS_API_KEY")?).parse()?,
    );
    let http = reqwest::Client::builder()
        .default_headers(headers)
        .build()?;
    let provider = ProviderBuilder::new().connect_client(RpcClient::new(
        Http::with_client(http, "https://rpc.graphops.xyz/v1/evm/1".parse()?),
        false,
    ));

    let signed = hex::decode("0x<SIGNED_TRANSACTION>")?;
    let pending = provider.send_raw_transaction(&signed).await?;

    println!("{}", serde_json::to_string(pending.tx_hash())?);
    Ok(())
}

```

Output

```json
"0x536129e5bd563374f67d3131b7edb31cd9b95eaa89526b23f3de05ca11592dc5"
```

Transaction hash returned by the submitted operation

Python · web3.py

Prerequisites

1. Install Python.

2. Install web3. `python -m pip install web3`

3. Set GRAPHOPS\_API\_KEY to your GraphOps API key.

4. Replace `<SIGNED_TRANSACTION>` with your signed, hex-encoded transaction.

Tested with Python 3.12.12, web3 8.0.0.

[Example project](/examples/rpc.guides.send-raw-transaction.python-4.tar.gz)

```python
import json
import os
from web3 import Web3

w3 = Web3(
    Web3.HTTPProvider(
        "https://rpc.graphops.xyz/v1/evm/1",
        request_kwargs={
            "headers": {
                "Authorization": f"Bearer {os.environ['GRAPHOPS_API_KEY']}",
            },
        },
    )
)

tx_hash = w3.eth.send_raw_transaction("0x<SIGNED_TRANSACTION>")
print(json.dumps(tx_hash.to_0x_hex()))

```

Output

```json
"0x536129e5bd563374f67d3131b7edb31cd9b95eaa89526b23f3de05ca11592dc5"
```

Transaction hash returned by the submitted operation

## Store the hash

The hash identifies the transaction before it is included in a block. Store it before you report
progress to your application, so a crash or timeout can resume from the hash instead of signing
again.

## Wait for the receipt

*The hash comes back at submission, before the transaction is in a block. The receipt is null until inclusion, then shows the result in block N. Your confirmation policy decides when block N is final enough to report success.*

1. `eth_sendRawTransaction` returns `0x8a…17`: store the hash first.
2. `eth_getTransactionReceipt` returns `null`: not included yet: poll again.
3. `eth_getTransactionReceipt` returns `"status": "0x1"`: included in block N.
4. Wait until block N reaches safe or finalized: your policy.

Poll `eth_getTransactionReceipt` with the stored hash. It returns `null` until the transaction is
included in a block:

Prerequisites

1. Set GRAPHOPS\_API\_KEY to your GraphOps API key.

2. Replace `<TRANSACTION_HASH>` with the transaction hash returned when you sent it.

[Example project](/examples/rpc.guides.send-raw-transaction.sh-6.tar.gz)

```sh
curl --fail-with-body --silent --show-error "https://rpc.graphops.xyz/v1/evm/1" \
  -H "Authorization: Bearer ${GRAPHOPS_API_KEY}" \
  -H "Content-Type: application/json" \
  --data '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "eth_getTransactionReceipt",
    "params": ["0x<TRANSACTION_HASH>"]
  }'

```

Output

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "type": "0x0",
    "status": "0x1",
    "cumulativeGasUsed": "0x5208",
    "logs": [],
    "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "transactionHash": "0x536129e5bd563374f67d3131b7edb31cd9b95eaa89526b23f3de05ca11592dc5",
    "transactionIndex": "0x0",
    "blockHash": "0x48ba51e86cbc56519a4303a92eaa607a985abddfe40a0171b8c7b37e0e5a8459",
    "blockNumber": "0x1",
    "gasUsed": "0x5208",
    "effectiveGasPrice": "0x3b9aca00",
    "blobGasPrice": "0x1",
    "from": "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266",
    "to": "0x70997970c51812dc3a010c7d01b50e0d17dc79c8",
    "contractAddress": null
  }
}
```

0x1 succeeded; 0x0 reverted but still paid gas

The hash you stored

Recheck this if the block reorganizes

The block that included it

Gas cost is gasUsed times this

A receipt shows inclusion, not finality. For payments, settlement, or other irreversible steps,
wait until the receipt's block is at or behind the `safe` or `finalized` block your workflow
requires. If the block is reorganized, the transaction can return to the pending pool or be
included in a different block, so check the receipt again before acting on it.

## Retry guidance

Retrying the exact same signed transaction is usually safe because the transaction hash is
deterministic. Do not mutate gas, nonce, or value and retry blindly.

| Message                               | Meaning                                       | Action                                               |
| ------------------------------------- | --------------------------------------------- | ---------------------------------------------------- |
| `already known`                       | A node already has the transaction.           | Track the transaction hash and poll for a receipt.   |
| `nonce too low`                       | The nonce has already been used.              | Check whether the transaction was mined or replaced. |
| `replacement transaction underpriced` | A replacement transaction did not pay enough. | Reprice according to wallet policy.                  |
| `insufficient funds`                  | Sender cannot cover value plus gas.           | Surface the error to the funding workflow.           |

## Next steps

* [Retries and reorgs](./retries-and-reorgs): Decide which failures to retry and how to recover from a reorganization.
* [Supported methods](../methods#transactions): Check parameters and results for transaction and receipt methods.
* [API integration](../api#error-responses): Interpret JSON-RPC error codes and HTTP statuses.