Connector chembl — ChEMBL (EMBL-EBI)
| Source | https://www.ebi.ac.uk/chembl/ — European Molecular Biology Laboratory, European Bioinformatics Institute |
| Tier / category | 1 / drugs |
| Access | REST, https://www.ebi.ac.uk/chembl/api/data/<resource>.json, no authentication |
| Docs verified | 2026-09-08 — https://chembl.gitbook.io/chembl-interface-documentation/web-services/chembl-data-web-services + live probes (status, molecule/search, mechanism, target, drug_indication) |
| License | CC BY-SA 3.0 — licenseStatus: approved, redistribution: attribution (share-alike noted) |
| Code | packages/connectors/src/connectors/chembl/ (manifest.ts, api.ts, index.ts, chembl.test.ts, fixtures/) |
License (quoted)
About page (https://chembl.gitbook.io/chembl-interface-documentation/about, fetched 2026-09-08):
The ChEMBL data is made available on a Creative Commons Attribution-Share Alike 3.0 Unported License.
Attribution: "ChEMBL, EMBL-EBI, CC BY-SA 3.0" on /source/chembl and in the API sources envelope; every run records the release (datasetVersion, e.g. ChEMBL_37, release DOIs exist on the Downloads page). Share-alike: the fields and edges derived from ChEMBL carry ChEMBL provenance so a redistributed extract can be published under a compatible licence. Citation: Zdrazil B et al., The ChEMBL Database in 2023, Nucleic Acids Res 2024 (PMID 37933841).
Verified endpoints (2026-09-08, ChEMBL_37 released 2026-05-01)
| Endpoint | Used for | Notes |
|---|---|---|
status.json |
health, datasetVersion |
{status: "UP", chembl_db_version: "ChEMBL_37", chembl_release_date, disinct_compounds: 2921148, targets: 18552} |
molecule/search.json?q=<name>&limit=20 |
identity | full molecule objects (pref_name, molecule_type, max_phase, first_approval, molecule_synonyms[{molecule_synonym, syn_type}], molecule_hierarchy.parent_chembl_id, atc_classifications, cross_references, …) + page_meta |
mechanism.json?parent_molecule_chembl_id=<id>&limit=200 |
mechanisms | mechanism_of_action, action_type, target_chembl_id, max_phase, mechanism_refs. Attached to the dosed form: molecule_chembl_id=CHEMBL941 (imatinib) returns 0, the parent filter returns the 4 mechanisms recorded on CHEMBL1642 (mesylate) |
target/<id>.json |
targets → genes | target_type (SINGLE PROTEIN, CHIMERIC PROTEIN, PROTEIN FAMILY, PROTEIN COMPLEX…), organism, target_components[].accession (UniProt) and target_component_synonyms with syn_type GENE_SYMBOL |
drug_indication.json?parent_molecule_chembl_id=<id>&limit=500&offset= |
indications | mesh_id, mesh_heading, efo_id/efo_term (EFO or MONDO), max_phase_for_ind (string "3.0"; mechanism max_phase is a number — both coerced) |
No published rate limit ("reasonable use") → manifest 3 req/s, 2 in flight; targets are cached for the run. During development the API returned HTTP 500 for several minutes (EBI outage) — the SDK retries 5xx with back-off and the run fails cleanly if the outage persists; the cursor lets the next run resume.
Scope — enrichment, not import
Only existing drugs rows (CIViC therapies today, trial interventions later) are looked up; the 2.9 M ChEMBL compounds are never crawled. Names that cannot be molecules — regimens (… Regimen), combinations (A/B), procedures (radiation, surgery, transplantation) — are skipped and counted (isNonMoleculeName).
Sync design
Drugs are processed in (name, id) order; ctx.cursor = {pass, after: {name, id}, processed, completedAt} is flushed every 25 drugs so a run stopped by the time budget (--max-minutes) or a crash resumes after the last processed drug. A completed pass starts a fresh pass on the next run (full re-check against the current release; every write is idempotent).
Per drug:
- Identity —
molecule/search.json?q=<name>thenpickExactMatch(): the hit whosepref_name, or one of itsmolecule_synonyms, equals our name afternormalizeLabel(case/punctuation-insensitive). Ties:pref_namebeats synonym → highermax_phase→ parent molecule (molecule_hierarchy.parent_chembl_id= itself) → lowest ChEMBL number. Examples: "Osimertinib" → CHEMBL3353410 (free base, not the mesylate); "Imatinib" → CHEMBL941, "Imatinib Mesylate" → CHEMBL1642 (both exist as CIViC therapies); "Trastuzumab" → CHEMBL1201585 (not the ADCs). No exact hit →unresolved_labels(entity_kind drug, context = total hits + top 5{chemblId, prefName, maxPhase, type}) — never a fuzzy assignment. - Mechanisms for the parent molecule, deduplicated by (mechanism, target, action type) — ChEMBL repeats a mechanism per salt/dosed form.
- Targets: each
target_chembl_id→target/<id>.json(cached) → human protein components →GENE_SYMBOLsynonyms →GeneCache.ensure(symbol)(shared with CIViC/ClinVar/GDC; HGNC-style symbol check). Chimeric proteins (Bcr/Abl → ABL1 + BCR) and protein families (ABL → ABL1 + ABL2) expand to every component gene. Non-human targets yield nothing. - Indications for the parent molecule (paged), deduplicated by MeSH UI with the highest
max_phase_for_ind; EFO/MONDO ids accumulated. - Raw record
ctx.upsertSourceRecord('molecule', <ChEMBL id>, {molecule, parentChemblId, matchedBy, firstApproval, atcClassifications, mechanisms, targets, indications})→ raw lakedata/raw/chembl/<date>/molecule/.first_approvaland ATC codes live only here: a first-approval year is not a jurisdictional approval record (drug_approvalsis fed by regulatory connectors, CLAUDE.md §13). - Provenance: one row per (drug, molecule) —
sourceRecordId= ChEMBL id,sourceUrl https://www.ebi.ac.uk/chembl/compound_report_card/<id>/,dataset ChEMBL,datasetVersion ChEMBL_37,evidenceType database,accessLevel open,license CC BY-SA 3.0— reused while the source record is unchanged (looked up from the drug's existing edges). drugsrow:chembl_id;kindfrommolecule_type(Small molecule →small_molecule, Antibody →monoclonal_antibody, Antibody drug conjugate →adc, Cell →cell_therapy, Gene →gene_therapy, Protein / Enzyme / Oligonucleotide / … →other;Unknownkeeps the existing value);mechanism= distinctmechanism_of_actionstrings joined with "; ";development_statusfrommax_phase(4 →approved, 3 →phase_3, 2 →phase_2, 1 →phase_1, 0.5 →early_phase_1, null/−1 untouched) and never downgraded (a regulatoryapprovedsurvives a ChEMBL phase 3);target_gene_ids= union with the new gene ids (sql.raw('ARRAY[…]')). Achange_eventsrow records the ChEMBL id assignment.drug_aliasesfrommolecule_synonyms:TRADE_NAME → brand,INN/USAN/BAN/JAN/USP → generic,RESEARCH_CODE → development_code, anything else →synonym; the drug's own name is skipped, duplicates collapse per (normalized, type).knowledge_edges- drug TARGETS gene — one edge per gene,
evidenceLevel= ChEMBLaction_type(s) joined with|(INHIBITOR, ANTAGONIST, BINDING AGENT…),directionnull,cancerContextIds [],evidenceCategory curated_evidence,sourceRecordId= ChEMBL id,provenanceIds [provenance]. - drug INVESTIGATED_FOR cancer — for each indication whose
mesh_idresolves throughresolver.byCode('mesh', ui)(i.e.cancer_codes system 'mesh'written by themeshconnector; runmeshfirst):evidenceLevel=max_phase_for_indas text ("4", "3", …, never re-scaled),direction 'unknown',cancerContextIds [cancer],evidenceCategory curated_evidence. Unresolved MeSH ids go tounresolved_labels(entity_kind cancer, context{meshId, efoIds, chemblId, drugId, drugName, maxPhaseForIndication}) only when the heading is a neoplasm — known MeSH neoplasm descriptor (mesh source records / codes) orNEOPLASM_HEADING_RE— so rheumatoid arthritis, malaria or pulmonary hypertension indications (imatinib) do not pollute the cancer queue; they are counted asindicationsNonCancer. - Upsert on the unique key (source/target/type/source_id/source_record_id);
supportCountstays 1,lastSeenAtrefreshed.
- drug TARGETS gene — one edge per gene,
evidenceCategory note: the task brief suggested database for indication edges; knowledge_edges.evidence_category is the scientific safety label set (observed_data | published_evidence | curated_evidence | regulatory_status | clinical_guideline | computed_metric, CLAUDE.md §3/§12) and ChEMBL indications are manually curated from labels and trial registries, so they are labelled curated_evidence; the provenance row carries evidenceType database. Change evidenceCategory in index.ts if the integrator prefers a new label.
Tests (chembl.test.ts, fixtures from live responses, trimmed)
Search pages (osimertinib, imatinib, trastuzumab, empty, page with a malformed molecule), mechanisms (osimertinib single, imatinib four mechanisms, duplicate dosed forms), targets (single proteins EGFR/ABL1/KIT/ERBB2/PDGFRB, chimeric Bcr/Abl, ABL family, non-human), indications (dedupe by UI with max phase, string-phase coercion, multi-page merge, malformed rows), vocabulary mappings (kind, phase, synonym type, non-molecule names), manifest licence.
Observed run (cancerindex_c, 2026-09-08, --max-minutes 15, 661 CIViC drugs)
See the table below — filled from the run log of ING-CHEMBL-20260908-*.
OBSERVED_RUN_TABLE
Limitations / notes
- Identity is exact-name only: CIViC names that differ from ChEMBL
pref_name/synonyms (e.g. investigational codes written differently, salts named "X Hydrochloride" vs ChEMBL "X HYDROCHLORIDE" are fine, but "Anti-PD-1 antibody" is not) stay unresolved and appear in the queue with the top hits for a curator. - Mechanisms/indications come from the parent molecule, so a salt row (e.g. "Imatinib Mesylate") receives the same mechanism and indication edges as its parent — both rows keep their own ChEMBL id.
max_phase_for_indreflects the highest trial phase ChEMBL curated for that indication, not an approval; approvals are regulatory records (FDA/EMA connectors).- Targets without a human protein component (organisms, viral proteins, "unchecked" targets) produce no gene edge; non-protein targets are skipped.
- The connector depends on the
meshconnector for indication edges; run order:ncit-evs → oncotree → hgnc → civic → mesh → chembl.