Comparison · quantifier
salmon vs kallisto: Which One Should You Use?
Selective alignment with GC bias correction against pure pseudoalignment: near-identical answers on clean data, real divergence on biased libraries, and only one ships sleuth-ready bootstraps.
By Ming "Tommy" Tang, Director of Bioinformatics in Big Pharma · Reviewed September 2026 · 5 min read
The verdict
Default to salmon for bulk RNA-seq quantification, especially anything sourced from public repositories like GEO where samples were sequenced on different runs or with different library preps. Its explicit GC bias correction is not a marginal feature: the Salmon paper shows a large sensitivity improvement at almost every FDR threshold specifically when GC bias is present, and public data is exactly where you'll hit that condition without knowing it in advance. Salmon's .sf output also drops straight into the tximport → DESeq2/edgeR path that most bulk RNA-seq DE workflows already use, and it can emit gene-level counts directly with --geneMap if you don't want a separate collapsing step.
Reach for kallisto when the workflow actually needs its bootstrap output: sleuth's differential expression model is built around kallisto's resampled technical replicates in abundance.h5, and there's no equivalent standard path from salmon into sleuth. Also reach for kallisto if you're already inside the kallisto|bustools ecosystem for single-cell or single-nuclei work, or if you've confirmed your library has no meaningful GC bias and just want the simpler model. Outside those cases, don't pick kallisto on a speed assumption alone; the packet behind this page found no reliable modern, dataset-scale-matched benchmark comparing the two, so verify runtime on your own data rather than trusting an old claim.
Neither salmon nor kallisto aligns reads to the genome base by base the way STAR does. Both work against the transcriptome and both skip the expensive part of traditional alignment, but they get there differently. Kallisto builds a De Bruijn graph out of the transcriptome's k-mers, matches reads to it, and assigns each read to a transcript compatibility class without ever computing a base-level alignment; an EM algorithm then turns those compatibility classes into abundance estimates. Salmon does something closer to alignment: a fast filtering pass proposes candidate transcripts, and then a lightweight but real sequence-based scoring step (selective alignment) confirms and scores the match, folding in a fragment-level probabilistic model as it goes.
That modeling step is where the two tools actually diverge in output. Salmon corrects explicitly for fragment GC-content bias, and further splits each equivalence class into subclasses (four by default) based on the conditional probability of observing each fragment, which layers in sequencing bias and fragment-length information. Kallisto has a more basic sequence bias correction and no comparable GC-content model. On libraries without much GC bias this barely matters and the two tools converge; on libraries with real bias, salmon's correction produces meaningfully better sensitivity at almost every FDR threshold.
The other real difference is what happens after quantification. Kallisto ships bootstrap resamples of the abundance estimates by default, stored in abundance.h5, and sleuth was built specifically to use those bootstraps as proxies for technical replicates when estimating differential expression variance. Salmon's standard output is a tximport-compatible .sf file that feeds directly into the more common DESeq2 or edgeR path. Neither ecosystem is a strict superset of the other; you're choosing between a tool tuned for bias correction and gene-level convenience, and a tool tuned for speed and bootstrap-based uncertainty estimation.
Head to head
| Criterion | salmon | kallisto | Edge |
|---|---|---|---|
| Core algorithm | Selective alignment: a filtering pass finds candidate transcripts by k-mer match, then a lightweight but full sequence-based scoring step (with SIMD-accelerated dynamic programming) confirms and scores each fragment-to-transcript mapping. | Pseudoalignment: reads are matched against a De Bruijn graph built from transcriptome k-mers and assigned to transcript compatibility classes without any base-by-base scoring, then quantified with an EM algorithm. | Tie Different engineering choices, not a strictly better/worse call on their own; the difference matters once you look at bias handling. |
| GC bias correction | Corrects for fragment GC bias explicitly as part of the quantification model; was the first quantifier to build this in. | Offers basic sequence bias correction but no comprehensive GC-content bias model. | salmon Source: nmeth.4197. |
| Sensitivity when GC bias is present | GC correction produces a large improvement in sensitivity at almost every FDR threshold compared to kallisto, per the Salmon paper's benchmarks. | Without a comparable GC model, sensitivity drops relative to salmon specifically on libraries with GC-content bias. | salmon Applies when GC bias is actually present; on unbiased libraries the two tools converge. |
| Results on unbiased data | Produces near-identical quantification to kallisto when the input library has no significant GC-content bias. | Produces near-identical quantification to salmon under the same no-bias condition. | Tie The gap between the two tools is bias-dependent, not a fixed accuracy difference. |
| Equivalence class granularity | Subdivides each equivalence class into subclasses (four by default) based on the conditional probability of each fragment, layering in sequencing bias and fragment-length information. | Assigns reads to compatibility classes directly from the De Bruijn graph without this subclass-level probabilistic refinement. | salmon Part of why salmon's bias correction is more granular, not just present/absent. |
| Technical-replicate / bootstrap support | Bootstrap replicate generation is not part of its documented default workflow in the sources here; downstream uncertainty typically comes from tximport into DESeq2/edgeR's own dispersion model. | Natively outputs bootstrap resamples alongside abundance estimates in abundance.h5, purpose-built to feed sleuth. | kallisto Sleuth's whole DE model is built around kallisto's bootstraps as technical-replicate proxies. |
| Gene-level summarization at quant time | Can output gene-level summaries directly at the quant step by passing a gene mapping file (`salmon quant --geneMap mapping.txt`). | Is transcript-level only at quant time; gene-level collapsing has to happen afterward, typically via tximport with a tx2gene table. | salmon Both tools still route through tximport for a standard DESeq2/edgeR workflow; this is about what quant itself can emit. |
| Output files | Writes `.sf` tab-delimited quant files, tximport-compatible out of the box (`tximport(files, type='salmon', tx2gene=tx2gene)`). | Writes `abundance.h5` (HDF5, includes bootstraps, run info, transcript lengths) and `abundance.tsv` (plain text, no bootstrap data). | Tie Pick the file kallisto users need based on downstream tool: abundance.h5 for sleuth, abundance.tsv or tximport for everything else. |
| Downstream DE ecosystem fit | Fits directly into the tximport → DESeq2/edgeR path used across most bulk RNA-seq DE tutorials. | Fits directly into sleuth when you want bootstrap-informed variance; can also feed tximport → DESeq2/edgeR like salmon when you don't need bootstraps. | Tie kallisto is the only one of the two with a first-class bootstrap-aware DE tool; salmon's path is the more common bulk RNA-seq default. |
Use salmon when
- Your samples come from different sequencing runs, library preps, or public repositories (GEO/SRA), where GC-content bias is likely to vary across batches and would otherwise confound your comparisons.
- You want gene-level counts straight out of the quant step without a separate collapsing script, using `salmon quant --geneMap mapping.txt`.
- Your downstream plan is the standard tximport → DESeq2 or edgeR path, which is what salmon's `.sf` output is built for.
- You're preprocessing CITE-seq or other single-cell-adjacent protein+RNA data, where salmon's Alevin mode is the documented route.
- Maximizing sensitivity at a given FDR threshold matters more than shaving quantification time, and your library has any realistic chance of GC bias.
Use kallisto when
- You need bootstrap-based technical-replicate variance estimates for differential transcript or gene expression, feeding directly into sleuth.
- Your library has no meaningful GC-content bias, so the two tools converge in accuracy and kallisto's simpler model is enough.
- You're already working in a kallisto|bustools pipeline for single-cell or single-nuclei preprocessing and want to stay in that ecosystem rather than switch tools mid-pipeline.
- You want the lighter-weight index and workflow for a quick first pass, and plan to verify results before committing to a full DE pipeline.
- Your lab or collaborators already standardized on sleuth for transcript-level differential expression, and switching would break an established comparison.
Switching between them
Switching quantifiers means switching the whole downstream chain, not just the command you run. Output format changes: salmon's .sf files versus kallisto's abundance.h5 (with bootstraps) and abundance.tsv (without). If you read either into R, the tximport() call needs type='salmon' or type='kallisto' to match, and you still need a tx2gene mapping to collapse to gene level for either tool, unless you used salmon's --geneMap at quant time to get gene-level output directly.
The bigger change is on the DE side. If you're moving off kallisto+sleuth, you lose sleuth's bootstrap-based technical-variance estimate; salmon's default output doesn't feed sleuth, so plan on tximport → DESeq2/edgeR instead, which models variance differently (from biological replicates, not resampled technical ones). If you're moving onto kallisto specifically to use sleuth, make sure your kallisto run actually writes bootstraps to abundance.h5, since the plain-text abundance.tsv doesn't carry them.
Don't assume counts transfer numerically between tools, especially for libraries with GC bias, where salmon's correction changes per-gene sensitivity relative to kallisto. And don't assume counts transfer between reference builds either: mixing hg19- and hg38-quantified samples in one comparison, or mixing samples quantified with different tools, is a well-documented way to get per-gene count differences that dwarf whatever the quantifier choice contributes on its own.
Pitfalls with either
- Assuming salmon and kallisto always agree closely: they converge on unbiased libraries but diverge in sensitivity once GC-content bias is present, so check a per-gene correlation between the two before trusting either blindly on a new dataset.
- Mixing salmon and kallisto output (or STAR/HTSeq alongside either) within one differential expression comparison: reprocess every sample in the comparison with the same tool and the same reference build instead.
- Running sleuth on kallisto's abundance.tsv instead of abundance.h5: abundance.tsv has no bootstrap data, so make sure your kallisto run writes and retains abundance.h5 for every sample before starting the sleuth workflow.
- Treating transcript-level quant output as if it were already gene-level: always collapse through tximport with a tx2gene table, or use salmon's `--geneMap` at quant time, before handing counts to DESeq2 or edgeR.
- Blending samples quantified against different genome/transcriptome builds (hg19 vs hg38) regardless of which quantifier you used: confirm every sample used the same annotation build first, since this causes bigger count discrepancies than the salmon-vs-kallisto choice itself.
- Carrying over an old runtime claim that one tool is categorically faster: current benchmark numbers for both tools at realistic dataset scales aren't well documented publicly, so time both on your own hardware and read counts before deciding speed settles the choice.
Questions people ask
- Do salmon and kallisto give the same results?
Close, but not guaranteed identical. On libraries without significant GC-content bias, the two produce near-identical quantification. Where GC bias is present, salmon's explicit correction pulls ahead in sensitivity at almost every FDR threshold, because kallisto only has basic sequence bias correction and no comparable GC model.
- Which is faster, salmon or kallisto?
Both skip full base-pair alignment against the genome and are much faster than aligner-based pipelines like STAR, but current, dataset-scale-specific benchmark numbers comparing the two directly aren't well documented in the sources behind this page. Don't carry over an old runtime claim; time both on your own read counts and hardware before deciding speed is the deciding factor.
- Can I use salmon output with sleuth?
Not in the standard workflow. Sleuth is built around kallisto's bootstrap resamples stored in abundance.h5, which salmon's default output doesn't produce in that form. If you're on salmon, route through tximport into DESeq2 or edgeR instead.
- Do I need gene-level or transcript-level counts?
Both tools quantify at transcript level by default. For gene-level counts, run either through tximport with a tx2gene mapping, or take salmon's shortcut and pass
--geneMapat quant time. Transcript-level and gene-level are different questions biologically, especially for genes with multiple transcripts, so don't treat one as a drop-in stand-in for the other.- Can I switch from kallisto to salmon (or back) mid-project?
Yes, but reprocess every sample in the comparison with the same tool and the same reference transcriptome build. Mixing quantifier outputs, or mixing hg19 and hg38 references, within one differential expression comparison is a bigger source of spurious count differences than the choice of tool itself.
Related pages
- Guide · Why You Must Not Use TPM for Differential Expression in Bulk RNA-seq
- Glossary · CITE-seq
- Glossary · Snakemake
Related reading on the blog
Sources
- Salmon provides fast and bias-aware quantification of transcript expression — Source for salmon's GC bias correction, subclass modeling of equivalence classes, and its sensitivity advantage at matched FDR when GC bias is present.
- A direct comparison of genome alignment and transcriptome pseudoalignment — Source for the selective-alignment vs pseudoalignment mechanics: salmon's sequence-based matching to filtered candidates, kallisto's De Bruijn graph and EM.
- Kallisto Documentation - Manual — Source for kallisto's abundance.h5 and abundance.tsv output file contents.
- Getting started with sleuth — Source for how sleuth uses kallisto's bootstrap estimates as technical-replicate proxies for differential expression.
- Downstream of bulk RNAseq: read in salmon output using tximport and then DESeq2 — Source for the tximport(type='salmon') workflow into DESeq2.