Aller au contenu

omnilab/chembl

Omnilab ChEMBL: a bioactive molecule by ChEMBL id, InChIKey or name - its type, maximum development phase, computed properties (molecular weight, ALogP, polar surface area, hydrogen bond donors and acceptors, Lipinski's rule of five, QED), SMILES, InChI, structure depiction, mechanisms of action and their targets, and its identifiers in ChEBI, PubChem, UNII and DSSTox through UniChem - and a search by text or InChIKey, through the ChEMBL data web services (EMBL-EBI), no key needed.

Étage
Fournisseur · Omnilab
Version
1.x
Suite de conformité
non branchée
Vérifié en réel
Son README ne le dit pas
Licence
MIT
composer require omnilab/chembl:1.x-dev

ChEMBL (EMBL-EBI), through its data web services (https://www.ebi.ac.uk/chembl/api/data/) and UniChem, for omnisense/omnilab: bioactive molecules by ChEMBL id, InChIKey or name - development phase, computed properties, mechanisms of action and their targets - with their ChEBI, PubChem, UNII and DSSTox ids from UniChem as cross-references, as entities; a search. No key.

Installation

composer require omnilab/chembl
use Omnilab\Chembl\ChemblSourceFactory;

$chembl = (new ChemblSourceFactory($httpClient))->create([
    'mailto' => 'support@glitchr.io',  // optional
    'synonyms' => 20,                  // optional
    'unichem' => true,                 // optional: the cross-references from UniChem
    'mechanisms' => true,              // optional: the mechanisms of action and their targets
]);

With the Symfony bundle, the factory is registered as chembl when the bundle knows it; otherwise register ChemblSourceFactory as a service (it is autoconfigured as a SourceFactoryInterface).

Credentials

None: ChEMBL's data web services and UniChem take no key. Give mailto, the address the EBI writes to before it blocks a caller.

Options

The factory's options, with the defaults it applies (ChemblSourceFactory):

Option Type Default Required Effect
synonyms int 20 no how many synonyms to keep; 0: none
unichem bool true no entity() asks UniChem for the molecule's ChEBI, PubChem, UNII and DSSTox ids (one POST)
mechanisms bool true no entity() asks the mechanisms of action and their targets (one or two calls)
unichem_uri string https://www.ebi.ac.uk/unichem/api/v1/compounds no UniChem's compounds API, for the cross-references
mailto ?string null no a contact address, in the User-Agent: whom the EBI writes to before it blocks a caller
user_agent ?string null no the whole User-Agent, in place of the one made from mailto
throttle float 0 no seconds left between two calls of the data web services (UniChem's POST is not spaced): the EBI publishes no figure
base_uri string https://www.ebi.ac.uk/chembl/api/data/ no ChEMBL's data web services

Capabilities

capabilities() answers entity, search, structure, cross_references; an operation it does not answer throws NotSupportedException before any call.

Capability Supported
entity yes a ChEMBL id (CHEMBL113, chembl:, its page), a standard InChIKey, or a name (preferred, then synonym); another scheme refused before any call; up to four calls
search yes a full InChIKey or its first block (matched on the structures), else ChEMBL's full-text search, the best first, 100 a page at most, paged by offset, with a total; a domain other than chemistry answers an empty page
series no
products no
occurrences no
datasets no
jobs no
open no
structure yes the image is ChEMBL's SVG of the structure (none for an antibody)
cross_references yes the InChIKey and InChI, and from UniChem the ChEBI, PubChem, UNII and DSSTox ids; the mechanisms' targets are related
forecast no
historical no
area no
station no
cloud_cover no

Which API

The ChEMBL data web services, https://www.ebi.ac.uk/chembl/api/data/ (ChEMBL 37, released 2026-05-01, as status.json says), in JSON. A molecule's record holds its names, type, phase, computed properties and structures, but no identifier of another base beyond the InChI and its key; those ChEMBL keeps in UniChem, its sister service at the EBI (https://www.ebi.ac.uk/unichem/api/v1/compounds), which answers only POST: the source posts the ChEMBL id there. UniChem's legacy GET interface (/unichem/rest/) answers 404.

Calls

Method Reads ChEMBL
entity() a ChEMBL id (CHEMBL113, chembl:CHEMBL113, chembl.compound:CHEMBL113, its page) molecule/{id}.json
a standard InChIKey molecule/{inchikey}.json: ChEMBL reads a key where it reads an id
a name molecule.json?pref_name__iexact=; when none, molecule.json?molecule_synonyms__molecule_synonym__iexact=
then, for each UniChem POST compounds {"type": "sourceID", "sourceID": 1, "compound": id}; mechanism.json?molecule_chembl_id=; target/set/{ids}.json with their components
search() a full InChIKey molecule.json?molecule_structures__standard_inchi_key=
an InChIKey's first block (14 letters: the skeleton) molecule.json?molecule_structures__standard_inchi_key__startswith=
any other text; a domain other than chemistry answers nothing molecule/search.json?q= (names, synonyms, ids; the best first)

A molecule costs one call, two by a synonym, then one to UniChem and one or two for the mechanisms (the targets' names in one call for all) - four for caffeine; unichem: false and mechanisms: false leave those out. A search pages by offset (Page::$next is the next offset) and reads the molecules from its records: no UniChem nor mechanisms there, entity() adds them.

An identifier of a scheme ChEMBL does not read (a PubChem CID, ChEBI, CAS, Wikidata, UniProt...) is refused before any call; unknown is null (ChEMBL answers 404 with an empty body). A name that several molecules have as a synonym, none as its preferred name, is null rather than a guess.

What an entity holds

id (its ChEMBL id), label (ChEMBL's preferred name in the case of the synonym that spells it - Caffeine, not ChEMBL's CAFFEINE; its id when it has no name), domain chemistry, kind (ChEMBL's molecule type, lower-cased: small molecule, antibody, protein, oligonucleotide...), url (https://www.ebi.ac.uk/chembl/explore/compound/CHEMBL113), image (the SVG of its structure, image/{id}.svg: Capability::STRUCTURE; null without a structure, as for an antibody), synonyms (ChEMBL's synonyms but its trade names, research codes and registry numbers, 20 by default), no description (ChEMBL writes none for a small molecule), and these properties:

Key ChEMBL Value
molecular_formula full_molformula text
molecular_weight full_mwt (the salt's, for a salt) Quantity, g/mol
molecular_weight_parent mw_freebase Quantity, g/mol
alogp alogp (Crippen) number
tpsa psa Quantity, Ų
h_bond_donors, h_bond_acceptors hbd, hba number
rotatable_bonds, heavy_atoms, aromatic_rings rtb, heavy_atoms, aromatic_rings number
ro5_violations num_ro5_violations number
rule_of_five at most one violation of Lipinski's rule of five boolean
rule_of_three ro3_pass (fragment-like) boolean
qed, np_likeness qed_weighted, np_likeness_score number
smiles, inchi, inchikey the molecule's structures (canonical SMILES, standard InChI and key) text
max_phase max_phase: 4 approved, 3, 2, 1, 0.5 early phase 1, -1 unknown; absent before the clinic number
first_approval first_approval year
atc_code atc_classifications, one property each text
natural_product, withdrawn natural_product, withdrawn_flag boolean
parent_chembl_id the parent of a salt (caffeine citrate's is CHEMBL113) text
mechanism_of_action, action_type, target, target_chembl_id each curated mechanism: what it does, the action (lower-cased), the target's name with its type and organism, its id text

Keys shared with PubChem (molecular_weight, tpsa, h_bond_donors, h_bond_acceptors, rotatable_bonds, heavy_atoms, smiles, inchi, inchikey) name the same quantity, computed by each base in its own way: ChEMBL's polar surface for caffeine is 61.82 Ų, PubChem's 58.4; ChEMBL counts 6 acceptors, PubChem 3. After a merge both are kept, each with its source. ChEMBL's alogp is not PubChem's xlogp: two methods, two keys.

identifiers: the InChIKey and the InChI, then UniChem's ChEBI id, PubChem CID, UNII and DSSTox id when it has them (caffeine: CHEBI:27732, CID 2519, 3G6A5W338E, DTXSID0020232). UniChem lists more bases (DrugBank, HMDB, the PDB ligand, Guide to Pharmacology, clinical trials...): the core has no scheme for them.

related: what the molecule acts on, not what it is - target, the ChEMBL ids of its mechanisms' targets (caffeine: CHEMBL2111329, the adenosine receptor family), and target_protein, the UniProt accessions of their proteins (P30542, P29275, P0DMS8, P29274: the receptors A1, A2b, A3 and A2a). The Collector does not follow them; an application can ask a UniProt source for them.

The Collector: caffeine from PubChem

PubChem gives caffeine's InChIKey before its ChEMBL id; ChEMBL reads both, so the Collector asks it by the key. Against the real services on 2026-10-15:

$collector->collect('pubchem', 'caffeine', 1, ['chembl']);

8 calls in 3.4 s - PubChem's four, then molecule/RYYVLZVUVIJVGH-UHFFFAOYSA-N.json, UniChem, mechanism.json, target/set/CHEMBL2111329.json - and sources = pubchem, chembl:

molecular_weight      194.19 g/mol [pubchem]  194.19 g/mol [chembl]
tpsa                  58.4 Ų [pubchem]  61.82 Ų [chembl]
h_bond_acceptors      3 [pubchem]  6 [chembl]
xlogp                 -0.1 [pubchem]
alogp                 -1.03 [chembl]
max_phase             4 [chembl]
rule_of_five          yes [chembl]
mechanism_of_action   Adenosine receptor antagonist [chembl]
target                Adenosine receptor (protein family, Homo sapiens) [chembl]

From ChEMBL the other way, UniChem's CID 2519 and ChEBI 27732 lead to PubChem and ChEBI.

What ChEMBL does not give here

The bioactivities (activity: 24.5 million measurements), the assays, the documents, the drug indications, the warnings beyond the withdrawn flag, the routes (oral, parenteral, topical), the biotherapeutic's sequence and description, the molfile, the similarity and substructure searches and the targets' own records are not read. Nor are the cross-references UniChem gives in bases the core has no scheme for.

Example

<?php

require __DIR__.'/vendor/autoload.php';

use Omnilab\Chembl\ChemblSourceFactory;
use Omnilab\Model\Scheme;
use Omnilab\Source\Query;
use Symfony\Component\HttpClient\HttpClient;

$chembl = (new ChemblSourceFactory(HttpClient::create()))->create(['mailto' => 'you@example.org', 'synonyms' => 3]);

$aspirin = $chembl->entity('aspirin');
echo $aspirin->label, ' (', $aspirin->id, '): phase ', $aspirin->value('max_phase'), ', ', $aspirin->property('molecular_weight'), ', mechanism: ', $aspirin->value('mechanism_of_action'), "\n";
echo '  ChEBI ', $aspirin->identifier(Scheme::CHEBI), ', PubChem ', $aspirin->identifier(Scheme::PUBCHEM_CID), ', UNII ', $aspirin->identifier(Scheme::UNII), '; targets ', implode(', ', $aspirin->related('target')->values(Scheme::CHEMBL)), "\n";

$page = $chembl->search(new Query('caffeine', limit: 3));
printf("%d molecules for \"caffeine\"; the first %d: %s\n", $page->total, count($page), implode(', ', array_map(static fn ($m) => $m->id->value.' '.$m->label, $page->items)));
Aspirin (chembl:CHEMBL25): phase 4, 180.16 g/mol, mechanism: Cyclooxygenase inhibitor
  ChEBI 15365, PubChem 2244, UNII R16CO5Y76E; targets CHEMBL2094253
110 molecules for "caffeine"; the first 3: CHEMBL113 Caffeine, CHEMBL1200569 Caffeine citrate, CHEMBL1927665 CHEMBL1927665

(as answered on 2026-10-27: five calls - the name, UniChem, the mechanisms and their target, the search)

# config/packages/omnilab.yaml
omnilab:
    sources:
        chembl: { factory: chembl, options: { mailto: '%env(OMNILAB_MAILTO)%', synonyms: 20, unichem: true, mechanisms: true } }

Errors

Every call goes through HttpSource: a 404 or a 410 (and a 204) is null - an empty page for a list -; a 429 is a RateLimitedException (retryAfter from Retry-After); a 5xx, a time-out or a host that does not answer an UnavailableException; any other 4xx a ProviderException with its status and what the service said; an answer that is not JSON a ProviderException, a web page answered for JSON an UnavailableException.

ChEMBL's own: an unknown id is its 404 with an empty body, null; a name several molecules have as a synonym, none as its preferred name, is null. UniChem's POST is made by the source and mapped alike (a 429 a RateLimitedException, a 5xx an UnavailableException, another 4xx a ProviderException, "UniChem answered ..."); UniChem's "Not found" (an antibody) leaves the cross-references out. Before any call: an identifier of a scheme ChEMBL does not read (a PubChem CID, ChEBI, CAS, Wikidata, UniProt...) (NotSupportedException).

Verified, and not

Against the real services on 2026-10-15, without a key (ChEMBL 37): caffeine by its ChEMBL id, its InChIKey, its name and its synonym "guaranine"; its properties, phase, ATC codes, mechanism and target; its ChEBI, PubChem, UNII and DSSTox ids from UniChem; aspirin by name (CHEMBL25, cyclooxygenase); trastuzumab (CHEMBL1201585, an antibody: no structure, no image, UniChem "Not found", HER2 as target); an unknown id (404, empty body) and an unknown name; a search for "caffeine" (110 molecules, offsets 0 and 3), by caffeine's InChIKey and by its first block (and aspirin's); the SVG of the structure (200, image/svg+xml; 404 for the antibody); the Collector's walk above. The fixtures in Tests/Fixtures/ are its answers, UniChem's 177 clinical trials for caffeine cut to 2.

Again on 2026-10-27: aspirin by its name (phase 4, its UniChem ids, its target), the search for "caffeine" (110 molecules; the example above).

Not verified: ChEMBL's or UniChem's answer to a client that calls too fast (no 429 met, no rate published - the handling is the core's); a full-text search of an InChIKey, which ChEMBL answers with 10,000 unrelated molecules, which is why a key is matched on the structures instead. The UniChem call is a POST the core's HttpSource does not make: the source maps its errors the same way, but the throttle option does not space it.

Responsibility

This package is an independent, unofficial client: it is not affiliated with, endorsed by or sponsored by ChEMBL or the EMBL-EBI. ChEMBL's data are under CC BY-SA 3.0 - cite ChEMBL, share what is derived from it alike - and the EMBL-EBI's terms of use reserve the right to block a client that loads the services unreasonably, as the README's Terms of use says. Whoever runs the package is responsible for complying with them and for any use made of what it returns: see the README's Responsibility section.

Proposer une modification docs/index.md