eth_sendRawTransaction: Broadcast a Signed Transaction
eth_sendRawTransaction•Write method•Ethereum JSON-RPCeth_sendRawTransaction is the only way transactions reach Ethereum: it takes a fully signed transaction as one hex blob and hands it to the node, which validates it and gossips it into the mempool. Wallets, bots and every library's sendTransaction ultimately funnel through this method.
Try eth_sendRawTransaction
curl -s https://ethereum-rpc.publicnode.com \
-X POST \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "eth_sendRawTransaction",
"params": [
"0xSIGNED_RAW_TX"
]
}'This one cannot be demoed with a canned payload: the request needs a freshly signed transaction, and broadcasting an example verbatim would only return an error like "invalid sender" or "nonce too low". Copy the curl command, swap in the output of your library's signTransaction, and it runs as-is.
What eth_sendRawTransaction does
You build and sign the transaction locally: nonce, fee fields, gas limit, recipient, value and calldata are RLP-encoded (legacy) or packed into a typed EIP-2718 envelope (0x02 for EIP-1559 fee-market transactions), then signed with your private key. The node never sees the key; it receives finished bytes.
On receipt the node runs static checks: valid signature, matching chain id, nonce not below the account's, balance covering value plus the gas ceiling, and fees meeting its minimums. If those pass, the transaction enters the node's mempool and is announced to peers. The method returns the transaction hash immediately; inclusion in a block happens later, or never, which is why every serious pipeline follows up with eth_getTransactionReceipt.
Its sibling eth_sendTransaction asks the node to sign with an account it manages. Hosted and production nodes hold no keys, so outside local dev chains like Anvil and Hardhat you will always sign locally and broadcast with eth_sendRawTransaction.
Parameters
| # | Name | Type | Description |
|---|---|---|---|
| 1 | signedTransaction | string | The signed transaction as a hex string: a typed envelope (0x02... for EIP-1559) or legacy RLP. Everything, including the signature, is inside this blob. |
Replace 0xSIGNED_RAW_TX in the example with the hex output of your library's signTransaction. A real signed transaction is several hundred bytes and single-use, so a hardcoded example would only ever return an error.
What it returns
The 32-byte transaction hash as a hex string. The hash is computable from the signed bytes alone, so getting it back proves only that the node accepted the transaction into its mempool, not that it will be mined.
On rejection you get a JSON-RPC error whose message names the failed check; the common ones are covered in the gotchas below.
Example response
{
"jsonrpc": "2.0",
"id": 1,
"result": "0x5c65708b2b76bbeee2d7bb18a405101dbaa6ff42fa627a5c2c1c0dcbcd5cfefa"
}The result is the transaction hash. Store it and poll eth_getTransactionReceipt; a null receipt means not mined yet.
eth_sendRawTransaction with ethers.js
import { JsonRpcProvider, Wallet, parseEther } from "ethers";
const provider = new JsonRpcProvider("https://ethereum-rpc.publicnode.com");
const wallet = new Wallet(process.env.PRIVATE_KEY, provider);
// signs locally, then submits via eth_sendRawTransaction
const tx = await wallet.sendTransaction({
to: "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045",
value: parseEther("0.01"),
});
console.log("hash:", tx.hash);
const receipt = await tx.wait();
console.log("mined in block", receipt.blockNumber);Gotchas and common errors
A hash is not a confirmation
The method resolves as soon as the transaction sits in the local mempool. It can still be dropped, evicted, replaced, or simply never picked up by a block builder. Poll eth_getTransactionReceipt (or use your library's tx.wait()), treat a null receipt as "not mined yet", and decide on a fee bump or rebroadcast after a deadline of your choosing.
"nonce too low" and invisible gaps
An account's transactions execute in strict nonce order. A nonce below the account's current count is rejected with "nonce too low"; a nonce above it is accepted but parked in the queued pool, invisible to block builders until the gap fills. A bot that crashes mid-sequence and resumes from a stale nonce strands everything behind the gap. Comparing eth_getTransactionCount at "latest" versus "pending" shows how deep the backlog is.
"replacement transaction underpriced"
Re-sending with the same nonce is how you replace or cancel a pending transaction, but nodes only accept the replacement if it bumps both maxFeePerGas and maxPriorityFeePerGas by a minimum margin, around 10% by Geth's default. To cancel, send a 0-ETH transfer to yourself at the same nonce with bumped fees and let it mine instead of the original.
The pre-checks that reject you
"insufficient funds" means the balance cannot cover value plus gasLimit times maxFeePerGas; it is checked against that ceiling, not the eventual actual cost. "max fee per gas less than block base fee" means your fee cap sits below the current base fee. "invalid sender" is usually a chain id mismatch: the signature encodes the chain id (EIP-155), so a transaction signed for mainnet breaks on other networks and vice versa. "already known" just means the node already has these exact bytes; when rebroadcasting, it is safe to ignore.
Where you broadcast from matters
The transaction propagates peer-to-peer starting from the node you submit to, and every hop adds latency before builders see it. For time-sensitive flows like arbitrage, liquidations or sniping, submitting through a distant shared gateway can cost the block. Code co-located with its own node skips the client-to-node internet leg entirely and starts propagation from a well-connected mainnet node.
What eth_sendRawTransaction costs on BLAZED.sh
eth_sendRawTransaction costs 1 credit on BLAZED.sh like other standard methods, and the 32-byte hash response never triggers the large-response surcharge. Send-heavy workloads care less about the credit and more about the path: a signed transaction leaving a co-located container reaches the node over a local socket in under 10ms and propagates from there, instead of first crossing the public internet to a shared gateway.
See the full credit price listeth_sendRawTransaction: frequently asked questions
What is the difference between eth_sendRawTransaction and eth_sendTransaction?
eth_sendRawTransaction submits a transaction you signed locally; eth_sendTransaction asks the node to sign with a key it manages. Hosted and production nodes hold no keys, so eth_sendTransaction only works on dev chains and unlocked local nodes.
Why do I get "already known"?
The node already has these exact transaction bytes in its mempool. It is harmless when rebroadcasting for reliability. If you meant to replace the transaction, you need the same nonce with maxFeePerGas and maxPriorityFeePerGas bumped, which changes the bytes.
How do I know when the transaction is mined?
Poll eth_getTransactionReceipt with the returned hash, or use your library's wait helper. A null receipt means pending or dropped; once mined, the receipt's status field is 0x1 for success and 0x0 for a revert.
Can I cancel a pending transaction?
Before it mines, yes: send a 0-ETH transfer to your own address at the same nonce with both fee fields bumped at least around 10%. Whichever transaction mines first wins; once one is in a block, the other is invalid forever.
Does eth_sendRawTransaction work over WebSocket?
Yes, the method behaves identically over HTTP and WebSocket. On BLAZED.sh both transports follow the same credit model, and WebSocket lets you subscribe to newHeads on the same connection to watch for inclusion.
Call eth_sendRawTransaction from the node itself
Deploy your container or script next to a synced Ethereum node. No rate limits, no compute units, sub-10ms local RPC.