Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Query Open Targets GraphQL API for target-disease associations, evidence, drug links, safety. Search targets by gene, diseases by EFO ID; scores from 20+ sources, drug mechanisms, tractability. For ChEMBL use chembl-database-bioactivity; for trials use clinicaltrials-database-search.
.claude/skills/jaechang-hits-opentargets-database/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-02 | ✗→✓ | ▲ Improved | 126% | 0% |
| case-03 | ✗→✓ | ▲ Improved | 221% | 0% |
| case-08 | ✗→✓ | ▲ Improved | 313% | 0% |
| case-09 | ✗→✓ | ▲ Improved | 233% | 0% |
| case-10 | ✗→✓ | ▲ Improved | 135% | 0% |
Open Targets Platform integrates evidence from genetics, genomics, literature, and drug databases to systematically score target-disease associations for 60,000+ targets and 20,000+ diseases/phenotypes. The public GraphQL API (no authentication required) provides access to association scores, evidence from 20+ data sources (GWAS, ClinVar, ChEMBL, drugs, pathways, mouse models, expression), and detailed drug-target-disease triangles.
chembl-database-bioactivity; for clinical trial details use clinicaltrials-database-searchrequestsbashpip install requests
pythonimport requests OT_URL = "https://api.platform.opentargets.org/api/v4/graphql" def ot_query(gql, variables=None): r = requests.post(OT_URL, json={"query": gql, "variables": variables or {}}) r.raise_for_status() return r.json()["data"] # Top disease associations for BRCA1 query = """ query TargetDiseases($ensgId: String!) { target(ensemblId: $ensgId) { id approvedSymbol associatedDiseases(page: {index: 0, size: 5}) { rows { disease { id name } score } } } } """ data = ot_query(query, {"ensgId": "ENSG00000012048"}) target = data["target"] print(f"Target: {target['approvedSymbol']}") for row in target["associatedDiseases"]["rows"]: print(f" {row['disease']['name']}: {row['score']:.3f}")
Search for a target and retrieve basic metadata (Ensembl ID, biotype, description).
pythonimport requests OT_URL = "https://api.platform.opentargets.org/api/v4/graphql" def ot_query(gql, variables=None): r = requests.post(OT_URL, json={"query": gql, "variables": variables or {}}) r.raise_for_status() return r.json()["data"] # Search by gene symbol query = """ query SearchTarget($sym: String!) { search(queryString: $sym, entityNames: ["target"]) { hits { id name entity object { ... on Target { approvedSymbol approvedName biotype functionDescriptions } } } } } """ data = ot_query(query, {"sym": "BRCA1"}) for hit in data["search"]["hits"][:3]: obj = hit.get("object", {}) print(f"ID: {hit['id']} | {obj.get('approvedSymbol')} | {obj.get('biotype')}") descs = obj.get("functionDescriptions", []) if descs: print(f" Function: {descs[0][:120]}")
python# Direct lookup by Ensembl ID query2 = """ query Target($ensgId: String!) { target(ensemblId: $ensgId) { id approvedSymbol approvedName biotype tractability { label modality value } } } """ data2 = ot_query(query2, {"ensgId": "ENSG00000141510"}) # TP53 t = data2["target"] print(f"\n{t['approvedSymbol']} ({t['id']}): {t['biotype']}") print("Tractability:") for tr in t.get("tractability", [])[:5]: print(f" {tr['modality']} | {tr['label']}: {tr['value']}")
Retrieve association scores for a target across all associated diseases.
pythonimport requests, pandas as pd OT_URL = "https://api.platform.opentargets.org/api/v4/graphql" def ot_query(gql, variables=None): r = requests.post(OT_URL, json={"query": gql, "variables": variables or {}}) r.raise_for_status() return r.json()["data"] query = """ query Associations($ensgId: String!, $size: Int!) { target(ensemblId: $ensgId) { approvedSymbol associatedDiseases(page: {index: 0, size: $size}, orderByScore: "score") { count rows { disease { id name therapeuticAreas { name } } score datatypeScores { id score } } } } } """ data = ot_query(query, {"ensgId": "ENSG00000012048", "size": 20}) target = data["target"] assoc = target["associatedDiseases"] print(f"{target['approvedSymbol']}: {assoc['count']} associated diseases") rows = [] for r in assoc["rows"]: scores = {d["id"]: d["score"] for d in r.get("datatypeScores", [])} rows.append({ "disease": r["disease"]["name"], "disease_id": r["disease"]["id"], "overall_score": round(r["score"], 4), "genetics": round(scores.get("genetic_association", 0), 3), "drugs": round(scores.get("known_drug", 0), 3), "literature": round(scores.get("literature", 0), 3), }) df = pd.DataFrame(rows) print(df.head(10).to_string(index=False))
Given a disease, retrieve all associated targets ranked by score.
pythonimport requests, pandas as pd OT_URL = "https://api.platform.opentargets.org/api/v4/graphql" def ot_query(gql, variables=None): r = requests.post(OT_URL, json={"query": gql, "variables": variables or {}}) r.raise_for_status() return r.json()["data"] query = """ query DiseaseTargets($efoId: String!, $size: Int!) { disease(efoId: $efoId) { id name associatedTargets(page: {index: 0, size: $size}, orderByScore: "score") { count rows { target { id approvedSymbol biotype } score datatypeScores { id score } } } } } """ # EFO_0000305 = breast carcinoma data = ot_query(query, {"efoId": "EFO_0000305", "size": 10}) disease = data["disease"] print(f"Disease: {disease['name']}") print(f"Total associated targets: {disease['associatedTargets']['count']}") for row in disease["associatedTargets"]["rows"][:5]: t = row["target"] print(f" {t['approvedSymbol']:12s} score={row['score']:.3f} biotype={t['biotype']}")
Retrieve approved and investigational drugs, their mechanism, and clinical phase.
pythonimport requests, pandas as pd OT_URL = "https://api.platform.opentargets.org/api/v4/graphql" def ot_query(gql, variables=None): r = requests.post(OT_URL, json={"query": gql, "variables": variables or {}}) r.raise_for_status() return r.json()["data"] query = """ query KnownDrugs($ensgId: String!) { target(ensemblId: $ensgId) { approvedSymbol drugAndClinicalCandidates { count rows { maxClinicalStage drug { id name drugType maximumClinicalStage mechanismsOfAction { rows { mechanismOfAction } } } diseases { disease { id name } } } } } } """ data = ot_query(query, {"ensgId": "ENSG00000146648"}) # EGFR target = data["target"] drugs_data = target["drugAndClinicalCandidates"] print(f"{target['approvedSymbol']}: {drugs_data['count']} drug-indication pairs") rows = [] for r in drugs_data["rows"]: drug = r["drug"] moa = drug.get("mechanismsOfAction") or {} moa_first = (moa.get("rows") or [{}])[0].get("mechanismOfAction") first_disease = (r.get("diseases") or [{}])[0].get("disease") or {} rows.append({ "drug": drug["name"], "type": drug["drugType"], "maxClinicalStage": r["maxClinicalStage"], "approved": drug["maximumClinicalStage"] == "PHASE_4", "indication": first_disease.get("name", "n/a"), "mechanism": moa_first, }) df = pd.DataFrame(rows).drop_duplicates(subset=["drug", "indication"]) print(df.head(10).to_string(index=False))
Retrieve detailed evidence records (GWAS, ClinVar, literature) for a target-disease pair.
pythonimport requests OT_URL = "https://api.platform.opentargets.org/api/v4/graphql" def ot_query(gql, variables=None): r = requests.post(OT_URL, json={"query": gql, "variables": variables or {}}) r.raise_for_status() return r.json()["data"] query = """ query Evidence($ensgId: String!, $efoId: String!) { disease(efoId: $efoId) { evidences( ensemblIds: [$ensgId] enableIndirect: true size: 10 datasourceIds: ["gwas_catalog", "clinvar", "chembl"] ) { count rows { datasourceId score variantRsId studyId publicationYear clinicalSignificances } } } } """ data = ot_query(query, {"ensgId": "ENSG00000012048", "efoId": "EFO_0000305"}) evidences = data["disease"]["evidences"] print(f"Evidence records: {evidences['count']}") for ev in evidences["rows"][:5]: print(f" Source: {ev['datasourceId']:20s} | Score: {ev['score']:.3f}")
Retrieve known adverse events and safety data for a target.
pythonimport requests OT_URL = "https://api.platform.opentargets.org/api/v4/graphql" def ot_query(gql, variables=None): r = requests.post(OT_URL, json={"query": gql, "variables": variables or {}}) r.raise_for_status() return r.json()["data"] query = """ query Safety($ensgId: String!) { target(ensemblId: $ensgId) { approvedSymbol safetyLiabilities { event effects { direction dosing } biosamples { tissueLabel cellLabel } datasource } } } """ data = ot_query(query, {"ensgId": "ENSG00000146648"}) # EGFR target = data["target"] print(f"Safety liabilities for {target['approvedSymbol']}:") for s in target.get("safetyLiabilities", [])[:5]: print(f" Event: {s['event']}") # 2025 schema: `datasource` is a scalar literature-citation string, # not the legacy `datasources[{name, pmid}]` list of objects print(f" Source: {s.get('datasource', 'n/a')}") for eff in s.get("effects", []) or []: print(f" Effect: direction={eff.get('direction')} dosing={eff.get('dosing')}")
Open Targets uses harmonic sum aggregation to combine evidence from multiple data sources into a 0–1 association score. Subscores include: genetic_association, somatic_mutation, known_drug, affected_pathway, literature, RNA_expression, animal_model, and others. Higher scores indicate more and stronger evidence.
Open Targets uses Experimental Factor Ontology (EFO) identifiers for diseases (e.g., EFO_0000305 for breast carcinoma). Search by disease name using the search query to find EFO IDs before querying associations.
Goal: Given a disease, rank all associated targets by overall score and export with evidence breakdown.
pythonimport requests, pandas as pd, time OT_URL = "https://api.platform.opentargets.org/api/v4/graphql" def ot_query(gql, variables=None): r = requests.post(OT_URL, json={"query": gql, "variables": variables or {}}) r.raise_for_status() return r.json()["data"] def disease_search(name): q = 'query S($q:String!){search(queryString:$q,entityNames:["disease"]){hits{id name}}}' data = ot_query(q, {"q": name}) return [(h["id"], h["name"]) for h in data["search"]["hits"][:3]] def get_top_targets(efo_id, n=50): q = """ query($efoId:String!,$size:Int!){ disease(efoId:$efoId){ name associatedTargets(page:{index:0,size:$size},orderByScore:"score"){ count rows{ target{id approvedSymbol biotype} score datatypeScores { id score } } } } }""" data = ot_query(q, {"efoId": efo_id, "size": n}) disease = data["disease"] rows = [] for row in disease["associatedTargets"]["rows"]: t = row["target"] scores = {d["id"]: round(d["score"], 3) for d in row.get("datatypeScores", [])} rows.append({ "target": t["approvedSymbol"], "ensembl_id": t["id"], "biotype": t["biotype"], "overall_score": round(row["score"], 4), **scores }) return disease["name"], pd.DataFrame(rows) # Step 1: Find EFO ID for disease candidates = disease_search("non-small cell lung carcinoma") print("Disease candidates:", candidates) # Step 2: Get top targets disease_name, df = get_top_targets("EFO_0003060", n=50) df.to_csv("target_prioritization.csv", index=False) print(f"\nTop targets for {disease_name}:") cols = [c for c in ["target", "overall_score", "genetic_association", "known_drug", "literature", "rna_expression", "somatic_mutation"] if c in df.columns] print(df[cols].head(10).to_string(index=False))
Goal: For a target, retrieve all drugs and their associated indications and phases.
pythonimport requests, pandas as pd OT_URL = "https://api.platform.opentargets.org/api/v4/graphql" def ot_query(gql, variables=None): r = requests.post(OT_URL, json={"query": gql, "variables": variables or {}}) r.raise_for_status() return r.json()["data"] query = """ query($ensgId:String!){ target(ensemblId:$ensgId){ approvedSymbol drugAndClinicalCandidates{ count rows{ maxClinicalStage drug{id name drugType maximumClinicalStage mechanismsOfAction { rows { mechanismOfAction } } } diseases { disease { id name } } } } } }""" targets = { "EGFR": "ENSG00000146648", "ERBB2": "ENSG00000141736", } all_rows = [] for sym, ensg in targets.items(): data = ot_query(query, {"ensgId": ensg}) for row in data["target"]["drugAndClinicalCandidates"]["rows"]: drug = row["drug"] moa = drug.get("mechanismsOfAction") or {} moa_first = (moa.get("rows") or [{}])[0].get("mechanismOfAction") first_disease = (row.get("diseases") or [{}])[0].get("disease") or {} all_rows.append({ "target": sym, "drug": drug["name"], "drug_type": drug["drugType"], "maxClinicalStage": row["maxClinicalStage"], "approved": drug["maximumClinicalStage"] == "PHASE_4", "indication": first_disease.get("name", "n/a"), "mechanism": moa_first, }) df = pd.DataFrame(all_rows) df.to_csv("drug_target_matrix.csv", index=False) print(df.head(10).to_string(index=False))
| Parameter | Module | Default | Range / Options | Effect | |-----------|--------|---------|-----------------|--------| | page.size | Associations | 10 | 1–10000 | Records per page | | page.index | Associations | 0 | 0–N | Page index for pagination | | orderByScore | Associations | "score" | "score", component IDs | Sort associations by score | | datasourceIds | Evidence | all sources | list of datasource IDs | Filter evidence by source | | enableIndirect | Evidence | false | true/false | Include child disease evidence | | entityNames | Search | all | ["target"], ["disease"] | Filter search entity type |
search query to get the canonical EFO ID before running association queries to avoid name-matching issues.page.size: 10000 for complete results, but be aware this can return large payloads.datatypeScores: For genetic target validation, filter on genetic_association subscore > 0.1; for drug repurposing, prioritize known_drug subscore.enableIndirect: true in evidence queries to include evidence for disease subtypes (child terms in EFO hierarchy).When to use: Resolve a disease name to the EFO ID needed for association queries.
pythonimport requests OT_URL = "https://api.platform.opentargets.org/api/v4/graphql" query = """ query($q: String!) { search(queryString: $q, entityNames: ["disease"]) { hits { id name score } } }""" r = requests.post(OT_URL, json={"query": query, "variables": {"q": "breast cancer"}}) for hit in r.json()["data"]["search"]["hits"][:5]: print(f"{hit['id']}: {hit['name']} (score={hit['score']:.3f})")
When to use: Assess whether a target is tractable for small molecules, antibodies, or PROTACs.
pythonimport requests OT_URL = "https://api.platform.opentargets.org/api/v4/graphql" query = """ query($ensgId: String!) { target(ensemblId: $ensgId) { approvedSymbol tractability { label modality value } } }""" r = requests.post(OT_URL, json={"query": query, "variables": {"ensgId": "ENSG00000141510"}}) t = r.json()["data"]["target"] print(f"Tractability for {t['approvedSymbol']}:") for tr in t.get("tractability", []): if tr["value"]: print(f" [{tr['modality']}] {tr['label']}")
When to use: Find all approved drugs for a disease with phase 4 evidence.
pythonimport requests, pandas as pd OT_URL = "https://api.platform.opentargets.org/api/v4/graphql" query = """ query($efoId: String!) { disease(efoId: $efoId) { name drugAndClinicalCandidates { count rows { maxClinicalStage drug { name maximumClinicalStage drugType } }} } }""" r = requests.post(OT_URL, json={"query": query, "variables": {"efoId": "EFO_0000305"}}) data = r.json()["data"]["disease"] approved = [row for row in data["drugAndClinicalCandidates"]["rows"] if row["drug"]["maximumClinicalStage"] == "PHASE_4"] print(f"Approved drugs for {data['name']}: {len(approved)}") for row in approved[:5]: print(f" {row['drug']['name']} ({row['drug']['drugType']}) maxStage={row['maxClinicalStage']}")
| Problem | Cause | Solution | |---------|-------|----------| | HTTP 400 with GraphQL error | Malformed query or invalid field name | Check query against GraphQL schema at https://api.platform.opentargets.org/api/v4/graphql | | Empty rows in associations | EFO ID not recognized | Use search query to find correct EFO ID | | Target not found | Gene symbol vs Ensembl ID mismatch | Use search query first to resolve Ensembl ID | | Slow query for large result set | page.size too large | Cap at 500 rows; paginate with multiple requests | | Missing tractability data | Target not assessed | Not all targets have tractability; check tractability field is non-null | | drugAndClinicalCandidates empty | No drug-target evidence in ChEMBL | Use chembl-database-bioactivity for preclinical compound activity |
chembl-database-bioactivity — Bioactivity IC50/Ki data for compounds against targetsclinicaltrials-database-search — Detailed clinical trial information for drugs found via Open Targetsensembl-database — Ensembl IDs and variant annotations needed as input to Open Targets queriesstring-database-ppi — Protein-protein interaction networks to contextualize target biology| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-11 | pass→pass | 9,751 | 4,685 | -52% | 1 | 1 | 0% | 2,072 | 6,917 | +234% | 0 | 0 | — |
case-17 | pass→pass | 10,917 | 6,231 | -43% | 1 | 1 | 0% | 2,540 | 7,380 | +191% | 0 | 0 | — |
case-01 | fail→fail | 14,939 | 14,002 | -6% | 1 | 1 | 0% | 3,280 | 6,776 | +107% | 0 | 0 | — |
case-02 | fail→pass | 19,926 | 13,651 | -31% | 1 | 1 | 0% | 3,890 | 8,795 | +126% | 0 | 0 | — |
case-03 | fail→pass | 12,250 | 12,205 | -0% | 1 | 1 | 0% | 2,635 | 8,467 | +221% | 0 | 0 | — |
case-04 | fail→fail | 13,844 | 17,859 | +29% | 1 | 1 | 0% | 2,785 | 9,493 | +241% | 0 | 0 | — |
case-05 | fail→fail | 11,906 | 23,258 | +95% | 1 | 1 | 0% | 2,432 | 10,723 | +341% | 0 | 0 | — |
case-06 | pass→pass | 9,487 | 5,480 | -42% | 1 | 1 | 0% | 1,932 | 7,138 | +269% | 0 | 0 | — |
case-07 | pass→pass | 7,810 | 5,781 | -26% | 1 | 1 | 0% | 1,771 | 7,159 | +304% | 0 | 0 | — |
case-08 | fail→pass | 27,224 | 9,061 | -67% | 1 | 1 | 0% | 1,960 | 8,098 | +313% | 0 | 0 | — |
case-09 | fail→pass | 10,094 | 6,474 | -36% | 1 | 1 | 0% | 2,227 | 7,408 | +233% | 0 | 0 | — |
case-10 | fail→pass | 15,856 | 9,574 | -40% | 1 | 1 | 0% | 3,541 | 8,315 | +135% | 0 | 0 | — |
case-12 | pass→pass | 12,585 | 7,579 | -40% | 1 | 1 | 0% | 2,991 | 7,729 | +158% | 0 | 0 | — |
case-13 | fail→pass | 16,813 | 10,660 | -37% | 1 | 1 | 0% | 3,813 | 8,494 | +123% | 0 | 0 | — |
case-14 | fail→pass | 13,641 | 12,377 | -9% | 1 | 1 | 0% | 2,733 | 8,814 | +223% | 0 | 0 | — |
case-15 | fail→pass | 16,101 | 6,465 | -60% | 1 | 1 | 0% | 3,024 | 7,404 | +145% | 0 | 0 | — |
case-16 | pass→pass | 9,010 | 4,675 | -48% | 1 | 1 | 0% | 1,915 | 6,994 | +265% | 0 | 0 | — |
case-18 | pass→pass | 22,799 | 3,738 | -84% | 1 | 1 | 0% | 2,461 | 6,724 | +173% | 0 | 0 | — |
case-19 | pass→pass | 6,392 | 9,779 | +53% | 1 | 1 | 0% | 1,275 | 7,081 | +455% | 0 | 0 | — |
case-20 | pass→pass | 13,486 | 10,427 | -23% | 1 | 1 | 0% | 2,411 | 7,973 | +231% | 0 | 0 | — |
case-21 | pass→pass | 4,010 | 1,796 | -55% | 1 | 1 | 0% | 839 | 6,298 | +651% | 0 | 0 | — |
case-22 | fail→pass | 7,291 | 2,537 | -65% | 1 | 1 | 0% | 1,309 | 6,425 | +391% | 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, and 21 counted toward the lift figure. The other 1 produced results that are not comparable between the two arms, so they are excluded from the headline rather than averaged into it. The headline lift of +41 percentage points is the difference between those two pass rates over the 21 comparable cases. 1 case got worse with the skill loaded, and it is included in that figure.
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.