Query the ClinPGx (formerly PharmGKB) REST API plus the CPIC PostgREST companion API for pharmacogenomic clinical annotations, CPIC/DPWG dosing guidelines, gene-drug pairs, variant-drug associations, FDA/EMA drug labels, and PGx pathways. Two-host architecture: api.clinpgx.org for annotation records, api.cpicpgx.org for genotype→recommendation lookups. No auth. For germline pathogenicity use clinvar-database; for somatic cancer PGx use cosmic-database or opentargets-database; for drug bioacti...
Scanned 9/5/2026
Install to Claude Code
npx -y skills add jaechang-hits/SciAgent-Skills --skill clinpgx-database --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Clinpgx Database?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/jaechang-hits-clinpgx-database)More formats (shields.io, HTML) on the badges page.
---
name: "clinpgx-database"
description: "Query the ClinPGx (formerly PharmGKB) REST API plus the CPIC PostgREST companion API for pharmacogenomic clinical annotations, CPIC/DPWG dosing guidelines, gene-drug pairs, variant-drug associations, FDA/EMA drug labels, and PGx pathways. Two-host architecture: api.clinpgx.org for annotation records, api.cpicpgx.org for genotype→recommendation lookups. No auth. For germline pathogenicity use clinvar-database; for somatic cancer PGx use cosmic-database or opentargets-database; for drug bioactivity use chembl-database-bioactivity."
license: "CC-BY-SA-4.0"
---
# ClinPGx (PharmGKB) Pharmacogenomics Database
## Overview
PharmGKB rebranded as **ClinPGx** in 2024 and the API moved from `api.pharmgkb.org` to `api.clinpgx.org`. The old host now returns 404/405; every example here uses the new endpoints. Two complementary APIs are used together:
- **ClinPGx Data API** (`api.clinpgx.org/v1`) — record-style access to genes, drugs, variants, clinical annotations, guideline annotations, drug labels, and pathways. Responses wrap data as `{"data": [...], "status": "success"}`. Filters use dotted property paths (e.g. `relatedChemicals.name=clopidogrel`, `levelOfEvidence.term=1A`).
- **CPIC PostgREST API** (`api.cpicpgx.org/v1`) — relational lookup of genotype → drug recommendation rows. PostgREST filter syntax (`column=eq.value`, JSON `cs.{...}` for jsonb containment). Returns flat JSON arrays.
Use ClinPGx for *what is known* about a gene/drug/variant; use CPIC for *how to prescribe* given a phenotype. The pattern is `ClinPGx for annotations, CPIC for recommendations`.
## When to Use
- Retrieving CPIC genotype-specific dosing recommendations for a gene-drug pair (e.g., CYP2C19 + clopidogrel) — use CPIC
- Looking up all pharmacogenomic clinical annotations for a drug or evidence level — use ClinPGx `data/clinicalAnnotation`
- Finding all CPIC/DPWG guideline annotations for a pharmacogene — use ClinPGx `data/guidelineAnnotation`
- Resolving a gene symbol, drug name, or rsID to ClinPGx PA identifiers — use `data/{gene,drug,variant}`
- Free-text search across all ClinPGx record types (genes, drugs, variants, annotations) — use `POST /site/search`
- Retrieving FDA/EMA pharmacogenomic drug label annotations — use ClinPGx `data/label`
- Building precision-medicine prescribing workflows that combine annotation evidence with phenotype-specific recommendations
- For germline disease pathogenicity (not PGx) use `clinvar-database`
- For somatic cancer pharmacogenomics use `cosmic-database` or `opentargets-database`
## Prerequisites
- **Python packages**: `requests`, `pandas` — both already in standard environments
- **Data requirements**: HGNC gene symbols, drug names (lowercase generic), dbSNP rsIDs, or PA identifiers
- **Environment**: internet connection; no authentication required for either host
- **Rate limits**: the ClinPGx host occasionally returns HTTP 429; insert `time.sleep(0.3–0.5)` between sequential calls. CPIC is more permissive.
If you are inside a pixi/conda environment that already provides `requests` and `pandas`, skip the install — invoke scripts with `pixi run python ...`.
```bash
pip install requests pandas
```
## Quick Start
```python
import requests
CLINPGX = "https://api.clinpgx.org/v1"
CPIC = "https://api.cpicpgx.org/v1"
# CPIC genotype → recommendation: clopidogrel + CYP2C19 Poor Metabolizer
drug = requests.get(f"{CPIC}/drug", params={"name": "eq.clopidogrel"}).json()[0]
recs = requests.get(f"{CPIC}/recommendation",
params={"drugid": f"eq.{drug['drugid']}",
"phenotypes": 'cs.{"CYP2C19":"Poor Metabolizer"}'}).json()
print(f"clopidogrel CYP2C19=PM: {len(recs)} recommendation(s)")
for rec in recs[:2]:
print(f" [{rec['classification']}] {rec['drugrecommendation'][:80]}…")
# ClinPGx side: how many CPIC guideline annotations cover CYP2C19?
glines = requests.get(f"{CLINPGX}/data/guidelineAnnotation",
params={"relatedGenes.symbol": "CYP2C19",
"source": "CPIC", "view": "base"}).json()["data"]
print(f"CYP2C19 CPIC guidelines: {len(glines)}")
```
## Core API
### Module 1: Free-text site search
`POST /site/search` with a JSON body `{"query": "<term>"}` is the canonical entry point when you don't know the PA ID. It searches across drugs, genes, variants, clinical annotations, guideline annotations, and labels in one shot.
```python
import requests
CLINPGX = "https://api.clinpgx.org/v1"
r = requests.post(f"{CLINPGX}/site/search",
json={"query": "rs4149056"}, timeout=15)
r.raise_for_status()
hits = r.json()["data"]["hits"]
print(f"Total hits: {r.json()['data']['total']}")
for h in hits[:5]:
print(f" id={h.get('id')} name={h.get('name')[:80]}")
```
```python
# Broader concept search
r = requests.post(f"{CLINPGX}/site/search",
json={"query": "TPMT azathioprine"}, timeout=15)
hits = r.json()["data"]["hits"]
print(f"TPMT+azathioprine hits: {len(hits)}")
for h in hits[:5]:
print(f" {h.get('id'):>15} {h.get('name','')[:80]}")
```
### Module 2: Gene, drug, and variant record lookup
The `/data/{type}` endpoints accept simple property filters. All return `{"data": [...], "status": "success"}` — use `view=base` for summary, `view=max` for full nested objects.
```python
import requests
CLINPGX = "https://api.clinpgx.org/v1"
# Gene by HGNC symbol
gene = requests.get(f"{CLINPGX}/data/gene",
params={"symbol": "CYP2D6", "view": "base"}).json()["data"][0]
print(f"{gene['symbol']} id={gene['id']} {gene['name']}")
# Drug by name (lowercase generic preferred)
drug = requests.get(f"{CLINPGX}/data/drug",
params={"name": "warfarin", "view": "base"}).json()["data"][0]
print(f"{drug['name']} id={drug['id']}")
# Variant by rsID
var = requests.get(f"{CLINPGX}/data/variant",
params={"name": "rs4149056", "view": "base"}).json()["data"][0]
print(f"{var['name']} id={var['id']} significance={var.get('clinicalSignificance')}")
```
```python
# Direct record fetch when you already have a PA ID
r = requests.get(f"{CLINPGX}/data/drug/PA449088", params={"view": "max"}).json()
d = r["data"]
print(f"PA449088 → {d['name']} (objCls={d['objCls']})")
```
### Module 3: Clinical annotations
`data/clinicalAnnotation` records associate a variant (`location`) with one or more drugs (`relatedChemicals`) and an evidence level (`levelOfEvidence.term`). The two supported filters are `relatedChemicals.name=` and `levelOfEvidence.term=`. There is **no working `gene=` filter on this endpoint** — see Module 4 for gene-driven access.
```python
import requests, pandas as pd
CLINPGX = "https://api.clinpgx.org/v1"
# All clinical annotations for clopidogrel
data = requests.get(f"{CLINPGX}/data/clinicalAnnotation",
params={"relatedChemicals.name": "clopidogrel",
"view": "base"}).json()["data"]
print(f"clopidogrel annotations: {len(data)}")
rows = []
for ann in data[:10]:
loc = ann.get("location") or {}
drugs = ", ".join(c.get("name", "") for c in ann.get("relatedChemicals", []))
rows.append({
"id": ann["id"],
"variant": loc.get("displayName"),
"gene": (loc.get("genes") or [{}])[0].get("symbol"),
"drug": drugs,
"level": (ann.get("levelOfEvidence") or {}).get("term"),
"score": ann.get("score"),
})
print(pd.DataFrame(rows).to_string(index=False))
```
```python
# All Level 1A clinical annotations (highest evidence)
data = requests.get(f"{CLINPGX}/data/clinicalAnnotation",
params={"levelOfEvidence.term": "1A",
"view": "base"}).json()["data"]
print(f"Level 1A annotations: {len(data)}")
drug_to_count = {}
for ann in data:
for c in ann.get("relatedChemicals") or []:
drug_to_count[c["name"]] = drug_to_count.get(c["name"], 0) + 1
top = sorted(drug_to_count.items(), key=lambda x: -x[1])[:10]
for d, n in top:
print(f" {n:3} {d}")
```
### Module 4: Guideline annotations (gene-driven access)
`data/guidelineAnnotation` supports both `relatedGenes.symbol=` and `relatedChemicals.name=`, plus `source=` (`CPIC`, `DPWG`, `CPNDS`, `RNPGx`). This is the canonical way to get gene→guideline coverage.
```python
import requests
CLINPGX = "https://api.clinpgx.org/v1"
# All CPIC guidelines mentioning CYP2C19
data = requests.get(f"{CLINPGX}/data/guidelineAnnotation",
params={"relatedGenes.symbol": "CYP2C19",
"source": "CPIC",
"view": "base"}).json()["data"]
print(f"CYP2C19 CPIC guidelines: {len(data)}")
for g in data[:5]:
print(f" PA{g['id']}: {g['name'][:80]}")
```
```python
# Guidelines for a specific drug across all bodies (CPIC, DPWG, …)
data = requests.get(f"{CLINPGX}/data/guidelineAnnotation",
params={"relatedChemicals.name": "clopidogrel",
"view": "base"}).json()["data"]
by_source = {}
for g in data:
for s in (g.get("crossReferences") or []):
by_source.setdefault(s.get("resource", "?"), 0)
by_source[s["resource"]] = by_source.get(s["resource"], 0) + 1
print(f"clopidogrel guidelines: {len(data)} ({list({g.get('source') for g in data})})")
```
### Module 5: Regulatory drug labels (FDA / EMA)
`data/label` records are PharmGKB-curated annotations of FDA/EMA pharmacogenomic labeling. Filter by `relatedChemicals.name=` and `source=` (`FDA`, `EMA`, `HCSC`, `PMDA`, `Swissmedic`).
```python
import requests, pandas as pd
CLINPGX = "https://api.clinpgx.org/v1"
data = requests.get(f"{CLINPGX}/data/label",
params={"relatedChemicals.name": "warfarin",
"source": "FDA",
"view": "base"}).json()["data"]
print(f"warfarin FDA labels: {len(data)}")
rows = [{
"name": d["name"][:60],
"biomarker_status": d.get("biomarkerStatus"),
"testing_required": d.get("testingRequired"),
"alternate_drug": d.get("alternateDrugAvailable"),
} for d in data]
print(pd.DataFrame(rows).to_string(index=False))
```
### Module 6: CPIC genotype → recommendation chain
CPIC's PostgREST API uses `column=eq.value` for equality and `column=cs.{...}` for JSONB containment. The standard lookup chain is `drug → drugid → recommendation`, optionally filtered by phenotype.
```python
import requests
CPIC = "https://api.cpicpgx.org/v1"
# Resolve drug name to drugid (RxNorm-prefixed)
drug = requests.get(f"{CPIC}/drug",
params={"name": "eq.clopidogrel"}).json()[0]
print(f"clopidogrel drugid: {drug['drugid']}")
# All phenotype-specific recommendations for clopidogrel
recs = requests.get(f"{CPIC}/recommendation",
params={"drugid": f"eq.{drug['drugid']}"}).json()
print(f"Total recommendations: {len(recs)}")
for rec in recs[:3]:
print(f" {rec['phenotypes']} [{rec['classification']}]")
print(f" {rec['drugrecommendation'][:90]}…")
```
```python
# Phenotype filter via jsonb containment (cs.{...})
# The phenotypes column is a jsonb dict; cs. checks that the query is a subset.
recs = requests.get(f"{CPIC}/recommendation",
params={"drugid": f"eq.{drug['drugid']}",
"phenotypes": 'cs.{"CYP2C19":"Poor Metabolizer"}'}
).json()
for rec in recs:
print(f" [{rec['classification']}] {rec['drugrecommendation'][:90]}…")
# Gene-driven: list every drug with a CPIC pair for CYP2C19
pairs = requests.get(f"{CPIC}/pair",
params={"genesymbol": "eq.CYP2C19"}).json()
print(f"\nCYP2C19 CPIC pairs: {len(pairs)}")
drug_ids = sorted({p["drugid"] for p in pairs})
print(f"Sample drug IDs: {drug_ids[:5]}")
```
## Key Concepts
### Two-host architecture
| Question | Use | Why |
| ----------------------------------------------- | ----------------------------------- | -------------------------------------------------------------------------------- |
| What clinical annotations exist for this drug? | ClinPGx `data/clinicalAnnotation` | Annotation-level evidence with curated levelOfEvidence.term |
| What CPIC guidelines cover this gene? | ClinPGx `data/guidelineAnnotation` | Filter by `relatedGenes.symbol`; no working `gene=` filter on clinicalAnnotation |
| Given phenotype X, what should I prescribe? | CPIC `recommendation` + `phenotypes`| Structured genotype→action rows; CPIC is the prescribing-rule oracle |
| What FDA labels mention this drug + gene? | ClinPGx `data/label?source=FDA` | Curated regulatory PGx labeling |
| Free-text "anything about X" | ClinPGx `POST /site/search` | Cross-record-type fan-out |
### PharmGKB / ClinPGx evidence levels
Levels 1A → 4 in decreasing evidence quality:
- **1A** — Annotation of a variant–drug pair in a clinical guideline or FDA label (strongest)
- **1B** — Significant association replicated in multiple studies
- **2A** — Variant in a known PGx gene, significant association
- **2B** — Moderate evidence, often single study
- **3** — Limited evidence (single study or unreplicated)
- **4** — Case reports / biological plausibility only
Filter via `levelOfEvidence.term` on `data/clinicalAnnotation`. The term is a *string*, not an enum (`"1A"` not `1A`).
### ClinPGx response envelope and view modes
Every ClinPGx `/data/...` response is `{"data": [...] | {...}, "status": "success" | "fail"}`. On failure the body is `{"status": "fail", "data": {"errors": [{"message": "..."}]}}` — always read both keys.
- `view=base` (default) — flat summary record; recommended for bulk filters
- `view=max` — full nested objects (relatedDiseases, allelePhenotypes, scoreDetails, …). Larger payload, slower; use only for single-record details.
## Common Workflows
### Workflow 1: Pharmacogene panel CPIC coverage
**Goal:** Given a patient's pharmacogene panel, count how many CPIC guideline annotations cover each gene.
```python
import requests, pandas as pd, time
CLINPGX = "https://api.clinpgx.org/v1"
pharmacogenes = ["CYP2D6", "CYP2C19", "CYP2C9", "DPYD", "TPMT", "SLCO1B1"]
rows = []
for g in pharmacogenes:
data = requests.get(f"{CLINPGX}/data/guidelineAnnotation",
params={"relatedGenes.symbol": g,
"source": "CPIC", "view": "base"},
timeout=20).json()["data"]
drugs = sorted({c["name"] for guideline in data
for c in (guideline.get("relatedChemicals") or [])})
rows.append({"gene": g, "cpic_guidelines": len(data),
"n_drugs": len(drugs), "sample": ", ".join(drugs[:3])})
time.sleep(0.3)
df = pd.DataFrame(rows).sort_values("cpic_guidelines", ascending=False)
print(df.to_string(index=False))
df.to_csv("pharmacogene_cpic_coverage.csv", index=False)
```
### Workflow 2: Drug panel — CPIC prescribing rule lookup
**Goal:** Given a prescribed drug list, identify which have CPIC genotype-specific recommendations and surface the rule rows.
```python
import requests, pandas as pd, time
CPIC = "https://api.cpicpgx.org/v1"
drugs = ["warfarin", "clopidogrel", "codeine", "simvastatin",
"metoprolol", "omeprazole", "azathioprine", "tacrolimus"]
rows = []
for name in drugs:
drug = requests.get(f"{CPIC}/drug", params={"name": f"eq.{name}"}, timeout=15).json()
if not drug:
rows.append({"drug": name, "in_cpic": False, "n_recs": 0, "phenotypes": ""}); continue
did = drug[0]["drugid"]
recs = requests.get(f"{CPIC}/recommendation",
params={"drugid": f"eq.{did}"}, timeout=15).json()
phens = sorted({f"{k}={v}" for rec in recs
for k, v in (rec.get("phenotypes") or {}).items()})
rows.append({"drug": name, "in_cpic": True, "n_recs": len(recs),
"phenotypes": "; ".join(phens[:3])})
time.sleep(0.3)
df = pd.DataFrame(rows).sort_values(["in_cpic", "n_recs"], ascending=[False, False])
print(df.to_string(index=False))
```
### Workflow 3: Variant → drug interactions (rsID-driven)
**Goal:** Starting from a single rsID (e.g., SLCO1B1 *5 = rs4149056), find every clinical annotation that involves it.
The Data API does **not** accept rsID as a filter property. Use `POST /site/search` to discover related annotation IDs, then fetch each by ID.
```python
import requests
CLINPGX = "https://api.clinpgx.org/v1"
rsid = "rs4149056"
hits = requests.post(f"{CLINPGX}/site/search",
json={"query": rsid}, timeout=15).json()["data"]["hits"]
print(f"{rsid}: {len(hits)} hits")
# Filter hits that look like clinical annotations
ann_hits = [h for h in hits if h.get("name", "").lower().startswith("clinical annotation")]
print(f"Clinical-annotation hits: {len(ann_hits)}")
for h in ann_hits[:5]:
print(f" id={h['id']} {h['name'][:90]}")
# Dereference one annotation by ID for full detail
if ann_hits:
ann = requests.get(f"{CLINPGX}/data/clinicalAnnotation/{ann_hits[0]['id']}",
params={"view": "max"}, timeout=15).json()["data"]
drugs = ", ".join(c["name"] for c in (ann.get("relatedChemicals") or []))
print(f"\nFirst annotation:")
print(f" drugs: {drugs}")
print(f" level: {(ann.get('levelOfEvidence') or {}).get('term')}")
```
## Key Parameters
| Parameter | Module / Endpoint | Default | Range / Options | Effect |
| --------------------------- | ---------------------------------------------- | ------- | -------------------------------------------------------- | ----------------------------------------------------------------------- |
| `view` | all `/data/...` | `base` | `base`, `min`, `max` | Field detail level; `max` includes all nested arrays (slow but complete) |
| `relatedChemicals.name` | `clinicalAnnotation`, `variantAnnotation`, `guidelineAnnotation`, `label`, `pathway` | — | lowercase generic drug name | Filter records related to a drug |
| `relatedGenes.symbol` | `guidelineAnnotation`, `pathway` | — | HGNC gene symbol | Filter records related to a gene (not available on `clinicalAnnotation`) |
| `levelOfEvidence.term` | `clinicalAnnotation` | — | `"1A"`, `"1B"`, `"2A"`, `"2B"`, `"3"`, `"4"` | Minimum evidence level |
| `source` | `guidelineAnnotation`, `label` | — | `CPIC`, `DPWG`, `FDA`, `EMA`, `HCSC`, `PMDA`, `Swissmedic` | Issuing body |
| `symbol` | `data/gene` | — | HGNC gene symbol | Gene record lookup |
| `name` | `data/drug`, `data/variant` | — | drug name or rsID | Record lookup by canonical name |
| CPIC `column=eq.value` | all `api.cpicpgx.org/v1/...` | — | PostgREST equality | Filter by exact match |
| CPIC `phenotypes=cs.{json}` | `recommendation` | — | JSON-encoded jsonb subset | Filter by phenotype containment (must URL-encode if special chars) |
## Best Practices
1. **Resolve PA identifiers once.** Never hand-construct ClinPGx PA IDs. Call `data/{type}?{symbol|name}=...` (or `site/search`) once and cache the returned `id` for reuse — `gene/PA128` for CYP2D6, `drug/PA449088` for clopidogrel, `variant/PA166154579` for rs4149056.
2. **Pick the right host for the question.** Use ClinPGx for `what is annotated` and CPIC for `what to prescribe`. Trying to derive genotype-specific recommendations from ClinPGx alone misses the structured `recommendation.phenotypes` rows.
3. **Filter by evidence level upfront** when building clinical workflows. `levelOfEvidence.term=1A` returns 312 actionable annotations across all of ClinPGx; Level 3/4 records are exploratory and shouldn't drive prescribing.
4. **Don't filter `clinicalAnnotation` by gene — filter by `guidelineAnnotation`** with `relatedGenes.symbol`. The `clinicalAnnotation` endpoint has no working gene property and returns HTTP 400 for any attempt.
5. **Use `view=base` for bulk filters, `view=max` for single-record drill-downs.** A list query with `view=max` can time out or hit 429; the difference is roughly 5–10× payload size.
6. **Throttle the ClinPGx host.** Insert `time.sleep(0.3)` between sequential queries in loops; the API returns occasional HTTP 429s on tight loops. CPIC tolerates faster iteration.
7. **URL-encode `cs.{...}` jsonb filters** when phenotype values contain spaces or special characters. `requests.get(..., params={"phenotypes": 'cs.{"CYP2C19":"Poor Metabolizer"}'})` works because `requests` does the encoding; a manual URL string needs `urllib.parse.quote`.
## Common Recipes
### Recipe 1 — Free-text discovery via site/search
When to use: you have an arbitrary string (rsID, drug name, gene, allele) and want to find related ClinPGx records without knowing which endpoint to hit.
```python
import requests
r = requests.post("https://api.clinpgx.org/v1/site/search",
json={"query": "VKORC1 warfarin"}, timeout=15)
hits = r.json()["data"]["hits"]
for h in hits[:10]:
print(f" {h.get('id'):>15} {h.get('name','')[:80]}")
```
### Recipe 2 — Top drugs by Level 1A annotation count
When to use: build a leaderboard of the most actionable PGx drugs.
```python
import requests, pandas as pd
data = requests.get("https://api.clinpgx.org/v1/data/clinicalAnnotation",
params={"levelOfEvidence.term": "1A", "view": "base"},
timeout=30).json()["data"]
counts = {}
for ann in data:
for c in ann.get("relatedChemicals") or []:
counts[c["name"]] = counts.get(c["name"], 0) + 1
df = pd.DataFrame(sorted(counts.items(), key=lambda x: -x[1]),
columns=["drug", "n_1A_annotations"]).head(15)
print(df.to_string(index=False))
```
### Recipe 3 — Patient genotype → drug recommendations
When to use: given a phenotype call from a PGx test, surface every CPIC recommendation row.
```python
import requests
CPIC = "https://api.cpicpgx.org/v1"
genotype = {"CYP2C19": "Poor Metabolizer"}
drug = "clopidogrel"
did = requests.get(f"{CPIC}/drug", params={"name": f"eq.{drug}"}).json()[0]["drugid"]
import json
recs = requests.get(f"{CPIC}/recommendation",
params={"drugid": f"eq.{did}",
"phenotypes": f"cs.{json.dumps(genotype)}"}).json()
for rec in recs:
print(f"[{rec['classification']}] {rec['drugrecommendation']}")
print(f" implications: {rec['implications']}")
```
### Recipe 4 — Robust session with retry
When to use: long-running loops over many genes / drugs / variants.
```python
import requests
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry
s = requests.Session()
s.headers.update({"Accept": "application/json"})
s.mount("https://", HTTPAdapter(max_retries=Retry(
total=4, backoff_factor=1.0,
status_forcelist=[429, 500, 502, 503, 504],
allowed_methods=["GET", "POST"])))
r = s.get("https://api.clinpgx.org/v1/data/gene",
params={"symbol": "CYP2D6", "view": "base"}, timeout=20)
r.raise_for_status()
print(r.json()["data"][0]["name"])
```
## Troubleshooting
| Problem | Cause | Solution |
| ----------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- |
| HTTP 404/405 on `https://api.pharmgkb.org/v1/...` | Old PharmGKB host is dead; the service rebranded to ClinPGx in 2024 | Migrate to `https://api.clinpgx.org/v1/...`. Old `/clinicalAnnotation?gene=X` is now `data/clinicalAnnotation` with different filters. |
| `{"status":"fail","data":{"errors":[{"message":"No such property: 'gene'"}]}}` | `data/clinicalAnnotation` does **not** accept `gene=` or `relatedGenes.symbol=` | Use `data/guidelineAnnotation?relatedGenes.symbol=X` for gene-driven access, or `?relatedChemicals.name=Y` for drug-driven. |
| `{"status":"fail","data":{"errors":[{"message":"Missing criteria."}]}}` | A `data/{type}` list query has no filter and no ID | Add at least one filter (`name=`, `symbol=`, `relatedChemicals.name=`, …) or fetch by ID via `data/{type}/{paId}`. |
| HTTP 405 on `GET /site/search?query=...` | `site/search` only accepts POST with a JSON body | Use `requests.post(url, json={"query": "..."})`. |
| HTTP 429 mid-loop | Hit ClinPGx rate limit | Insert `time.sleep(0.3–0.5)` between calls; use the Retry session in Recipe 4. |
| HTTP 400 on `https://api.cpicpgx.org/v1/recommendation?phenotypes=cs.{...}` | The `cs.` JSON wasn't URL-encoded | Pass via `requests` `params={"phenotypes": 'cs.{"CYP2C19":"Poor Metabolizer"}'}` (auto-encoded) or `urllib.parse.quote` manually. |
| Empty `data` list for an obviously-real drug | Drug name mismatch (brand vs. generic; capitalization) | Try lowercase generic name; fall back to `POST /site/search` to fan out and find the canonical PA ID. |
| `data/variant?name=rs...` returns 1 record but `data/clinicalAnnotation?location.name=rs...` returns 404 | rsID is stored under `location.displayName`/`location.rsid`, not exposed as a filterable property | Use `site/search` to discover annotation IDs by rsID, then dereference each with `data/clinicalAnnotation/{id}`. (Workflow 3.) |
## Related Skills
- `clinvar-database` — germline pathogenicity / clinical significance for variants found in PharmGKB (complementary; ClinVar is disease-focused, ClinPGx is drug-response-focused)
- `opentargets-database` — drug-target associations and safety signals overlapping ClinPGx pharmacogene targets
- `chembl-database-bioactivity` — bioactivity and binding data for the drugs annotated in ClinPGx
- `cosmic-database` — somatic cancer mutations and tumor-specific PGx (orthogonal to germline PGx covered here)
## References
- [ClinPGx (formerly PharmGKB) website](https://www.clinpgx.org/) — Database front end; web links match the `data/{type}/{paId}` URL shape
- [ClinPGx REST API documentation](https://www.clinpgx.org/page/webResources) — Official endpoint reference, `data/...` and `site/search` schemas
- [CPIC API documentation](https://api.cpicpgx.org/) — Swagger UI for the PostgREST companion API
- [CPIC guidelines](https://cpicpgx.org/guidelines/) — Source of `recommendation` rows; canonical genotype-prescribing oracle
- [Relling & Klein (2011) Nature Reviews Drug Discovery — PharmGKB foundational paper](https://doi.org/10.1038/nrd3499)
- [Whirl-Carrillo et al. (2021) Clin Pharmacol Ther — PharmGKB data architecture update](https://doi.org/10.1002/cpt.2350)
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!