Most Accurate Method: Get the highest precision priority fee estimates by analyzing your exact transaction. Recommended for production applications where accuracy matters most.

Overview

Serialized transactions provide the most accurate fee estimates because the API can analyze the exact accounts and operations that will be performed in your transaction.

Why Use Serialized Transactions

  • Highest accuracy - Analyzes exact operations
  • Detailed analysis - Instruction-specific patterns
  • Realistic estimates - Reflects actual transaction
  • Production-ready - Built for critical applications

Best For

  • Production applications
  • Complex transactions
  • Critical operations
  • Maximum accuracy needed

Advantages Over Account Keys

Instruction-Specific Analysis

The API can analyze specific operations and their historical fee patterns, not just account activity.

Transaction Size Awareness

Considers the actual size and complexity of your transaction for more accurate estimates.

Read-Only Account Handling

Better analysis of both writable and read-only accounts in their transaction context.

Implementation Guide

1

Build Your Transaction

Create your transaction with all instructions (except priority fee)

2

Serialize the Transaction

Convert your transaction to a serialized format

3

Get Fee Estimate

Call the Priority Fee API with your serialized transaction

4

Apply Priority Fee

Add the priority fee instruction and send your transaction

Quick Start Example

import { 
  Connection, 
  PublicKey, 
  Transaction, 
  SystemProgram, 
  ComputeBudgetProgram 
} from "@solana/web3.js";
import bs58 from "bs58";

const connection = new Connection("https://mainnet.helius-rpc.com/?api-key=YOUR_API_KEY");

// 1. Build your transaction (without priority fee)
const transaction = new Transaction();
const transferIx = SystemProgram.transfer({
  fromPubkey: senderKeypair.publicKey,
  toPubkey: recipientPublicKey,
  lamports: 1000000, // 0.001 SOL
});
transaction.add(transferIx);

// 2. Set required fields and serialize
transaction.recentBlockhash = (await connection.getLatestBlockhash()).blockhash;
transaction.feePayer = senderKeypair.publicKey;
const serializedTx = bs58.encode(transaction.serialize());

// 3. Get priority fee estimate
const priorityFee = await getPriorityFeeEstimate(connection, serializedTx, "Medium");

// 4. Add priority fee and send
transaction.instructions = []; // Reset
transaction.add(ComputeBudgetProgram.setComputeUnitPrice({ microLamports: priorityFee }));
transaction.add(transferIx);
transaction.recentBlockhash = (await connection.getLatestBlockhash()).blockhash;
transaction.sign(senderKeypair);

Core Implementation Function

Here’s a reusable function for getting priority fee estimates from serialized transactions:

async function getPriorityFeeEstimate(connection, serializedTransaction, priorityLevel = "Medium") {
  const response = await fetch(connection.rpcEndpoint, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({
      jsonrpc: "2.0",
      id: "1",
      method: "getPriorityFeeEstimate",
      params: [{
        transaction: serializedTransaction,
        options: { 
          priorityLevel: priorityLevel,
          recommended: true 
        }
      }]
    })
  });
  
  const result = await response.json();
  
  if (result.error) {
    throw new Error(`Fee estimation failed: ${JSON.stringify(result.error)}`);
  }
  
  return result.result.priorityFeeEstimate;
}

Complete Implementation Examples

Advanced Configuration Options

Best Practices

Transaction Serialization

Always serialize your actual transaction, not a simplified version

// ✅ Good - serialize actual transaction
const transaction = new Transaction();
transaction.add(actualInstruction1);
transaction.add(actualInstruction2);
const serialized = bs58.encode(transaction.serialize());

Instruction Order

Include all instructions except the priority fee in your estimation transaction

// ✅ Good - all business logic included
transaction.add(createAccountIx);
transaction.add(initializeIx);
transaction.add(transferIx);

// ❌ Don't include priority fee in estimation

Error Handling Strategies

class SerializedTransactionFeeEstimator {
  constructor(connection) {
    this.connection = connection;
    this.fallbackFee = 10000; // 10k micro-lamports
  }

  async getEstimate(serializedTransaction, priorityLevel = "Medium") {
    try {
      // Primary attempt with serialized transaction
      return await this.getPrimaryEstimate(serializedTransaction, priorityLevel);
    } catch (error) {
      console.warn("Serialized transaction estimate failed:", error.message);
      
      // Fallback to account-based estimation
      try {
        return await this.getFallbackEstimate(serializedTransaction, priorityLevel);
      } catch (fallbackError) {
        console.warn("Fallback estimate failed:", fallbackError.message);
        return this.getFallbackFee(priorityLevel);
      }
    }
  }

  async getPrimaryEstimate(serializedTransaction, priorityLevel) {
    const response = await fetch(this.connection.rpcEndpoint, {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({
        jsonrpc: "2.0",
        id: "1",
        method: "getPriorityFeeEstimate",
        params: [{
          transaction: serializedTransaction,
          options: { 
            priorityLevel: priorityLevel,
            recommended: true 
          }
        }]
      })
    });

    const result = await response.json();
    if (result.error) {
      throw new Error(result.error.message);
    }
    
    return result.result.priorityFeeEstimate;
  }

  async getFallbackEstimate(serializedTransaction, priorityLevel) {
    // Extract account keys from transaction and use account-based estimation
    const transaction = Transaction.from(bs58.decode(serializedTransaction));
    const accountKeys = transaction.instructions
      .flatMap(ix => [ix.programId, ...ix.keys.map(k => k.pubkey)])
      .map(key => key.toString());

    const uniqueAccountKeys = [...new Set(accountKeys)];
    
    // Use account-based estimation as fallback
    const response = await fetch(this.connection.rpcEndpoint, {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({
        jsonrpc: "2.0",
        id: "1",
        method: "getPriorityFeeEstimate",
        params: [{
          accountKeys: uniqueAccountKeys,
          options: { 
            priorityLevel: priorityLevel,
            recommended: true 
          }
        }]
      })
    });

    const result = await response.json();
    if (result.error) {
      throw new Error(result.error.message);
    }
    
    return result.result.priorityFeeEstimate;
  }

  getFallbackFee(priorityLevel) {
    const fallbacks = {
      "Low": 1000,
      "Medium": 5000,
      "High": 15000,
      "VeryHigh": 50000
    };
    
    return fallbacks[priorityLevel] || 5000;
  }
}

// Usage
const estimator = new SerializedTransactionFeeEstimator(connection);
const fee = await estimator.getEstimate(serializedTransaction, "High");

Common Issues & Solutions

When to Use vs Account Keys

Use Serialized Transactions

Production applications

  • Maximum accuracy required
  • Complex multi-instruction transactions
  • Critical operations
  • Performance-sensitive applications

Development scenarios

  • Final integration testing
  • Performance optimization
  • Production deployment

Use Account Keys

Development & prototyping

  • Quick estimates during development
  • Simple transactions
  • Pre-transaction planning
  • Architecture constraints preventing serialization

Analysis scenarios

  • Account-level fee pattern analysis
  • Batch account analysis
  • Quick market research