PIASOmarkerDB API client
PIASOmarkerDB is a curated collection of
cell-type marker genes with specificity scores. piaso.tl.getMarkers is the
Python client — no key, no separate install.
import piaso1. What is in there
studies = piaso.tl.getMarkers(list_studies=True)len(studies), studies[:6](36, ['AllenAgeingBrainAtlas2025_supertype', 'AllenDevVisualCortex2025_RNA', 'AllenHumanImmuneHealthAtlas_L2', 'AllenHumanImmuneHealthAtlas_L3', 'AllenWholeMouseBrain_GABA', 'AllenWholeMouseBrain_Neuron'])Thirty-six studies spanning human and mouse, brain, blood, bone marrow, breast, thymus and whole-body atlases.
2. One study
df = piaso.tl.getMarkers(study="AllenWholeMouseBrain_isocortex")df.shape, list(df.columns)((1300, 7), ['cell_type', 'condition', 'gene', 'species', 'specificity_score', 'study_publication', 'tissue']) cell_type condition gene species specificity_score 056 Sst Chodl Gaba normal Chodl Mouse 14.086876 056 Sst Chodl Gaba normal Krt18 Mouse 13.081692051 Pvalb chandelier Gaba normal Vmn1r209 Mouse 12.826864 330 VLMC NN normal Slc22a6 Mouse 11.932269specificity_score is a COSG score: higher means the gene is more exclusive to
that type. Chodl topping Sst Chodl Gaba is the sanity check — the type is
named after the gene.
For anything that consumes marker sets, ask for the dictionary as well. It returns both, so unpack two names:
markers_df, marker_sets = piaso.tl.getMarkers( study="AllenWholeMouseBrain_isocortex", as_dict=True)len(marker_sets)26Assigning that to one variable hands the tuple to whatever you call next — a mistake that reached three published tutorials before it was caught.
3. Ask the other way round
The client is not only “give me markers for this study”. Every field filters.
By gene — where is this gene a marker, across all studies?
piaso.tl.getMarkers(gene="Sst")114 rows
cell_type gene species specificity_score study_publication enteroendocrine cell SST Human 12.843140 TabulaSapiens_10Xenteroendocrine cell of small intestine SST Human 7.971432 TabulaSapiens_10X SST InN SST Human 7.363342 XuTeichmann2023_HippocampalFormation 053 Sst Gaba Sst Mouse 7.326999 AllenWholeMouseBrain_isocortex Interneurons Sst Mouse 7.298782 DiBellaArlotta2021Somatostatin is a marker of cortical interneurons and of gut enteroendocrine cells, more specifically of the latter. Useful before assuming a gene means what it means in your tissue.
By cell type, species, score, or a limit:
piaso.tl.getMarkers(cell_type="Pvalb Gaba")piaso.tl.getMarkers(species="Mouse", limit=5)piaso.tl.getMarkers(study="AllenWholeMouseBrain_isocortex", min_score=5.0)Cell type names must match the study’s vocabulary exactly — "Astro-TE"
returns 0 rows because the stored name is "319 Astro-TE NN". List them first:
piaso.tl.getMarkers(study="AllenWholeMouseBrain_isocortex", list_cell_types=True)[:6]['001 CLA-EPd-CTX Car3 Glut', '004 L6 IT CTX Glut', '005 L5 IT CTX Glut', '006 L4/5 IT CTX Glut', '007 L2/3 IT CTX Glut', '022 L5 ET CTX Glut']len(piaso.tl.getMarkers(study="AllenWholeMouseBrain_isocortex", list_genes=True))236574. From a gene list to a cell type
analyzeMarkers is the reverse query: you have a set of genes — a cluster’s
markers, a differential expression hit list — and want to know which annotated
cell types they look like.
piaso.tl.analyzeMarkers(["Sst", "Pvalb", "Vip", "Lamp5", "Gad1", "Gad2"]) cell_type study_publication species matched matched_genes avg_specificity Interneurons DiBellaArlotta2021 Mouse 3 Gad1,Sst,Gad2 7.273702 Lamp5 Gaba_2 AllenAgeingBrainAtlas2025_supertype Mouse 3 Gad1,Lamp5,Gad2 3.712216 Inh_Six3 MacoskoWholeMouseBrain... Mouse 3 Pvalb,Gad2,Gad1 3.241107742_Pvalb Gaba_3 AllenDevVisualCortex2025_RNA Mouse 3 Gad1,Gad2,Pvalb 3.197737 IN-Mix-LAMP5 WangKriegstein2025 Human 2 LAMP5,GAD2 5.817186 IN-MGE-PV WangKriegstein2025 Human 2 PVALB,GAD2 5.364346Six canonical GABAergic genes, and every top hit is an inhibitory neuron type — across four independent studies, two species, and both adult and developmental taxonomies. Ranking is by how many of your genes matched, then by average specificity.
This is the fastest sanity check on an unnamed cluster: hand it the top COSG genes and see what the literature already calls it.
5. Annotating clusters, not cells
analyzeMarkers scales up: run COSG on your clusters, hand each cluster’s
markers to the database, and take the best match. That labels a whole cluster
from its own gene list rather than scoring every cell.
import cosg
piaso.tl.infog(adata, layer="raw", n_top_genes=3000)piaso.tl.runSVD(adata, layer="infog", n_components=50, key_added="X_svd")piaso.tl.neighbors(adata, use_rep="X_svd", n_neighbors=15)piaso.tl.leiden(adata, resolution=1.0, key_added="leiden")
cosg.cosg(adata, key_added="cosg", groupby="leiden", n_genes_user=30, mu=10)names = pd.DataFrame(adata.uns["cosg"]["names"])rows = []for cluster in names.columns: hits = piaso.tl.analyzeMarkers(list(names[cluster])) best = hits.iloc[0] rows.append(dict(leiden=cluster, call=best["cell_type"], matched=int(best["matched_gene_count"]), study=best["study_publication"]))calls = pd.DataFrame(rows)On the adult mouse cortex reference, 30 Leiden clusters:
| leiden | call | matched / 30 | study |
|---|---|---|---|
| 3 | 053 Sst Gaba | 17 | AllenWholeMouseBrain_isocortex |
| 4 | OPC NN_1 | 21 | AllenAgeingBrainAtlas2025_supertype |
| 5 | 052 Pvalb Gaba | 19 | AllenWholeMouseBrain_isocortex |
| 7 | 032 L5 NP CTX Glut | 21 | AllenWholeMouseBrain_isocortex |
| 9 | Micro_Selplg | 16 | MacoskoWholeMouseBrain_nuclei_NonNeuron |
| 20 | 335 BAM NN | 27 | AllenWholeMouseBrain_NonNeuron |
| 26 | 333 Endo NN | 19 | AllenWholeMouseBrain_isocortex |
Two things to notice.
The matched column is the confidence. Cluster 20 matched 27 of its 30
genes to border-associated macrophages — that is not a close call. Clusters
matching 3 to 7 genes are guesses, and the column tells you which is which
without any extra work.
Different clusters match different studies. The query runs across all 36, so a cluster is labelled by whichever study describes it best rather than by one you picked in advance. The interneurons land on the Allen isocortex taxonomy, the glia on Macosko and the ageing atlas.
adata.obs["cluster_call"] = adata.obs["leiden"].map( dict(zip(calls.leiden, calls.call)))piaso.pl.embedding(adata, basis="X_umap", color=["leiden", "cluster_call"])
piaso.pl.sankey(adata, left="leiden", right="cluster_call")
Checked against this dataset’s own curated annotation, 87.6% of cells carry a reference label whose modal cluster-call is the right correspondence — Oligodendrocyte → MOL and Oligo_Opalin, Microglia → Micro_Selplg, OPC → OPC NN_1, PV → 052 Pvalb Gaba, L6 CT → 030 L6 CT CTX Glut.
Cluster-wise or cell-wise?
cluster-wise (analyzeMarkers) | cell-wise (predictCellTypeByMarker) | |
|---|---|---|
| unit | one label per cluster | one label per cell |
| needs | a clustering | an embedding |
| tells you | which study describes each cluster, and how well | which cells disagree with their neighbours |
| cost | one API call per cluster | one scoring pass |
Cluster-wise is the faster first pass and it names the source as well as the type. Cell-wise is what you want when the question is about individual cells — doublets, intermediate states, cells that sit between two labels.
6. Straight into annotation
markers_df, marker_sets = piaso.tl.getMarkers( study="AllenWholeMouseBrain_isocortex", as_dict=True)piaso.tl.predictCellTypeByMarker(adata, marker_gene_set=marker_sets, score_layer="infog", use_rep="X_svd", key_added="CellTypes")Worked through, with accuracy numbers, in marker-based cell type prediction.
Notes
- Results are cached locally after the first call, so repeated queries in a session do not re-hit the API.
piaso.tl.queryPIASOmarkerDBis the full-featured entry point;getMarkersis the short alias, and takes the same arguments.- Intersect marker sets with your own
var_namesand drop the types left with too few genes — a type scored on two surviving genes is scored on almost no evidence.