| name | jaspar-database |
| description | JASPAR 2024 TF binding profiles via REST API and pyJASPAR. Retrieve PFMs/PWMs by TF name, JASPAR ID, species, or structural class. Scan DNA for TFBS; browse by taxon (human, mouse) or TF family (bHLH, zinc finger). Use for motif enrichment input, TFBS scanning, and regulatory sequence analysis. For ChIP-seq peak motif discovery use homer-motif-analysis; for regulatory variant scoring use regulomedb-database. |
| license | CC-BY-4.0 |
JASPAR Database
Overview
JASPAR is a curated, open-access database of transcription factor (TF) binding profiles represented as position frequency matrices (PFMs). The 2024 release contains 1,209 profiles in the CORE vertebrate collection, covering 783 TFs with experimentally validated binding data from SELEX, ChIP-seq, and PBM experiments. Access is free via the JASPAR REST API at https://jaspar.elixir.no/api/v1/ — no authentication required — and through the pyJASPAR Python library for matrix retrieval and manipulation.
When to Use
- Looking up the PWM or PFM for a specific TF by name (e.g., CTCF, SP1, GATA1) to use as motif input for a scanning tool
- Retrieving all JASPAR profiles for a species (e.g., Homo sapiens, Mus musculus) to build a motif library for enrichment analysis
- Scanning a DNA promoter sequence for predicted TF binding sites using a known PWM
- Finding all TFs of a given structural class (bHLH, zinc finger, homeodomain) to build a TF family binding profile set
- Getting metadata for a JASPAR matrix: number of binding sites, information content, GC content, experiment type
- Downloading complete JASPAR collection sets (CORE, UNVALIDATED, CNE) in JASPAR or MEME format for batch analysis
- Use
homer-motif-analysis instead when you need de novo motif discovery from ChIP-seq peaks; JASPAR is for retrieving known matrices
- For regulatory element annotations tied to a genomic region use
encode-database or regulomedb-database
Prerequisites
- Python packages:
requests, pandas, matplotlib, numpy
- Optional:
pyJASPAR (Python library wrapping JASPAR REST API with BIOPYTHON motif objects)
- Data requirements: TF gene symbols, JASPAR matrix IDs (e.g.,
MA0139.1), or DNA sequences (string or FASTA)
- Environment: internet connection; no API key required
- Rate limits: no official published limits; use
time.sleep(0.5) between batch requests
pip install requests pandas matplotlib numpy
pip install pyJASPAR
Quick Start
import requests
JASPAR_API = "https://jaspar.elixir.no/api/v1"
r = requests.get(f"{JASPAR_API}/matrix/", params={
"search": "CTCF",
"collection": "CORE",
"tax_group": "vertebrates",
"format": "json"
}, timeout=15)
r.raise_for_status()
results = r.json()
print(f"Profiles found: {results['count']}")
for m in results["results"][:3]:
print(f" {m['matrix_id']} {m['name']} sites={m['sites']} type={m['type']}")
Core API
Query 1: Matrix Search
Search for TF profiles by TF name, species, collection, or taxonomic group. Returns a paginated list of matching profile records.
import requests, time
JASPAR_API = "https://jaspar.elixir.no/api/v1"
def jaspar_search(search=None, collection="CORE", tax_id=None, tax_group=None,
tf_class=None, tf_family=None, page_size=50):
"""Search JASPAR matrices. Returns list of result dicts."""
params = {"format": "json", "page_size": page_size}
if search: params["search"] = search
if collection: params["collection"] = collection
if tax_id: params["tax_id"] = tax_id
if tax_group: params["tax_group"] = tax_group
if tf_class: params["tf_class"] = tf_class
if tf_family: params["tf_family"] = tf_family
all_results = []
url = f"{JASPAR_API}/matrix/"
while url:
r = requests.get(url, params=params if url == f"{JASPAR_API}/matrix/" else None, timeout=15)
r.raise_for_status()
data = r.json()
all_results.extend(data["results"])
url = data.get("next")
time.sleep(0.3)
return all_results
gata_profiles = jaspar_search(search="GATA", collection=, tax_group=)
()
m gata_profiles[:]:
()
Query 2: Matrix Retrieval
Fetch the full profile record for a specific matrix ID, including the raw PFM counts, metadata, and TF annotations.
import requests
JASPAR_API = "https://jaspar.elixir.no/api/v1"
def get_matrix(matrix_id):
"""Return full matrix record for a JASPAR ID (e.g. 'MA0139.1')."""
r = requests.get(f"{JASPAR_API}/matrix/{matrix_id}/", params={"format": "json"}, timeout=15)
r.raise_for_status()
return r.json()
m = get_matrix("MA0139.1")
print(f"ID: {m['matrix_id']} Name: {m['name']}")
print(f"Collection: {m['collection']} Type: {m['type']}")
print(f"Species: {[s['name'] for s in m.get('species', [])]}")
print(f"UniProt: {m.get('uniprot_ids', [])}")
print(f"Sites: {m['sites']} Binding sites used to build matrix")
print(f"TF class: {m.get('class_name', 'n/a')} Family: {m.get('family_name', 'n/a')}")
pfm = m[]
n_positions = (pfm)
()
()
Query 3: PWM Computation from PFM
Convert a raw PFM (count matrix) to a position weight matrix (PWM) using log-odds scoring. The PWM is used for binding site scanning.
import requests, numpy as np
JASPAR_API = "https://jaspar.elixir.no/api/v1"
def pfm_to_pwm(pfm_dict, pseudocount=0.8, background=None):
"""
Convert JASPAR PFM dict to PWM (log2 odds).
pfm_dict: dict of str(position) -> {A, C, G, T: float}
Returns: numpy array shape (4, L), rows = [A, C, G, T]
"""
if background is None:
background = {"A": 0.25, "C": 0.25, "G": 0.25, "T": 0.25}
bases = ["A", "C", "G", "T"]
L = len(pfm_dict)
counts = np.array([[pfm_dict[str(i)][b] for i in range(L)] for b in bases], dtype=float)
counts += pseudocount
freqs = counts / counts.sum(axis=0, keepdims=True)
bg = np.array([background[b] for b in bases])[:, None]
pwm = np.log2(freqs / bg)
return pwm
r = requests.get(f"{JASPAR_API}/matrix/MA0139.1/", params={"format": "json"}, timeout=15)
pfm = r.json()["pfm"]
pwm = pfm_to_pwm(pfm)
print(f"PWM shape: (4 bases × positions)")
()
()
Query 4: Sequence Scanning
Scan a DNA sequence for TFBS matches by sliding the PWM across the sequence and computing log-odds scores at each position.
import requests, numpy as np
JASPAR_API = "https://jaspar.elixir.no/api/v1"
BASE_IDX = {"A": 0, "C": 1, "G": 2, "T": 3}
def pfm_to_pwm(pfm_dict, pseudocount=0.8):
bases = ["A", "C", "G", "T"]
L = len(pfm_dict)
counts = np.array([[pfm_dict[str(i)][b] for i in range(L)] for b in bases], dtype=float)
counts += pseudocount
freqs = counts / counts.sum(axis=0, keepdims=True)
return np.log2(freqs / 0.25)
def scan_sequence(seq, pwm, threshold_pct=0.80):
"""
Slide pwm over seq, return hits above threshold_pct of max possible score.
Returns list of (position, score, strand).
"""
seq = seq.upper()
L = pwm.shape[1]
max_score = pwm.clip(min=0).sum(axis=0).sum()
min_score = pwm.clip(max=0).sum(axis=0).sum()
threshold = min_score + threshold_pct * (max_score - min_score)
hits = []
i ((seq) - L + ):
window = seq[i:i+L]
window:
score = (pwm[BASE_IDX[window[j]], j] j (L))
score >= threshold:
hits.append((i, (score, ), ))
hits, max_score, threshold
r = requests.get(, params={: }, timeout=)
pfm = r.json()[]
pwm = pfm_to_pwm(pfm)
seq = (
)
hits, max_score, thresh = scan_sequence(seq, pwm, threshold_pct=)
()
()
pos, score, strand hits:
()
Query 5: Taxon Browser
List all JASPAR CORE profiles for a specific organism, identified by NCBI taxonomy ID.
import requests, time, pandas as pd
JASPAR_API = "https://jaspar.elixir.no/api/v1"
TAX_IDS = {
"Homo sapiens": 9606,
"Mus musculus": 10090,
"Rattus norvegicus": 10116,
"Drosophila melanogaster": 7227,
"Saccharomyces cerevisiae": 4932,
}
def get_species_profiles(tax_id, collection="CORE"):
"""Return all matrices for a species tax ID."""
params = {"tax_id": tax_id, "collection": collection, "format": "json", "page_size": 100}
results = []
url = f"{JASPAR_API}/matrix/"
while url:
r = requests.get(url, params=params if url == f"{JASPAR_API}/matrix/" else None, timeout=15)
r.raise_for_status()
data = r.json()
results.extend(data["results"])
url = data.get("next")
time.sleep(0.3)
return results
human_profiles = get_species_profiles(TAX_IDS["Homo sapiens"])
print(f"Human CORE profiles: {len(human_profiles)}")
df = pd.DataFrame([{
"matrix_id": m["matrix_id"],
"name": m[],
: m.get(, ),
: m.get(, ),
: m[],
: m[],
} m human_profiles])
(df.head().to_string(index=))
()
Query 6: TF Class and Family Browser
Find all profiles belonging to a specific TF structural class or family, useful for building class-specific motif libraries.
import requests, time
JASPAR_API = "https://jaspar.elixir.no/api/v1"
def get_class_profiles(tf_class=None, tf_family=None, collection="CORE", tax_group="vertebrates"):
"""Return all profiles for a TF structural class or family."""
params = {"collection": collection, "tax_group": tax_group, "format": "json", "page_size": 100}
if tf_class: params["tf_class"] = tf_class
if tf_family: params["tf_family"] = tf_family
results = []
url = f"{JASPAR_API}/matrix/"
while url:
r = requests.get(url, params=params if url == f"{JASPAR_API}/matrix/" else None, timeout=15)
r.raise_for_status()
data = r.json()
results.extend(data["results"])
url = data.get("next")
time.sleep(0.3)
return results
bhlh = get_class_profiles(tf_family="bHLH", collection="CORE", tax_group="vertebrates")
print(f"bHLH vertebrate profiles: {len(bhlh)}")
for m in bhlh[:5]:
()
Key Concepts
JASPAR Collections
JASPAR organizes profiles into curated collections:
| Collection | Description | Profile count |
|---|
CORE | Manually curated, non-redundant, high-quality profiles | ~1,200 (2024) |
CNE | Profiles derived from conserved noncoding elements | ~100 |
UNVALIDATED | Profiles not yet manually curated | ~1,000 |
PHYLOFACTS | Profiles from phylogenetically constrained sites | ~100 |
POLII | RNA polymerase II binding profiles | ~10 |
For most analyses use CORE. The JASPAR CORE 2024 vertebrate collection is the default reference for motif enrichment tools.
Matrix ID Versioning
JASPAR matrix IDs have the format MA{number}.{version} (e.g., MA0139.1). A new version is released when the binding data is updated. When scripting, use the versioned ID for reproducibility. Searching by name (e.g., CTCF) returns all versions; select the highest-numbered version for the most up-to-date matrix.
Information Content
The information content (IC) at each position (in bits) measures binding site specificity:
- IC = 2 - H(position), where H is the Shannon entropy
- High IC (close to 2 bits) = near-invariant base (e.g., always A)
- Low IC (close to 0) = little positional preference
- Total IC = sum over all positions; high total IC = more specific binding
import numpy as np
def information_content(pfm_dict, pseudocount=0.8):
"""Compute per-position IC and total IC for a JASPAR PFM."""
bases = ["A", "C", "G", "T"]
L = len(pfm_dict)
counts = np.array([[pfm_dict[str(i)][b] for i in range(L)] for b in bases], dtype=float)
counts += pseudocount
freqs = counts / counts.sum(axis=0, keepdims=True)
entropy = -np.sum(freqs * np.log2(freqs + 1e-12), axis=0)
ic_per_pos = 2 - entropy
return ic_per_pos, ic_per_pos.sum()
import requests
r = requests.get("https://jaspar.elixir.no/api/v1/matrix/MA0139.1/",
params={"format": "json"}, timeout=15)
pfm = r.json()["pfm"]
ic_pos, total_ic = information_content(pfm)
print(f"Total IC: {total_ic:.2f} bits")
print(f"Max IC position: {ic_pos.argmax()} ({ic_pos.max():.2f} bits)")
Common Workflows
Workflow 1: Build a Human TF Motif Library and Export to MEME Format
Goal: Download all human CORE profiles and write them in MEME minimal format for use with FIMO, AME, or TOMTOM.
import requests, time, numpy as np
JASPAR_API = "https://jaspar.elixir.no/api/v1"
def get_all_profiles(tax_id=9606, collection="CORE"):
params = {"tax_id": tax_id, "collection": collection, "format": "json", "page_size": 100}
results, url = [], f"{JASPAR_API}/matrix/"
while url:
r = requests.get(url, params=params if url == f"{JASPAR_API}/matrix/" else None, timeout=15)
r.raise_for_status()
data = r.json()
results.extend(data["results"])
url = data.get("next")
time.sleep(0.3)
return results
def pfm_to_freq(pfm_dict, pseudocount=0.1):
"""Return (4, L) frequency matrix [A, C, G, T]."""
bases = ["A", "C", "G", "T"]
L = len(pfm_dict)
counts = np.array([[pfm_dict[str(i)][b] for i in range(L)] for b in bases], dtype=float)
counts += pseudocount
return counts / counts.sum(axis=0, keepdims=True)
profiles = get_all_profiles(tax_id=, collection=)
()
meme_lines = [
,
,
,
,
,
,
]
m profiles:
pfm = m[]
freq = pfm_to_freq(pfm)
L = freq.shape[]
meme_lines.append()
meme_lines.append()
j (L):
row = .join( b ())
meme_lines.append()
meme_lines.append()
output_path =
(output_path, ) f:
f.write(.join(meme_lines))
()
Workflow 2: Promoter Scan for Multiple TFs and Visualize PWM Logos
Goal: Download PWMs for a set of TFs and scan a promoter sequence, then visualize the best-scoring hit as a bar logo.
import requests, numpy as np, time
import matplotlib.pyplot as plt
import matplotlib.patches as mpatches
JASPAR_API = "https://jaspar.elixir.no/api/v1"
BASE_IDX = {"A": 0, "C": 1, "G": 2, "T": 3}
BASE_COLORS = {"A": "#2ca02c", "C": "#1f77b4", "G": "#ff7f0e", "T": "#d62728"}
def fetch_pwm(matrix_id, pseudocount=0.8):
r = requests.get(f"{JASPAR_API}/matrix/{matrix_id}/", params={"format": "json"}, timeout=15)
r.raise_for_status()
pfm = r.json()["pfm"]
L = len(pfm)
counts = np.array([[pfm[str(i)][b] for i in range(L)] for b in ["A","C","G","T"]], dtype=float)
counts += pseudocount
freqs = counts / counts.sum(axis=0, keepdims=True)
return np.log2(freqs / 0.25), freqs
def ():
seq = seq.upper()
L = pwm.shape[]
max_s = pwm.clip(=).()
min_s = pwm.clip(=).()
thresh = min_s + pct * (max_s - min_s)
[(i, (pwm[BASE_IDX[seq[i+j]], j] j (L)))
i ((seq)-L+) seq[i:i+L]
(pwm[BASE_IDX[seq[i+j]], j] j (L)) >= thresh]
():
L = freqs.shape[]
entropy = -np.(freqs * np.log2(freqs + ), axis=)
ic = - entropy
bases = [, , , ]
fig, ax = plt.subplots(figsize=((, L * ), ))
bottom = np.zeros(L)
idx, base (bases):
heights = freqs[idx] * ic
ax.bar((L), heights, bottom=bottom, color=BASE_COLORS[base], label=base, width=)
bottom += heights
ax.set_xticks((L))
ax.set_xticklabels([(i+) i (L)], fontsize=)
ax.set_ylabel()
ax.set_title(title, fontsize=)
ax.legend(handles=[mpatches.Patch(color=BASE_COLORS[b], label=b) b bases],
loc=, fontsize=, ncol=)
plt.tight_layout()
plt.savefig(outfile, dpi=, bbox_inches=)
plt.close()
()
promoter = (
)
matrix_id, tf_name [(, ), (, )]:
pwm, freqs = fetch_pwm(matrix_id)
hits = scan(promoter, pwm, pct=)
()
pos, score hits[:]:
()
plot_logo(freqs, , )
time.sleep()
Workflow 3: TF Co-binding Partner Discovery via Shared Matrix Families
Goal: Find all TF families that have profiles in JASPAR, then retrieve family members to identify potential co-binding partners of a TF of interest.
import requests, time, pandas as pd
from collections import Counter
JASPAR_API = "https://jaspar.elixir.no/api/v1"
def get_profiles_df(tax_group="vertebrates", collection="CORE"):
params = {"tax_group": tax_group, "collection": collection, "format": "json", "page_size": 100}
results, url = [], f"{JASPAR_API}/matrix/"
while url:
r = requests.get(url, params=params if url == f"{JASPAR_API}/matrix/" else None, timeout=15)
r.raise_for_status()
data = r.json()
results.extend(data["results"])
url = data.get("next")
time.sleep(0.3)
return pd.DataFrame([{
"matrix_id": m["matrix_id"],
"name": m["name"],
"tf_class": m.get("class_name", "Unknown"),
"tf_family": m.get("family_name", "Unknown"),
"sites": m["sites"],
"type": m["type"],
"uniprot": ";".join(m.get("uniprot_ids", [])),
} for m in results])
df = get_profiles_df(tax_group=, collection=)
()
family_counts = df[].value_counts()
()
(family_counts.head().to_string())
ctcf_row = df[df[] == ].iloc[]
ctcf_family = ctcf_row[]
co_family = df[df[] == ctcf_family][[, , , ]]
()
(co_family.to_string(index=))
df.to_csv(, index=)
()
Key Parameters
| Parameter | Endpoint | Default | Range / Options | Effect |
|---|
search | /matrix/ | — | Any string (TF name, gene symbol) | Full-text search across name and aliases |
collection | /matrix/ | — | CORE, UNVALIDATED, CNE, POLII, PHYLOFACTS | Restricts to a JASPAR sub-collection |
tax_group | /matrix/ | — | vertebrates, insects, plants, fungi, nematodes, urochordates | Filter by broad taxonomic group |
tax_id | /matrix/ | — | NCBI taxonomy ID integer (e.g., 9606) | Restrict to a single species |
tf_class | /matrix/ | — | "Zinc-coordinating", "Basic leucine zipper", "Helix-turn-helix", etc. | Filter by TF structural class |
tf_family | /matrix/ | — | "C2H2 ZF", "bHLH", "bZIP", "Homeodomain", etc. | Filter by TF structural family |
page_size | /matrix/ | 10 | 1–100 | Results per page; use 100 for batch downloads |
pseudocount | PFM→PWM (local) | 0.8 | 0.01–1.0 | Smooths zero-count positions; higher = less extreme PWM values |
threshold_pct | scan (local) | 0.80 | 0.50–0.99 | Fraction of max score required to call a hit; lower = more permissive |
Best Practices
-
Pin matrix ID versions for reproducibility: Use MA0139.1 (not just CTCF) in scripts and manuscripts so results do not silently change across JASPAR releases.
-
Always add pseudocounts when computing PWMs: Raw PFMs contain zero counts for rare positions. A zero count produces -inf in log space, eliminating any sequence with that nucleotide. Use pseudocount = 0.8 (JASPAR recommendation) or pseudocount = sqrt(sites) / 4.
-
Use the CORE collection for standard analyses: UNVALIDATED profiles have not been manually curated and may contain lower-confidence motifs. Reserve UNVALIDATED for exploratory analyses.
-
Respect pagination: JASPAR returns at most 100 results per page. Always follow the next URL in responses when building complete profile sets.
-
For batch scanning, use FIMO (MEME suite) rather than manual sliding window: The manual scanning above is educational. For production use, export matrices to MEME format (Workflow 1) and use fimo --thresh 1e-4 motifs.meme sequence.fa.
Common Recipes
Recipe: Fetch All Versions of a TF's Matrix
When to use: Compare old and new profile versions of the same TF to check for changes.
import requests
JASPAR_API = "https://jaspar.elixir.no/api/v1"
def get_all_versions(tf_name, collection="CORE"):
r = requests.get(f"{JASPAR_API}/matrix/", params={
"search": tf_name, "collection": collection, "format": "json", "page_size": 50
}, timeout=15)
r.raise_for_status()
return r.json()["results"]
versions = get_all_versions("SP1")
print(f"SP1 matrix versions in CORE: {len(versions)}")
for m in sorted(versions, key=lambda x: x["matrix_id"]):
print(f" {m['matrix_id']:12s} sites={m['sites']:5d} type={m['type']}")
Recipe: Retrieve Matrix in JASPAR Flat-File Format
When to use: Download a matrix in the legacy JASPAR text format for tools that accept it directly.
import requests
JASPAR_API = "https://jaspar.elixir.no/api/v1"
def get_jaspar_format(matrix_id):
"""Return matrix as JASPAR flat-file string."""
r = requests.get(f"{JASPAR_API}/matrix/{matrix_id}/", params={"format": "jaspar"}, timeout=15)
r.raise_for_status()
return r.text
jaspar_str = get_jaspar_format("MA0139.1")
print(jaspar_str[:200])
with open("CTCF_MA0139.1.jaspar", "w") as f:
f.write(jaspar_str)
print("Saved CTCF_MA0139.1.jaspar")
Recipe: pyJASPAR Quick Motif Retrieval
When to use: Retrieve a motif as a BioPython motifs.Motif object when downstream tools expect that interface.
import pyJASPAR
db = pyJASPAR.JASPAR2024(auto_reverse_complement=True)
motif = db.fetch_motif_by_id("MA0139.1")
print(f"Name: {motif.name}")
print(f"Matrix ID: {motif.matrix_id}")
print(f"Length: {len(motif)}")
print(f"Consensus: {motif.consensus}")
motifs = db.fetch_motifs(collection="CORE", tax_id=9606, tf_family="bHLH")
print(f"Human bHLH motifs: {len(motifs)}")
for m in motifs[:3]:
print(f" {m.matrix_id} {m.name} len={len(m)}")
Troubleshooting
| Problem | Cause | Solution |
|---|
Empty results list from search | TF not in JASPAR, wrong collection, or wrong tax_group | Try collection=None to search all collections; check TF alias (e.g., NF-kB → RELA) |
404 Not Found for matrix ID | Invalid or misspelled matrix ID | Verify ID format: MA + 4 digits + . + version (e.g., MA0139.1); search by name first |
pfm['0'] missing key | Some JASPAR profiles have 0-indexed positions as integers, not strings | Cast position keys: pfm_dict = {str(k): v for k, v in pfm.items()} |
| PWM scan produces no hits | Threshold too strict or sequence too short | Lower threshold_pct to 0.70; check sequence length vs motif length |
pyJASPAR install fails | Requires Python ≥3.8 and C extensions for BIOPYTHON | Use requests-based API directly; pyJASPAR is optional |
| Pagination stops early | next field is null before expected total | Check count field in first response vs len(results) after loop |
| High IC positions show wrong base | PFM row order assumed incorrectly | JASPAR always returns {A, C, G, T} keys; never assume positional ordering |
Related Skills
homer-motif-analysis — de novo motif discovery from ChIP-seq or ATAC-seq peak sets; complements JASPAR known-motif library
regulomedb-database — regulatory variant scoring using TF binding evidence overlapping JASPAR motifs
encode-database — download TF ChIP-seq peak files that can be cross-referenced with JASPAR profiles
remap-database — TF binding peak sets from ChIP-seq experiments for binding site validation
macs3-peak-calling — produce ChIP-seq peak BED files for downstream JASPAR motif enrichment
References