πŸš₯Priority Fee API

Additional fees paid to prioritize transactions in the network

What Are Priority Fees on Solana?

Priority fees are optional fees you can add to your transactions to incentivize block producers (leaders) to include your transaction in the next block. You should include a priority fee in your transactions, but how much should you pay?

Learn more about Solana's transaction fees and priority fee mechanics here.

Helius Priority Fee API

Helius' getPriorityFeeEstimate is a RPC method that provides fee recommendations based on historical data. Most importantly, it considers both global and local fee markets.

The estimate is priced in micro-lamports per compute unit.

To use this API, you must provide one of the following in the params:

  • transaction: A serialized, signed transaction. (recommended)

  • accountKeys: A list of account keys involved in the transaction.

We recommend passing a transaction instead of accountKeys for more accurate results. accountKeys provide reliable fee estimates only for writable accounts.

Optional Parameters

  • transactionEncoding: Specifies the encoding of the transaction. Defaults to Base58, but can be set to formats like Base64.

  • priorityLevel: Allows you to set a custom priority level for the fee. The levels and their corresponding percentiles are:

    • Min: 0th percentile

    • Low: 25th percentile

    • Medium: 50th percentile

    • High: 75th percentile

    • VeryHigh: 95th percentile

    • UnsafeMax: 100th percentile (use with caution).

  • includeAllPriorityFeeLevels: If enabled, the API will return fee estimates for all priority levels.

  • lookbackSlots: Determines how many recent slots are analyzed to calculate the fee. Default is 50, with a range of 1–300.

  • includeVote: If true, it includes vote transactions for the fee calculation.

  • recommended: Provides a default recommended fee based on the median (50th percentile), excluding vote transactions.

  • evaluateEmptySlotAsZero: When enabled, the API treats empty blocks as zero-fee slots, lowering fee estimates during low congestion and preventing overpayment during minimal network activity.

If the recommended option is set to true, the API returns a priority fee of 10,000 if the median is less than this value. If the median exceeds 10,000, the actual median fee is returned. This ensures a minimum baseline fee for your transactions.

Fetching Priority Fees via Serialized Transaction

Example payload using an base58/base64 encoded serialized transaction:

{
  "jsonrpc": "2.0",
  "id": "helius-example",
  "method": "getPriorityFeeEstimate",
  "params": [
    {
      "transaction": "LxzhDW7TapzziJVHEGPh1QjmZB6kjNqmvuJ9gmihBGEkzw2N8ospDq32UZdVmKWzRZqKuyvhp7xmLvsmmHa3MfxVKpYoLV9cPkw5isHnHgDUwLSMsDaZ4dLEULexXAvuV9k6fLD2LMhFKM6gqH6j69GQLAm1g4e3z5ZVZN6pmJdSmZ4PqKcwNn4sS7E3Abp1hV59uBJB9i4jEdEAh28rZ8WCeNizzEmrJZFhatFFFGSDsk23jDPEjbkMcoRWXKf1WthFScC2S6Wz284Dtjqp7kW8eybV3DpmN9DtBbcfFNQPtUwmiBZCKszdYym5AjJvRHiUKUdMwFpC3toPnMvTmKZ9iHQtzZRkg5xBufRtUN5eVYJfdw2DxzH6RfqhC2rAjfSbfJA4wmaq9f5eUe2iEjmqvne6r85PPcHBJzyqkVtAyUFtTc9DfU8UiM9rFhQ2UB71M6m5UCsC6EwowMw5k8iF8rL7iPCLdubVdy8S58DPLNG4E4rdosev1T63YdRWSgEwBZh7iX4zGBA9AKAm3B9itCSxLSL46Y6uZgVku9UXsMcWWz3URoa6hRbo55CYPLhrVeqjqX5DsXpaG6QkngbEaESQUnkakE1AFkYXmNXEd2m3sLRTYB2o5UTkwkJS2u5sJHyYqAvXr3vFZr7qAYFLD3LwS5tsdb45ZQVQwsbnNddMHgkpmSqLHS6Fv1epxqHbzpRCU1tgMsriApVWC3pj2LLD41HxkV8aKvkVyHgJFACUtbvRBZAasHf1F71Gz3qZNTdh3efWsZJRXnfxKPbATU7NTMaMUL8JjknfoVwp3",
      "options": {
        "recommended": true
      }
    }
  ]
}

How do you get serialized transactions?

Via Web3.js 1.0

To obtain a serialized transaction, use the transaction.serialize() method followed by encoding it to Base58 format.

const response = await fetch(HeliusURL, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({
      jsonrpc: "2.0",
      id: "1",
      method: "getPriorityFeeEstimate",
      params: [
        {
          transaction: bs58.encode(transaction.serialize()), // Pass the serialized transaction in Base58
          options: { priorityLevel: priorityLevel },
        },
      ],
    }),
  });
 const result = await response.json();
Via Web3.js 2.0

The getBase64EncodedWireTransaction method simplifies this process by providing a serialized transaction directly in Base64 encoding. Ensure the transactionEncoding is explicitly set to base64 in the API payload.

const response = await fetch(HELIUS_URL, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
            jsonrpc: '2.0',
            id: 'helius-example',
            method: 'getPriorityFeeEstimate',
            params: [{
                transaction: getBase64EncodedWireTransaction(signedTransaction),
                options: { 
                    transactionEncoding: "base64",
                    recommended: true,
                 }
            }]
        }),
});
const result = await response.json();

Fetching Priority Fees via Account Keys

Alternatively, you can provide a list of account keys.

{
    "jsonrpc": "2.0",
    "id": "1",
    "method": "getPriorityFeeEstimate",
    "params": [{
        "accountKeys": ["2CiBfRKcERi2GgYn83UaGo1wFaYHHrXGGfnDaa2hxdEA"],
        "options": {
            "includeAllPriorityFeeLevels": true
        }
    }]
}
Via Web3.js 1.0

The following example demonstrates how to extract account keys from a transaction and include them in the API request:

Here, we extract all the account keys from the transaction using compileMessage, parse the output, and then pass all the public keys to the API.

It is crucial to extract and provide all relevant account keys because the local fee market is influenced by the number of transactions trying to interact with specific accounts. Providing all account keys allows the API to accurately assess this local market condition.

const { Transaction, Connection, ComputeBudgetProgram, PublicKey, TransactionInstruction } = require('@solana/web3.js');

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

(async function getPriorityFeeEst() {
  const transaction = new Transaction();

  // A sample instruction to the transaction
  transaction.add(
    new TransactionInstruction({
        programId: new PublicKey("MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr"),
        data: Buffer.from("Experimenting", "utf8"),
        keys: [],
    })
  )

  transaction.recentBlockhash = (await connection.getLatestBlockhash()).blockhash;
  transaction.feePayer = new PublicKey("A3r2FvL1BkAGmcv74vQ5fVpFdm7GttDKhn9RhYV3zifL");

  // Extract all account keys from the transaction
  const accountKeys = transaction.compileMessage().accountKeys;

  // Convert PublicKeys to base58 strings
  const publicKeys = accountKeys.map(key => key.toBase58());

  try {
    const response = await fetch(connection.rpcEndpoint, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        jsonrpc: '2.0',
        id: 'helius-example',
        method: 'getPriorityFeeEstimate',
        params: [
          {
            accountKeys: publicKeys,
            options: {
              recommended: true,
            },
          }
        ],
      }),
    });

    const data = await response.json();

    const priorityFeeEstimate = data.result?.priorityFeeEstimate;

    // Add the priority fee to the transaction
    transaction.add(
      ComputeBudgetProgram.setComputeUnitPrice({
          microLamports: priorityFeeEstimate
      }),
    )

    console.log("Estimated priority fee:", priorityFeeEstimate);
    return priorityFeeEstimate;
  } catch (err) {
    console.error(`Error: ${err}`);
    return 10_000;
  }
})();

Examples

Sending a transaction with the Priority Fee API (Javascript)

This code snippet showcases how one can transfer SOL from one account to another. This code passes the transaction to the priority fee API, which determines the specified priority fee from all the accounts involved.

const {
  Connection,
  SystemProgram,
  Transaction,
  sendAndConfirmTransaction,
  Keypair,
  ComputeBudgetProgram,
} = require("@solana/web3.js");
const bs58 = require("bs58");

const HeliusURL = "https://mainnet.helius-rpc.com/?api-key=<YOUR_API_KEY>";
const connection = new Connection(HeliusURL);
const fromKeypair = Keypair.fromSecretKey(Uint8Array.from("[Your secret key]")); // Replace with your own private key
const toPubkey = "CckxW6C1CjsxYcXSiDbk7NYfPLhfqAm3kSB5LEZunnSE"; // Replace with the public key that you want to send SOL to

async function getPriorityFeeEstimate(priorityLevel, transaction) {
  const response = await fetch(HeliusURL, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({
      jsonrpc: "2.0",
      id: "1",
      method: "getPriorityFeeEstimate",
      params: [
        {
          transaction: bs58.encode(transaction.serialize()), // Pass the serialized transaction in Base58
          options: { priorityLevel: priorityLevel },
        },
      ],
    }),
  });
  const data = await response.json();
  console.log(
    "Fee in function for",
    priorityLevel,
    " :",
    data.result.priorityFeeEstimate
  );
  return data.result;
}
async function sendTransactionWithPriorityFee(priorityLevel) {
  const transaction = new Transaction();
  const transferIx = SystemProgram.transfer({
    fromPubkey: fromKeypair.publicKey,
    toPubkey,
    lamports: 100,
  });
  transaction.add(transferIx);

  let feeEstimate = { priorityFeeEstimate: 0 };
  if (priorityLevel !== "NONE") {
    feeEstimate = await getPriorityFeeEstimate(priorityLevel, transaction);
    const computePriceIx = ComputeBudgetProgram.setComputeUnitPrice({
      microLamports: feeEstimate.priorityFeeEstimate,
    });
    transaction.add(computePriceIx);
  }
  
  transaction.recentBlockhash = (
    await connection.getLatestBlockhash()
  ).blockhash;
  transaction.sign(fromKeypair);

  try {
    const txid = await sendAndConfirmTransaction(connection, transaction, [
      fromKeypair,
    ]);
    console.log(`Transaction sent successfully with signature ${txid}`);
  } catch (e) {
    console.error(`Failed to send transaction: ${e}`);
  }
}

sendTransactionWithPriorityFee("High"); // Choose between "Min", "Low", "Medium", "High", "VeryHigh", "UnsafeMax"
Empty Slot Evaluation

The evaluateEmptySlotAsZero flag helps optimize priority fee calculations for accounts with sparse transaction history. When true, this mechanism smooths out fee estimation by treating slots with no transactions for a particular account as having zero fees, rather than excluding them from the calculation.

To implement, add the evaluateEmptySlotAsZero flag to your options parameter:

const response = await fetch(HELIUS_API_BASE, {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    id: '1',
    method: 'getPriorityFeeEstimate',
    params: [{
      transaction: base58EncodedTransaction,
      options: {
        recommended: true,
        evaluateEmptySlotAsZero: true
      }
    }]
  })
});

Request all priority fee levels

Payload

{
    "jsonrpc": "2.0",
    "id": "1",
    "method": "getPriorityFeeEstimate",
    "params": [{
        "accountKeys": ["2CiBfRKcERi2GgYn83UaGo1wFaYHHrXGGfnDaa2hxdEA"],
        "options": {
            "includeAllPriorityFeeLevels": true
        }
    }]
}

Response

{
    "jsonrpc": "2.0",
    "result": {
        "priorityFeeLevels": {
            "min": 0.0,
            "low": 2.0,
            "medium": 10082.0,
            "high": 100000.0,
            "veryHigh": 1000000.0,
            "unsafeMax": 50000000.0
        }
    },
    "id": "1"
}

Request the high priority level

Payload

{
    "jsonrpc": "2.0",
    "id": "1",
    "method": "getPriorityFeeEstimate",
    "params": [{
        "accountKeys": ["2CiBfRKcERi2GgYn83UaGo1wFaYHHrXGGfnDaa2hxdEA"],
        "options": {
            "priorityLevel": "High"
        }
    }]
}

Response

{
    "jsonrpc": "2.0",
    "result": {
        "priorityFeeEstimate": 120000.0
    },
    "id": "1"
}

Version 2 (v2)

The v2 logic aligns more closely with the Agave scheduler's approach to prioritizing transactions. This updated algorithm is now the default for the current API.

Updated Algorithm

The new algorithm for priority_estimate is defined as:

priority_estimate(p: Percentile, accounts: Accounts) =  
    max(  
        percentile(txn_fees, p),  
        percentile(writable_account_fees(account1), p),  
        percentile(writable_account_fees(account2), p),  
        ...,  
        percentile(writable_account_fees(accountN), p)  
    )  

This approach ensures the fee estimate considers the maximum priority fee across all relevant accounts and transaction fees at the given percentile.

Key Differences from v1

  • Individual Account Consideration: Instead of calculating a single percentile for all accounts involved (account_fees(accounts)), v2 calculates the percentile for each account separately.

  • Exclude read-only accounts: Local fee markets kick in when there is a high demand for writing to an account, so the calculation only considers historical fees for writable accounts. If you supply accounts in the request payload, it will assume that all accounts are writable. If you supply a transaction in the request payload, it will only consider the writable accounts in the calculation.

  • Potential for Higher Estimates: In scenarios where one account has significantly higher fees than others, the v2 algorithm is more likely to capture this, potentially resulting in higher priority fee estimates than v1.

How to Access the v1 Algorithm

To use the previous v1 algorithm, include priority-fee-version=1.0 as a query parameter in your API request.

Note: You can confirm that you’re accessing the v1 endpoint by checking the response headers for x-priority-fee-version=1.0.

How Priority Fee API Works

The method uses a set of predefined priority levels (percentiles) to dictate the returned estimate. Users can optionally specify to receive all the priority levels and adjust the window with which these are calculated via lookbackSlots

fn get_recent_priority_fee_estimate(request: GetPriorityFeeEstimateRequest) -> GetPriorityFeeEstimateResponse

struct GetPriorityFeeEstimateRequest {
  transaction: Option<String>,   // estimate fee for a serialized txn
  accountKeys: Option<Vec<String>>, // estimate fee for a list of accounts
  options: Option<GetPriorityFeeEstimateOptions>
}

struct GetPriorityFeeEstimateOptions {
	priorityLevel: Option<PriorityLevel>, // Default to MEDIUM
	includeAllPriorityFeeLevels: Option<bool>, // Include all priority level estimates in the response
	transactionEncoding: Option<UiTransactionEncoding>, // Default Base58
	lookbackSlots: Option<u8>, // The number of slots to look back to calculate the estimate. Valid numbers are 1-150, default is 150
	recommended: Option<bool>, // The Helius recommended fee for landing transactions
	includeVote: Option<bool>, // Include vote transactions in the priority fee estimate calculation. Default to true 
}

enum PriorityLevel {
	Min, // 0th percentile
	Low, // 25th percentile
	Medium, // 50th percentile
	High, // 75th percentile
	VeryHigh, // 95th percentile
  // labelled unsafe to prevent people from using and draining their funds by accident
	UnsafeMax, // 100th percentile 
	Default, // 50th percentile
}

struct GetPriorityFeeEstimateResponse {
  priority_fee_estimate: Option<MicroLamportPriorityFee>
  priority_fee_levels: Option<MicroLamportPriorityFeeLevels>
}

type MicroLamportPriorityFee = f64

struct MicroLamportPriorityFeeLevels {
	min: f64,
	low: f64,
	medium: f64,
	high: f64,
	veryHigh: f64,
	unsafeMax: f64,
}

Calculating the Percentiles (v1)

To calculate the percentiles, we need to consider the global and local fee market over transactions in the last N slots

For example,

priority_estimate(p: Percentile, accounts: Accounts) = 
	max(
		percentile(txn_fees, p), 
		percentile(account_fees(accounts), p)
	)

where txn_fees are the txn_fees from the last 150 blocks, and account_fees(accounts) are the fees for transactions containing these accounts from the last 150 blocks. Here, we are considering the total set of fees seen for accounts and transactions instead of the minimum.

Global Fee Market Estimate

The global fee market estimate is a percentile of priority fees paid for transactions in the last N slots.

Local Fee Market Estimate

The local fee market is influenced by the number of people trying to obtain a lock on an account. We can estimate this similarly to the global fee market but instead use the percentile of fees paid for transactions involving a given account(s). If a user requests an estimate for multiple accounts in the same transaction, we will take the max of the percentiles across those accounts.

Priority Fee Estimate

The priority_fee_estimate will be the max of the global and local fee market estimates.

Extensions

This method could also be integrated into simulateTransaction and returned with the response context. This way, developers using simulateTransaction can eliminate an extra RPC call.

Last updated