TriportRPC

Reading BNB Smart Chain pending transactions: txpool, subscriptions and their limits

How to see what is waiting to be mined on BSC with the three txpool methods, what the pending and queued maps mean, and a polling loop whose request budget you can work out on paper.

"Show me pending BSC transactions" sounds like one request. In practice it is three decisions: which view of the pool you read, how often you read it, and what you conclude when a transaction you saw is gone. This post walks through the three txpool_* methods on BNB Smart Chain, the difference between pending and queued, the push-versus-poll trade-off, and a polling loop sized against a real per-second budget.

What a node's txpool is, and why no two nodes agree

A transaction that has been broadcast but not yet included in a block sits in the memory of whichever nodes have heard about it. Each node keeps its own pool, admits and evicts transactions by its own rules, and hears about transactions in its own order. There is no global mempool to ask for; there are only nodes, each with a partial, moving picture.

Triport's BSC reference says this plainly for all three methods: the snapshot "belongs to the selected upstream node and is not a canonical or pool-wide mempool view." Two consequences follow for any code you write:

  • Absence proves nothing. A transaction missing from one node's pool may be sitting in another node's pool, or may already be in a block.
  • Consecutive reads may not be comparable. The reference describes each answer as coming from the selected node and does not promise that two calls reach the same one, so a diff between two snapshots mixes real pool changes with differences between nodes.

Keep both in mind; the loop below is built around them.

Pending vs queued: nonce gaps

Every txpool_* answer on BSC has two top-level maps, pending and queued, each keyed first by sender address and then by nonce. The go-ethereum documentation for the same namespace describes pending as the transactions "currently pending for inclusion in the next block(s)" and queued as the ones "being scheduled for future execution only."

The dividing line is the sender's nonce. A go-ethereum pull request (29034) describes the legacy pool as "the pending set, containing all the executable transactions (no nonce gaps) and the queued set, containing a mixed bag of everything that's missing a nonce." If an account's next expected nonce is 41 and the node holds its transactions with nonces 41, 42 and 44, then 41 and 42 are pending and 44 is queued until 43 arrives.

For monitoring this matters in two ways:

  1. Queued is not "about to land". A queued transaction cannot execute until its gap is filled, and it may never be.
  2. Pending is ordered per sender, not globally. The nonce map tells you the order in which one account's transactions can execute; it says nothing about how the block producer will order different senders.

txpool_status → txpool_inspect → txpool_content

The three methods read the same pool at three levels of detail. All three take an empty params array, all three are on the bsc_read_rpc budget, and all three are observational.

MethodReturnsUse it for
txpool_statusTwo counters, pending and queued, as hex quantitiesA cheap size signal: is the pool growing or draining
txpool_inspectCompact string summaries per sender and nonceA human-readable overview without full objects
txpool_contentFull transaction objects per sender and nonceAnything that needs the hash, calldata or gas fields

A request is the same shape for all three:

curl https://triport.io/rpc/bsc \
  -H "x-token: $TRIPORT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":1,"method":"txpool_content","params":[]}'

The cost difference between them is not in the rate limit — each is one request against the same bucket — but in the response. txpool_status is two numbers. txpool_content is every transaction the node holds, with full objects, so its size grows with the pool. When we called txpool_status on /rpc/bsc on 2026-09-22 it answered with the two counters; the numbers themselves are per node and per moment, so we do not quote them as "the size of the BSC mempool".

Push vs poll

On an Ethereum-style node the push alternative is a WebSocket subscription to newPendingTransactions, which streams hashes (or full bodies) as the node admits them. Push has real advantages: no polling interval, no repeated transfer of transactions you have already seen.

On Triport, that option does not exist for BSC today. The streaming overview lists BSC Pub/Sub as "planned; not mounted", and a WebSocket upgrade to /ws/bsc answered 404 when we checked on 2026-09-22. So on this gateway the design is polling. Even where push exists, it inherits the same one-node limitation: a subscription tells you what that node admitted, and a missed notification during a reconnect is simply lost. Polling a snapshot has one property push does not: every read is a complete picture of the node's pool at that moment, so a missed read costs you one interval, not an unknown number of events.

A polling loop sized to an RPS budget

The loop keeps a set of hashes seen in the previous snapshot and compares it with the next one.

let previous = new Map<string, { from: string; nonce: string }>();


async function tick() {
  const { result } = await rpc("txpool_content", []);
  const current = new Map<string, { from: string; nonce: string }>();
  for (const [from, byNonce] of Object.entries(result.pending)) {
    for (const [nonce, tx] of Object.entries(byNonce as Record<string, { hash: string }>)) {
      current.set(tx.hash, { from, nonce });
    }
  }
  for (const [hash, meta] of current) if (!previous.has(hash)) onAppeared(hash, meta);
  for (const [hash, meta] of previous) if (!current.has(hash)) onDisappeared(hash, meta); // check a receipt
  previous = current;
}

onDisappeared should not conclude "mined" or "dropped". It should look the hash up with eth_getTransactionReceipt (or eth_getTransactionByHash), which are on the same bsc_read_rpc budget, and decide from that.

Now the arithmetic. BSC's bsc_read_rpc limits on Triport are 15 / 20 / 100 / 250 RPS on free / basic / pro / business. These are default, unmeasured values in the tier matrix, product-policy defaults rather than a BSC load-test result. Bursts are tolerated up to 2× sustained; above that you get 429 rate_limited with Retry-After: 1, and there is no daily quota.

Take an assumed design, not a measurement: one txpool_status per second as a cheap heartbeat, one txpool_content every 2 seconds, and receipt lookups for hashes that disappear, at an assumed 5 per second on average.

CallsRequests per second
txpool_status, every second1
txpool_content, every 2 seconds0.5
Receipt lookups (assumed average)5
Total6.5

6.5 requests per second fits inside the free and basic budgets (15 and 20) with room left, and a spike of receipt lookups after a large block can borrow from the 2× burst. The number that will actually break this loop is not the rate: it is the receipt lookups, because they scale with pool turnover, which you do not control. Two ways to keep them bounded:

  • Batch by block, not by hash. When a new block arrives (eth_blockNumber changes), read its transactions once with eth_getBlockByNumber and remove every included hash from your "disappeared" set in one request.
  • Cap and queue. Put disappeared hashes on a queue and drain it at a fixed rate below your budget, so a busy block delays the answers rather than earning a 429.

If you move from basic to pro, the budget rises from 20 to 100 RPS; that buys a shorter txpool_content interval, not a more complete view, because the view is still one node's.

What you can't learn from one node's pool

  • Whether a transaction will be included. Pending means executable by nonce at that node, not accepted by the producer.
  • Ordering across senders. The pool is grouped by sender and nonce; block order is decided elsewhere.
  • Network-wide totals. Counters from txpool_status are one node's, at one moment.

When this is the wrong approach

  • You need to submit transactions. Triport's BSC surface is observational only: the reference states that Triport "does not expose BSC transaction submission". Reading the pool here and sending elsewhere is a valid split; expecting both from this endpoint is not.
  • You need a push feed. BSC WebSocket pub/sub is planned and not mounted on Triport. If a stream of pending hashes is a hard requirement, this is not the endpoint for it yet.
  • You need confirmed data only. If the job is "tell me when a deposit is final", skip the pool entirely and follow new blocks and receipts; the pending layer adds noise and no certainty.
  • You need a network-wide mempool. No single node, ours included, gives you that.

Sources

  1. Triport, txpool_content on BSC: pending and queued maps by sender and nonce, node-local snapshot, no BSC submission, bsc_read_rpc 15/20/100/250 RPS (default, unmeasured).
  2. Triport, txpool_status on BSC and txpool_inspect on BSC: counters and summaries, same budget.
  3. Triport, BSC limits: categories and the note that the values are unmeasured product-policy defaults.
  4. Triport, Rate limits and tiers: 2× burst, 429 with Retry-After: 1, no daily quota.
  5. Triport, streaming overview: BSC Pub/Sub "planned; not mounted". Production check 2026-09-22 on the EU and CA origins: txpool_status on /rpc/bsc answered; the /ws/bsc upgrade answered 404 on both.
  6. go-ethereum, txpool namespace — https://geth.ethereum.org/docs/interacting-with-geth/rpc/ns-txpool, read 2026-09-22.
  7. go-ethereum pull request 29034 (description of the legacy pool's pending and queued sets) — https://github.com/ethereum/go-ethereum/pull/29034, read 2026-09-22.