Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Query RegulomeDB v2 GET REST API to score variants for regulatory function and retrieve overlapping evidence (TF binding, histone marks, DNase peaks, footprints, motifs, eQTLs, chromatin state). Scores range 1a (strongest) to 7 (none). Use for GWAS hit prioritization, regulatory variant annotation, cis-regulatory discovery. Use clinvar-database for pathogenicity; gwas-database for trait associations.
.claude/skills/jaechang-hits-regulomedb-database/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-01 | ✗→✓ | ▲ Improved | 117% | 0% |
| case-02 | ✗→✓ | ▲ Improved | 266% | 0% |
| case-03 | ✗→✓ | ▲ Improved | 197% | 0% |
| case-07 | ✗→✓ | ▲ Improved | 129% | 0% |
| case-08 | ✗→✓ | ▲ Improved | 101% | 0% |
RegulomeDB integrates large-scale functional genomics data (ENCODE, Roadmap Epigenomics) to score genetic variants for regulatory potential. Each variant receives a ranking from 1a (highest regulatory confidence: eQTL + TF + DNase + motif + chromatin) to 7 (no known regulatory function). The v2 API is exposed as GET https://regulomedb.org/regulome-search/; the legacy POST /regulome-search/, POST /regulome-summary/, and GET /regulome-datasets/ JSON endpoints are no longer functional (return regulome-notfound stubs or 500). Access is free and requires no authentication.
@graph evidence rows)features.QTL)nearby_snps)clinvar-database instead when you need clinical pathogenicity classifications; RegulomeDB scores regulatory function, not germline disease associationgwas-database instead when you want published GWAS associations with traitsrequests, pandas, matplotlibrs4946036), genomic positions (chr1:1000000), or region coordinates (chr1:1000000-2000000)time.sleep(0.3) between requests in batch workflowsbashpip install requests pandas matplotlib
pythonimport requests BASE = "https://regulomedb.org" def regulome_score(variant, genome="GRCh38"): """Score a single variant (rsID or chr:pos-pos) via the GET /regulome-search/ endpoint.""" r = requests.get( f"{BASE}/regulome-search/", params={"regions": variant, "genome": genome, "format": "json"}, timeout=30, ) r.raise_for_status() d = r.json() rs = d.get("regulome_score", {}) vs = d.get("variants", []) return { "query": variant, "ranking": rs.get("ranking"), # 1a / 1b / ... / 7 "probability": float(rs.get("probability", 0)), "rsids": vs[0].get("rsids") if vs else [], "chrom": vs[0].get("chrom") if vs else None, "pos": vs[0].get("start") if vs else None, } print(regulome_score("rs4946036")) # {'query': 'rs4946036', 'ranking': '7', 'probability': 0.18412, # 'rsids': ['rs4946036'], 'chrom': 'chr6', 'pos': 114819799}
The GET /regulome-search/ endpoint accepts an rsID or coordinate as regions=. Returns a regulome_score block (probability, ranking, tissue-specific scores) plus features flags and the per-dataset @graph evidence rows.
pythonimport requests BASE = "https://regulomedb.org" def score_variant(variant, genome="GRCh38"): """Return the regulome_score block and resolved coordinates.""" r = requests.get( f"{BASE}/regulome-search/", params={"regions": variant, "genome": genome, "format": "json"}, timeout=30, ) r.raise_for_status() d = r.json() rs = d.get("regulome_score", {}) vs = d.get("variants", []) feats = d.get("features", {}) print(f"Variant : {variant}") print(f"Resolved : {vs[0]['chrom']}:{vs[0]['start']} ({', '.join(vs[0].get('rsids', []))})") print(f"Ranking : {rs.get('ranking')} prob={rs.get('probability')}") print(f"Features : ChIP={feats['ChIP']} Chromatin_accessibility={feats['Chromatin_accessibility']} " f"QTL={feats['QTL']} Footprint={feats['Footprint']} PWM_matched={feats['PWM_matched']}") return d # Strong-regulatory locus example score_variant("chr11:5226739-5226740") # Ranking: 1a (HBB beta-globin promoter, multi-evidence)
python# Score by chromosomal position alone score_variant("chr17:7670000-7670001") # TP53 region
A range query returns up to limit resolved variants (variants[]) and all @graph evidence rows in the window, plus nearby_snps (rsIDs adjacent to the resolved hits).
pythonimport requests, pandas as pd BASE = "https://regulomedb.org" def scan_region(chrom, start, end, genome="GRCh38", limit=200): """List variants in a region with their resolved positions and overlapping rsIDs.""" r = requests.get( f"{BASE}/regulome-search/", params={"regions": f"{chrom}:{start}-{end}", "genome": genome, "format": "json", "limit": limit}, timeout=60, ) r.raise_for_status() d = r.json() variants = d.get("variants", []) print(f"Variants in {chrom}:{start}-{end}: {len(variants)} (total indexed = {d.get('total')})") rows = [{"rsids": ", ".join(v.get("rsids", [])), "chrom": v.get("chrom"), "start": v.get("start"), "end": v.get("end")} for v in variants] return pd.DataFrame(rows) df = scan_region("chr11", 5226000, 5227000) print(df.head(10).to_string(index=False))
@graph RowsEach @graph[i] row is one experimental piece of evidence overlapping the query. Fields: method, target_label, biosample_ontology{term_name, organ_slims, classification}, dataset, file, value, chrom, start, end, strand, ancestry, disease_term_name.
pythonimport requests, pandas as pd BASE = "https://regulomedb.org" def evidence_rows(variant, genome="GRCh38"): r = requests.get( f"{BASE}/regulome-search/", params={"regions": variant, "genome": genome, "format": "json"}, timeout=60, ) r.raise_for_status() g = r.json().get("@graph", []) rows = [] for row in g: bs = row.get("biosample_ontology") or {} rows.append({ "method": row.get("method"), "target_label": row.get("target_label"), "biosample": bs.get("term_name"), "organ_slims": ", ".join(bs.get("organ_slims") or []), "dataset": row.get("dataset", "").split("/")[-2] if row.get("dataset") else None, "value": row.get("value"), }) return pd.DataFrame(rows) df_evidence = evidence_rows("chr11:5226739-5226740") # Each method is one of: ChIP-seq, Histone ChIP-seq, ATAC-seq, DNase-seq, # footprints, PWMs, chromatin state, eQTLs print(df_evidence["method"].value_counts())
To list the transcription factors binding near a variant, filter @graph rows where method == "ChIP-seq" and read target_label + biosample_ontology.term_name.
pythonimport requests, pandas as pd BASE = "https://regulomedb.org" def tf_binding(variant, genome="GRCh38"): r = requests.get(f"{BASE}/regulome-search/", params={"regions": variant, "genome": genome, "format": "json"}, timeout=60) r.raise_for_status() rows = [] for g in r.json().get("@graph", []): if g.get("method") != "ChIP-seq": continue bs = g.get("biosample_ontology") or {} rows.append({ "tf": g.get("target_label"), "biosample": bs.get("term_name"), "classification": bs.get("classification"), }) return pd.DataFrame(rows) df_tfs = tf_binding("chr11:5226739-5226740") print(f"TF ChIP-seq peaks overlapping query: {len(df_tfs)}") print(df_tfs.groupby("tf").size().sort_values(ascending=False).head(10))
regulome_score.tissue_specific_scores maps ~50 tissues to per-tissue regulatory probabilities (0–1). Rank tissues to identify where the variant has the strongest regulatory signal.
pythonimport requests, pandas as pd BASE = "https://regulomedb.org" def tissue_scores(variant, genome="GRCh38", top_n=10): r = requests.get(f"{BASE}/regulome-search/", params={"regions": variant, "genome": genome, "format": "json"}, timeout=30) r.raise_for_status() ts = r.json().get("regulome_score", {}).get("tissue_specific_scores", {}) s = pd.Series({k: float(v) for k, v in ts.items()}) return s.sort_values(ascending=False).head(top_n) print("Top tissues by regulatory probability for chr11:5226739-5226740:") print(tissue_scores("chr11:5226739-5226740"))
nearby_snps carries dbSNP rsIDs near the resolved coordinates, with reference/alt allele frequencies (when GnomAD-indexed).
pythonimport requests, pandas as pd BASE = "https://regulomedb.org" def nearby(variant, genome="GRCh38"): r = requests.get(f"{BASE}/regulome-search/", params={"regions": variant, "genome": genome, "format": "json"}, timeout=30) r.raise_for_status() rows = [] for s in r.json().get("nearby_snps", []): rows.append({ "rsid": s.get("rsid"), "chrom": s.get("chrom"), "pos": s.get("coordinates", {}).get("gte"), "type": s.get("variation_type"), "maf": s.get("maf"), }) return pd.DataFrame(rows) df_nearby = nearby("rs4946036") print(f"Nearby SNPs to rs4946036: {len(df_nearby)}") print(df_nearby.head(10).to_string(index=False))
RegulomeDB ranks encode the strength of evidence overlapping a variant. The regulome_score.ranking string is one of:
| Ranking | Evidence | Confidence | |---------|----------|------------| | 1a | eQTL + TF + DNase + motif + matched footprint | Highest | | 1b–1f | Multi-evidence (sub-ranks reflect which inputs match) | Very high | | 2a | TF binding + DNase + motif | High | | 2b | TF binding + any DNase (no motif required) | High | | 2c | TF binding + DNase (limited) | Moderate-high | | 3a | DNase + motif (no TF ChIP-seq) | Moderate | | 3b | Motif only (no DNase) | Moderate | | 4 | Single TF binding evidence | Low-moderate | | 5 | DNase peak only | Low | | 6 | Other regulatory evidence | Minimal | | 7 | No known regulatory function | None |
regulome_score.probability is the numeric model score (0–1) underlying the discrete ranking.
features Booleans vs @graph Detailfeatures is a high-level summary — boolean flags indicating presence of ChIP, Chromatin_accessibility, Footprint, PWM, QTL, etc. For per-dataset detail (which exact TF / cell type / experiment), iterate @graph[] and filter by method.
regions= accepts:
python# rsID — resolved server-side to current-build coordinates "rs4946036" # Single-position range "chr11:5226739-5226740" # Wider region (returns multiple variants[] entries + larger @graph) "chr11:5226000-5227000"
Goal: Score a list of GWAS lead SNPs and rank by regulatory confidence.
pythonimport requests, time, pandas as pd import matplotlib.pyplot as plt BASE = "https://regulomedb.org" gwas_snps = ["rs7903146", "rs10811661", "rs1801282", "rs4946036", "rs2268177", "rs10830963", "rs1111875"] records = [] for snp in gwas_snps: r = requests.get(f"{BASE}/regulome-search/", params={"regions": snp, "genome": "GRCh38", "format": "json"}, timeout=30) r.raise_for_status() d = r.json() rs = d.get("regulome_score", {}) feats = d.get("features", {}) g = d.get("@graph", []) tfs = sorted({row["target_label"] for row in g if row.get("method") == "ChIP-seq" and row.get("target_label")}) records.append({ "snp": snp, "ranking": rs.get("ranking"), "probability": float(rs.get("probability", 0)), "has_qtl": feats.get("QTL", False), "tf_count": len(tfs), "num_evidence_rows": len(g), }) time.sleep(0.3) df = pd.DataFrame(records).sort_values("probability", ascending=False) print(df.to_string(index=False)) df.to_csv("gwas_regulatory_priority.csv", index=False) fig, ax = plt.subplots(figsize=(8, 4)) ax.bar(df["snp"], df["probability"], color="steelblue", edgecolor="black") ax.set_ylabel("Regulatory probability") ax.set_title("GWAS lead-SNP regulatory probabilities") plt.xticks(rotation=45, ha="right") plt.tight_layout() plt.savefig("gwas_score_distribution.png", dpi=150, bbox_inches="tight")
Goal: Summarize the methods underlying the score at a locus (e.g., HBB promoter).
pythonimport requests, pandas as pd import matplotlib.pyplot as plt BASE = "https://regulomedb.org" def locus_profile(region, genome="GRCh38"): r = requests.get(f"{BASE}/regulome-search/", params={"regions": region, "genome": genome, "format": "json"}, timeout=60) r.raise_for_status() d = r.json() rs = d.get("regulome_score", {}) g = d.get("@graph", []) counts = pd.Series([row.get("method") for row in g]).value_counts() print(f"\n=== {region} | ranking={rs.get('ranking')} prob={rs.get('probability')} ===") print(counts.to_string()) return counts counts = locus_profile("chr11:5226739-5226740") # HBB fig, ax = plt.subplots(figsize=(8, 4)) counts.plot(kind="barh", color="seagreen", ax=ax) ax.set_xlabel("Evidence rows in @graph") ax.set_title("Regulatory evidence by method (HBB promoter)") plt.tight_layout() plt.savefig("locus_evidence_profile.png", dpi=150, bbox_inches="tight")
| Parameter | Endpoint | Default | Range / Options | Effect | |-----------|----------|---------|-----------------|--------| | regions | GET /regulome-search/ | required | rsID, chrN:start-end, or chrN:pos-pos | Variant/region to score | | genome | GET /regulome-search/ | "GRCh38" | "GRCh38", "GRCh37" | Reference genome assembly | | format | GET /regulome-search/ | "html" | "json", "tsv", "html" | Use "json" for programmatic access | | limit | GET /regulome-search/ | 200 | 1–1000 | Max resolved variants in variants[] for region queries | | from | GET /regulome-search/ | 0 | non-negative int | Offset for paging through large @graph lists |
/regulome-search/, POST /regulome-summary/, and GET /regulome-datasets/ JSON endpoints return a regulome-notfound stub or HTTP 500. Only GET /regulome-search/?regions=...&genome=...&format=json returns real data.regulome_score.ranking, not regulomedb_score. The field used to be named regulomedb_score in legacy docs; the live API exposes it as regulome_score.ranking (string like "1a", "7").time.sleep(0.3) between calls. RegulomeDB has no published rate limit, but polite spacing prevents intermittent 502s under load.encode-database for negative results.tissue_specific_scores. The single ranking is an aggregate; the per-tissue probabilities reveal where the variant has the strongest regulatory signal.regions=chrN:start-end queries are build-specific — pass the matching genome= value.When to use: One-off check before kicking off a larger pipeline.
pythonimport requests def quick_score(variant, genome="GRCh38"): r = requests.get("https://regulomedb.org/regulome-search/", params={"regions": variant, "genome": genome, "format": "json"}, timeout=20) r.raise_for_status() rs = r.json().get("regulome_score", {}) print(f"{variant}: ranking={rs.get('ranking')} prob={rs.get('probability')}") quick_score("rs4946036") # ranking=7 prob=0.18412 quick_score("chr11:5226739-5226740") # ranking=1a (HBB promoter)
pythonimport requests, time, pandas as pd HIGH_CONF = {"1a", "1b", "1c", "1d", "1e", "1f", "2a", "2b"} def high_conf_only(variants, genome="GRCh38"): keep = [] for v in variants: r = requests.get("https://regulomedb.org/regulome-search/", params={"regions": v, "genome": genome, "format": "json"}, timeout=30) ranking = r.json().get("regulome_score", {}).get("ranking") if ranking in HIGH_CONF: keep.append({"variant": v, "ranking": ranking}) time.sleep(0.3) return pd.DataFrame(keep) df = high_conf_only(["rs4946036", "rs7903146", "chr11:5226739-5226740"]) print(df.to_string(index=False))
When to use: Find variants with features.QTL == True, i.e. those overlapping a curated QTL row in @graph (method "QTLs").
pythonimport requests, time, pandas as pd def qtl_overlap(variants, genome="GRCh38"): rows = [] for v in variants: r = requests.get("https://regulomedb.org/regulome-search/", params={"regions": v, "genome": genome, "format": "json"}, timeout=30) d = r.json() if not d.get("features", {}).get("QTL"): time.sleep(0.3); continue for g in d.get("@graph", []): if g.get("method") == "QTLs": rows.append({ "variant": v, "ranking": d.get("regulome_score", {}).get("ranking"), "qtl_target": g.get("target_label"), "value": g.get("value"), "biosample": (g.get("biosample_ontology") or {}).get("term_name"), }) time.sleep(0.3) return pd.DataFrame(rows) print(qtl_overlap(["rs4946036", "chr11:5226739-5226740"]))
| Problem | Cause | Solution | |---------|-------|----------| | Response body {"@id":"/regulome-notfound","@type":["regulome-help"]...} | Using the legacy POST /regulome-search/ with a JSON body | Switch to GET with regions= query param + format=json | | HTTP 500 | Hitting the deprecated /regulome-summary/ endpoint | Endpoint is dead; aggregate counts client-side from @graph[].method | | KeyError: 'regulomedb_score' | Field renamed | Use regulome_score.ranking (string) and regulome_score.probability (float-as-string) | | peaks / eqtls / assay_type missing | Old schema | Iterate @graph[] and filter by method (ChIP-seq, DNase-seq, Histone ChIP-seq, ATAC-seq, footprints, PWMs, QTLs, chromatin state) | | Empty variants[] for an rsID | rsID not in RegulomeDB index or build mismatch | Try the chr:pos form; check genome= matches the coordinates | | Region search returns 0 @graph rows | Region size too small or in an uncharacterized chromosome | Widen the window to ≥ 200 bp; avoid alt contigs (chrUn_*, *_random) | | Region query truncates at 200 results | Default limit=200 | Pass limit=1000 or page with from=0,200,400,... |
gwas-database — NHGRI-EBI GWAS Catalog for published SNP-trait associations; pair with RegulomeDB to prioritize GWAS hitsclinvar-database — Clinical pathogenicity classifications; complements RegulomeDB's functional regulatory evidenceencode-database — Direct ENCODE REST API access for the TF ChIP-seq / ATAC-seq peak sets that underlie RegulomeDB scoresensembl-database — Variant annotation and gene coordinate lookup; use to map rsIDs to genomic positions before region queries| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-01 | fail→pass | 19,516 | 9,524 | -51% | 1 | 1 | 0% | 3,817 | 8,296 | +117% | 0 | 0 | — |
case-02 | fail→pass | 12,484 | 10,906 | -13% | 1 | 1 | 0% | 2,392 | 8,757 | +266% | 0 | 0 | — |
case-03 | fail→pass | 15,470 | 14,075 | -9% | 1 | 1 | 0% | 3,086 | 9,175 | +197% | 0 | 0 | — |
case-04 | pass→pass | 10,526 | 6,359 | -40% | 1 | 1 | 0% | 1,955 | 7,700 | +294% | 0 | 0 | — |
case-05 | pass→pass | 11,385 | 7,643 | -33% | 1 | 1 | 0% | 2,131 | 8,001 | +275% | 0 | 0 | — |
case-06 | pass→pass | 14,148 | 5,611 | -60% | 1 | 1 | 0% | 2,781 | 7,604 | +173% | 0 | 0 | — |
case-07 | fail→pass | 18,461 | 7,341 | -60% | 1 | 1 | 0% | 3,453 | 7,898 | +129% | 0 | 0 | — |
case-08 | fail→pass | 18,319 | 6,129 | -67% | 1 | 1 | 0% | 3,820 | 7,689 | +101% | 0 | 0 | — |
case-09 | pass→pass | 7,770 | 2,941 | -62% | 1 | 1 | 0% | 1,380 | 6,910 | +401% | 0 | 0 | — |
case-10 | pass→pass | 11,963 | 8,007 | -33% | 1 | 1 | 0% | 1,911 | 7,768 | +306% | 0 | 0 | — |
case-11 | fail→pass | 10,041 | 5,580 | -44% | 1 | 1 | 0% | 1,594 | 7,544 | +373% | 0 | 0 | — |
case-12 | pass→pass | 17,911 | 4,643 | -74% | 1 | 1 | 0% | 3,105 | 7,340 | +136% | 0 | 0 | — |
case-13 | pass→pass | 16,658 | 10,671 | -36% | 1 | 1 | 0% | 3,094 | 8,458 | +173% | 0 | 0 | — |
case-14 | fail→pass | 13,700 | 6,884 | -50% | 1 | 1 | 0% | 2,531 | 7,595 | +200% | 0 | 0 | — |
case-15 | fail→pass | 16,976 | 4,427 | -74% | 1 | 1 | 0% | 3,315 | 7,362 | +122% | 0 | 0 | — |
case-16 | fail→pass | 8,733 | 4,574 | -48% | 1 | 1 | 0% | 1,674 | 7,330 | +338% | 0 | 0 | — |
case-17 | fail→pass | 16,381 | 12,718 | -22% | 1 | 1 | 0% | 2,947 | 8,971 | +204% | 0 | 0 | — |
case-18 | fail→pass | 16,987 | 8,020 | -53% | 1 | 1 | 0% | 3,236 | 8,076 | +150% | 0 | 0 | — |
case-19 | pass→pass | 14,768 | 6,549 | -56% | 1 | 1 | 0% | 2,051 | 7,475 | +264% | 0 | 0 | — |
case-20 | pass→pass | 17,657 | 12,563 | -29% | 1 | 1 | 0% | 3,187 | 8,778 | +175% | 0 | 0 | — |
case-21 | pass→pass | 24,713 | 14,687 | -41% | 1 | 1 | 0% | 4,773 | 9,285 | +95% | 0 | 0 | — |
case-22 | pass→pass | 21,288 | 18,093 | -15% | 1 | 1 | 0% | 4,020 | 9,732 | +142% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted. The headline lift of +50 percentage points is the difference between those two pass rates over the 22 comparable cases.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.