On Solana, a commitment level tells you how much of the cluster has agreed with the block your answer came from. Processed means your node executed it. Confirmed means a supermajority of the cluster voted on it. Finalized means it has been rooted, with the full lockout depth of confirmed blocks built on top. A live counter belongs at confirmed; a number you publish belongs at finalized.
The mistake that produces wrong figures is not choosing the wrong level. It is choosing different levels in different parts of one system and then comparing the results. A counter at processed and a reconciliation at finalized will always disagree, and the disagreement is not a bug in either of them.
The three levels, precisely
Processed
Your node has received the block, executed its transactions, and can answer questions about the resulting state. Nothing else is implied. The rest of the cluster may not have seen it, may disagree about it, and may build on a different block entirely. Processed is the earliest possible view and carries the weakest possible claim.
Confirmed
The block has received votes from validators representing a supermajority of stake. This is the mechanism usually described as optimistic confirmation: the cluster has effectively agreed that this block is on the canonical chain, and reversing it would require validators to act against commitments they have already made. It is not the strongest guarantee available, and it is strong enough for almost every live display.
Finalized
The block has been rooted. In addition to the supermajority vote, the maximum lockout depth of confirmed blocks has been built on top of it, which is 31 blocks in the consensus implementation. At this point the network treats the block as settled, and this is the level an RPC node uses when you do not ask for anything else.
What optimistic confirmation actually is
Solana's consensus has validators vote on blocks, and each vote carries a lockout: a commitment not to vote for a competing fork for a certain number of slots, doubling with each additional confirmation. Once votes representing more than two thirds of stake exist for a block, undoing it would require a large fraction of that stake to violate their lockouts, which is both detectable and penalised.
That is why confirmed is treated as practically safe rather than merely probable. It is not a heuristic about how long to wait; it is a statement about what the cluster has committed to. The distinction matters when somebody asks whether they should wait a few extra seconds to be sure, because the answer depends on whether the cost of being wrong is a stale chart or an irreversible action.
The terminology used across the ecosystem is set out in the Solana terminology reference, and the per-method behaviour is documented alongside each call in the RPC API documentation. Both are worth reading directly rather than through a summary, including this one.
Rooting and the lockout depth
Rooting is the step from confirmed to finalized. A validator sets a new root when a block has accumulated the maximum lockout, which the implementation defines as 31 confirmed blocks on top of it. Once rooted, the block is no longer a candidate for reorganisation from that validator's perspective, and older forks below the root can be discarded.
Two protocol constants therefore bound the wait. The target slot duration is 400 milliseconds, and the lockout depth is 31. Multiplying them gives roughly 12.4 seconds as an arithmetic floor. That figure is a calculation from two published constants, not a measurement of any network: slots are skipped, votes take time to propagate, and real finalization is slower than the floor.
Why the floor is still useful
You should never quote it as a latency figure. You should absolutely use it for design. If your product promises a settled number within five seconds, the arithmetic tells you the promise is impossible at finalized commitment, and no amount of provider shopping will change that. Constraints that come from constants are the ones worth discovering early.
The comparison table
| Level | What has happened | Can it still change | Belongs in |
|---|---|---|---|
| processed | Your node executed the block | Yes, it may be on a fork that loses | Preview surfaces, latency research, systems that verify afterwards |
| confirmed | Supermajority of stake has voted on it | Only under conditions that penalise the validators involved | Live counters, alerting, trading panels |
| finalized | Rooted with the full lockout depth above it | Treated as settled | Reporting, accounting, anything quoted back at you |
Read the last column as an instruction rather than a suggestion. Almost every argument about commitment levels dissolves once each surface in the product has been assigned one deliberately, and once the assignment is written down where the next engineer will see it.
Defaults are stricter than people expect
Most JSON-RPC methods default to finalized when you omit the commitment parameter. This is a sensible default for correctness and a surprising one for anybody building a live view, because it means the naive implementation is the slowest one available and nothing in the response says so.
The symptom is specific and recognisable. Somebody builds a panel, notices it trails a public chart by an uncomfortable margin, blames the provider, tries a second provider, sees the same behaviour, and eventually discovers a missing configuration field. The fix is one line, and the diagnosis usually costs days because the delay looks like an infrastructure problem rather than a semantics problem.
The habit that prevents it is to pass commitment explicitly on every call and every subscription, even where the default is what you wanted. An explicit value is documentation: the next person to read the code learns what the figure means without having to know the defaults of a JSON-RPC method they have never used.
What a reversal looks like in your data
A consumer running at processed will occasionally ingest a block that never becomes canonical. Nothing announces this. The stream does not send a retraction, and the events you already counted do not identify themselves as provisional. From inside your database, a phantom trade is indistinguishable from a real one.
What you can do is detect it structurally. A slot that never appears in a confirmed or finalized view of the same range is a slot whose contents should be removed. That comparison requires you to have stored the slot alongside every event, which is the reason slot storage appears in every ingestion checklist on this site.
The alternative, and the reason most systems choose it, is to simply not consume at processed. If you never count anything until it is confirmed, the reversal case becomes rare enough to treat as an anomaly rather than as a routine correction path you have to build and test.
There is a second-order effect worth anticipating. A consumer at processed will occasionally see the same logical activity twice, once on the fork that loses and once on the chain that survives, because a transaction dropped from an abandoned block can be re-included later. Without a deduplication key stable across both appearances, that transaction is counted twice, which is a more damaging outcome than the phantom event it accompanies. Keying on the transaction signature makes both problems disappear at once, since the second appearance simply overwrites the first.
This is the strongest practical argument for building the key before you build the counter. A pipeline whose totals are a function of a keyed event set can survive forks, replays, backfills and reprocessing without any special handling. A pipeline whose totals are a running sum has to detect and reverse each of those situations individually, and each detection is a piece of code that will be wrong at least once.
Choosing a level per surface
Assign a level to each surface, not to the system. A single application usually needs at least two, and the pairing below covers most products without inventing complexity.
- Live counter, ticker, sparkline. Confirmed. Fast enough to feel current, strong enough not to show activity that will vanish.
- Alerting. Confirmed, with a dwell requirement. Alerting at processed produces pages for events that may not have happened.
- Published totals, exports, anything a person quotes. Finalized, produced by a separate pass that overwrites the provisional figure.
- Research into propagation and delay. Processed, because that is the object of study, and the results are never presented as market data.
- Execution systems. Whatever the strategy requires, with the outcome verified independently afterwards, because a submission is not a landing.
The rule that makes this work is disclosure. Any tool that reports activity back to a user, including Solana volume automation reporting on its own run, should state the level its figures were produced at. Without that label, a total cannot be compared with anything, and two accurate systems will look like one of them is lying.
A reconciliation pass, worked through
The arithmetic below is illustrative. It shows the mechanics of a two-level design using round numbers, and describes no real market and no real endpoint.
Say your live counter runs at confirmed and reports 4,120 SOL of activity for the hour just ended. Ten minutes later a reconciliation job re-reads the same slot range at finalized and finds 4,118 SOL. The difference of 2 SOL comes from a small number of events that were counted from a block your node had confirmed but which did not survive into the rooted chain in the form you recorded.
- Freeze the window by slot, not by clock. Record the first and last slot in the hour so both passes cover exactly the same range.
- Run the finalized pass with a lag. Wait comfortably beyond the rooting floor before re-reading, so the range is genuinely settled.
- Recompute rather than adjust. Rebuild the total from the finalized data instead of applying a correction to the provisional one, so errors cannot accumulate.
- Store both figures. Keep the provisional and the settled number with their levels attached, because the difference between them is a health signal.
- Alert on the divergence, not the value. A gap of a couple of units is normal; a gap that grows over days means your live path is drifting and deserves investigation.
The habit worth keeping is the last one. The reconciliation exists to correct the number, but its more valuable output is a continuous measurement of how trustworthy your fast path is, which no single-level design can produce.
Commitment in subscriptions and streams
Subscriptions take a commitment in their configuration object, with the same strict default when it is omitted. A log subscription set to finalized will feel dramatically slower than the same subscription at confirmed, and the only visible difference in your code is a field you did not write.
Geyser-based streams are a special case worth understanding. A plugin receives updates as the validator processes them, which means the stream is closer to processed than to anything else by construction. Products built on Geyser expose slot status alongside the data so that consumers can apply their own commitment logic, and a consumer that ignores those status updates is running at processed whether it intended to or not.
The practical instruction is the same across all transports: record the level every consumer runs at, and treat it as part of the schema rather than as a connection detail. A figure without a level attached is not comparable with anything, including an earlier version of itself.
Mixing levels is the real bug
Almost every commitment-related incident this desk has seen described comes down to one system using two levels without knowing it. A dashboard reads confirmed while its alerting reads finalized, so alerts fire minutes after the chart moved. An exporter reads finalized while the live counter it is compared against reads processed, so the export appears to lose data every time it runs.
Neither component is wrong. The pairing is wrong, and it is invisible because commitment is a parameter rather than a type. Nothing in a JSON response says which level produced it, so the mismatch has to be prevented by convention rather than caught by a compiler.
- Every RPC call and every subscription passes commitment explicitly, including where the default is correct.
- Every stored event carries the slot and the level it was accepted at.
- Every displayed figure states its level somewhere a user can find it.
- Two figures are never compared unless they were produced at the same level over the same slot range.
- Published numbers come from a finalized pass, not from the live counter.
- The divergence between the live and settled totals is monitored as a health signal.
If the delay you are chasing turns out not to be a commitment problem, it is almost certainly a pipeline problem, and the next note maps every stage where one can hide: why dashboards lag. If it turns out to be a hole rather than a delay, start with data gaps and backfill instead.
Questions this desk keeps getting
What is the difference between confirmed and finalized on Solana?
Confirmed means the block has received votes from a supermajority of the cluster, so the network has optimistically agreed it belongs to the canonical chain. Finalized means the block has additionally been rooted, with the full lockout depth of confirmed blocks built on top of it. Confirmed is fast and practically stable; finalized is the strongest promise an RPC node offers and takes measurably longer to arrive.
Which commitment level should a live volume counter use?
Confirmed, in almost every case. Processed is earlier but includes blocks that may sit on a losing fork, which puts activity in your counter that never happened. Finalized is safer and adds enough delay that the panel stops feeling live. The mature pattern is to display confirmed and reconcile against finalized before any figure is published or quoted.
Is confirmed data ever reverted?
It is possible in principle and rare in practice. Optimistic confirmation exists because reverting a block that a supermajority has voted on would require validators to violate their own lockout commitments, which is heavily disincentivised. Treat confirmed as safe for a display counter, and treat finalized as the level you use before making an irreversible claim about a number.
Why does my RPC call seem slower than the chain?
Very often because you did not pass a commitment parameter. Most JSON-RPC methods default to finalized, so a caller who omits the field is asking for the strictest and slowest view without realising it. Setting confirmed explicitly is a one-line change that removes a delay many people spend days attributing to their provider.
How long does finalization take?
Long enough that you should design around the ordering rather than a number. Two protocol constants bound it: the target slot duration is 400 milliseconds and rooting requires 31 confirmed blocks on top. Multiplying those gives an arithmetic floor of roughly 12.4 seconds, which is a calculation rather than a measurement; real conditions with skipped slots and vote propagation take longer.
Does processed commitment have any legitimate use?
Yes, for anything where being early is worth more than being right, and where being wrong is cheap and recoverable. Preview panels, latency research and execution systems that will verify the outcome anyway all have reasons to look at processed. Accounting, reporting and anything a person will quote do not.
Do WebSocket subscriptions use the same commitment levels?
Yes. Subscriptions accept a commitment in their configuration and, like the HTTP methods, default to the strictest setting when you omit it. This produces a specific confusion where a stream feels sluggish for no visible reason. Set the level explicitly on every subscription and record which level each consumer is running at.