zinc-database について
ZINC22は、CartBlancheから利用できるバーチャルスクリーニング向けの市販化合物データベースです。創薬やドッキングの用途に、ZINC ID検索、SMILESの完全一致検索、サプライヤー情報を提供します。
主な機能
- 01ZINC IDによる化合物レコード取得
- 02完全一致するSMILESからZINC IDを特定
- 03サプライヤーカタログと購入可否の確認
- 04分子特性と構造識別子の取得
- 05RDKitによるローカルな特性フィルタリング
活用シーン
- 01候補化合物の購入可否を確認
- 02SMILES候補群をZINC識別子に対応付け
- 03分子ドッキング用の化合物セットを準備
- 04RDKitで分子特性に基づき候補を絞り込む
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ jaechang-hits/ sciagent-skills --skill zinc-databaseインストール後、Claude Code で/zinc-databaseを入力して読み込まれたか確認します。
詳細
ZINC 化学ライブラリデータベース(ZINC22 / CartBlanche)
概要
ZINC(ZINC Is Not Commercial)は、バーチャルスクリーニング用にキュレーションされた、市販化合物の無料データベースです。現行世代の ZINC22 には、受注生産および在庫の分子が数十億件収録されており、cartblanche22.docking.org の CartBlanche ウェブアプリケーションを通じて提供されています。CartBlanche には、ZINC ID による物質の検索、SMILES の ZINC 識別子への解決、サプライヤーやカタログでの購入可能性の確認を行う小規模な JSON API があります。
重要 — 古い ZINC15 REST API は、自動アクセスでは機能しなくなりました。
zinc15.docking.org(およびzinc.docking.org)は現在、サイト全体の CAPTCHA の背後にあります。/substances.json、/tranches/...、/substances/{id}.jsonなどへのすべてのリクエストは、/captcha/ページにリダイレクトされるか、403 Forbiddenを返します。zinc15.docking.orgに対してmwt__gte/availability/similarityクエリパラメーターを使用するスクリプトは、いずれも失敗します。以下に記載する ZINC22 CartBlanche エンドポイントを使用してください。
ZINC22/CartBlanche を利用する際に知っておくべき、使い方に影響する点が 3 つあります。
- サーバー側の物性範囲クエリはありません。 ZINC22/CartBlanche には、
mwt__gte/logp__lte/hbd__lteによる検索機能がありません。分子物性で絞り込むには、(a)一括ダウンロード用に トランシェ(MW x logP の 2D グリッド)を選択するか、(b)化合物を取得し、RDKit でローカルに絞り込みます。このスキルでは、RDKit によるローカル処理の方法を紹介します。 - SMILES の検索は非同期です。 SMILES を POST すると
taskID が返され、結果をポーリングして取得します。完全一致の検索は数秒で完了します。 - 信頼性の高いプログラム検索は完全一致です。 API のより広範な類似体検索(
dist> 0)は遅く、公開サーバーでは頻繁にタイムアウトします。また、Smallworld 類似性検索はウェブサイトのみで利用できます。信頼できる基本機能として、SMILES の完全一致検索による ZINC ID の取得を利用してください。限られた代替手段については、「類似体/類似性検索」の注記を参照してください。
使用する場面
- 既知の ZINC ID を検索し、その SMILES、計算済み物性、購入可能性/サプライヤーを取得する
- SMILES で指定した分子を ZINC22 の識別子に解決し、入手可能か確認する
- ヒット化合物を発注する前に、購入可能かどうかと、取り扱いカタログを確認する
- ドッキングキャンペーンに使用する SMILES/ZINC ID のセットを作成する
- 物性で絞り込んだライブラリを構築する場合は、ここでの SMILES/トランシェの取得と、RDKit によるローカルでの絞り込み(
rdkit-cheminformatics)を組み合わせる - 既知の医薬品の生物活性データには
chembl-database-bioactivityを、承認済み医薬品の構造にはdrugbank-database-accessを使用する
前提条件
- Python パッケージ:
requests、pandas(物性をローカルで絞り込む場合はrdkitも必要) - データ要件:ZINC ID または SMILES 文字列
- 環境:インターネット接続が必要です。API キーは不要です。SMILES の検索は非同期です(結果をポーリングして取得します)
- レート制限:適切な間隔を保ってください。検索は順次実行し、結果をキャッシュして、タスクエンドポイントへのポーリングは数秒に 1 回より速く行わないでください
pip install requests pandas
# optional, for local property filtering:
pip install rdkit
クイックスタート
import requests
BASE = "https://cartblanche22.docking.org"
HEADERS = {"User-Agent": "sciagent-zinc-skill/1.0"}
# Look up a substance by ZINC ID (synchronous, returns JSON immediately)
r = requests.get(f"{BASE}/substance/ZINC000000029632.json", headers=HEADERS, timeout=30)
r.raise_for_status()
c = r.json()
td = c["tranche_details"]
print(f"ZINC ID : {c['zinc_id']} (db: {c['db']})")
print(f"SMILES : {c['smiles']}")
print(f"MW : {td['mwt']:.2f} logP: {td['logp']:.2f} heavy atoms: {td['heavy_atoms']}")
print(f"InChIKey: {td['inchikey']}")
print(f"Catalogs: {len(c.get('catalogs', []))} supplier entries")
出力例(抜粋):
ZINC ID : ZINC000000029632 (db: zinc20)
SMILES : O=C([C@H]1CCCN1C(=O)Cc1c[nH]c2ccccc12)N1CCc2ccccc2C1
MW : 387.48 logP: 3.29 heavy atoms: 29
InChIKey: QCEMBLKSDIWPBK-JOCHJYFZSA-N
Catalogs: 4 supplier entries
コア API
CartBlanche のベース URL と、全体で使用する再利用可能な 2 つのヘルパー:
import requests, time
BASE = "https://cartblanche22.docking.org"
HEADERS = {"User-Agent": "sciagent-zinc-skill/1.0"}
def get_substance(zinc_id):
"""Fetch full record for one ZINC ID (synchronous)."""
r = requests.get(f"{BASE}/substance/{zinc_id}.json", headers=HEADERS, timeout=30)
r.raise_for_status()
return r.json()
def smiles_lookup(smiles, dist=0, adist=0, database="zinc22", timeout=120, poll=5):
"""Resolve a SMILES to ZINC22 substance dicts via the async task API.
dist=0, adist=0 -> exact match (fast, reliable; use this).
dist/adist > 0 -> near-neighbor analog search (slow; may time out — see note).
Returns a list of substance dicts (possibly empty).
"""
files = {"smiles": (None, smiles), "dist": (None, str(dist)),
"adist": (None, str(adist)), "database": (None, database)}
sub = requests.post(f"{BASE}/smiles.json", files=files, headers=HEADERS, timeout=60)
sub.raise_for_status()
task = sub.json()["task"] # async: returns {"task": "<uuid>"}
deadline = time.time() + timeout
while time.time() < deadline:
res = requests.get(f"{BASE}/search/result/{task}", headers=HEADERS, timeout=30).json()
if res.get("status") == "SUCCESS":
data = res.get("result")
# result is {"zinc22": [...], "zinc20": [...]} when hits exist,
# or an empty list when there are none.
if isinstance(data, dict):
return data.get("zinc22", []) + data.get("zinc20", [])
return data or []
time.sleep(poll) # status == "PROGRESS" until done
raise TimeoutError(f"ZINC22 search task {task} did not finish in {timeout}s")
クエリ 1:ZINC ID で化合物を取得する
既知の ZINC 識別子を使って、レコード全体(SMILES、計算済み物性、化学式、環/ヘテロ原子の数、サプライヤーカタログ)を取得します。
c = get_substance("ZINC000000029632")
td = c["tranche_details"]
print(f"ZINC ID : {c['zinc_id']}")
print(f"SMILES : {c['smiles']}")
print(f"Formula : {c['mol_formula']}")
print(f"MW : {td['mwt']:.2f}")
print(f"logP : {td['logp']:.2f}")
print(f"Heavy atoms: {td['heavy_atoms']}")
print(f"Rings : {c['rings']} Hetero atoms: {c['hetero_atoms']}")
print(f"InChIKey : {td['inchikey']}")
print(f"Suppliers : {len(c.get('catalogs', []))}")
最新形式の ZINCbq.../ZINCbj... ID と、従来の 12 桁の ZINC000... ID は、どちらもこのエンドポイントで解決できます。
クエリ 2:SMILES から ZINC ID を解決する(完全一致)
SMILES 文字列で指定した分子に対応する ZINC22 の識別子を検索します。これは信頼性の高い検索の基本機能です。
hits = smiles_lookup("CC(=O)Nc1ccc(O)cc1") # acetaminophen, exact (dist=0)
print(f"Exact matches: {len(hits)}")
for h in hits[:5]:
td = h["tranche_details"]
print(f" {h['zinc_id']:22s} MW {td['mwt']:.1f} logP {td['logp']:.2f} {h['smiles']}")
1 つの分子が、ZINC22 の複数のサブデータベース(例:ZINCbq... と ZINCbj...)にわたって複数の ZINC ID に対応する場合があります。それぞれは、異なるビルドにおける同一構造を表します。
クエリ 3:購入可能性とサプライヤーカタログ
購入可能性は catalogs の下にある化合物レコードに含まれており、別途リクエストする必要はありません。
c = get_substance("ZINC000000029632")
catalogs = c.get("catalogs", [])
print(f"Supplier entries: {len(catalogs)}")
for cat in catalogs:
print(f" {cat.get('catalog_name', 'n/a'):24s} | "
f"purchasable: {cat.get('purchase')} | "
f"code: {cat.get('supplier_code', 'n/a')} | "
f"price: {cat.get('price', 'n/a')} {cat.get('unit', '')}")
purchase: 1 は、そのカタログから化合物を注文できることを示します。エントリーには、価格、数量、納期(shipping)が含まれる場合があります。
クエリ 4:物性で絞り込むライブラリ(ローカルでの RDKit フィルタリング)
ZINC22 には、サーバー側で MW/logP を指定するクエリ機能はありません。汎用的な方法は、候補の SMILES(ZINC ID を解決したもの、ダウンロードしたトランシェファイル、または独自に列挙したもの)を集め、RDKit でローカルにフィルタリングすることです。
import pandas as pd
from rdkit import Chem
from rdkit.Chem import Descriptors, Lipinski
# Candidate SMILES (here: a small hand list; in practice load a tranche .smi or your own set)
smiles_list = [
"CC(=O)Nc1ccc(O)cc1", # acetaminophen (MW 151 - too small, filtered out)
"CC(C)NCC(O)COc1cccc2ccccc12", # propranolol (MW 259, logP 2.6 - lead-like)
"CN(C)CCOC(c1ccccc1)c1ccccc1", # diphenhydramine
"c1ccc(NC(=O)c2ccccc2)cc1", # benzanilide (MW 197 - too small, filtered out)
]
def lead_like(smi):
m = Chem.MolFromSmiles(smi)
if m is None:
return None
mw, logp = Descriptors.MolWt(m), Descriptors.MolLogP(m)
hbd, hba = Lipinski.NumHDonors(m), Lipinski.NumHAcceptors(m)
ok = 250 <= mw <= 350 and 1 <= logp <= 3 and hbd <= 3 and hba <= 7
return {"smiles": Chem.MolToSmiles(m), "mw": round(mw, 1), "logp": round(logp, 2),
"hbd": hbd, "hba": hba} if ok else None
rows = [r for r in (lead_like(s) for s in smiles_list) if r]
df = pd.DataFrame(rows).drop_duplicates(subset=["smiles"])
print(f"Lead-like compounds after RDKit filter: {len(df)}")
print(df.head())
類似体/類似性検索(制限あり — 利用前にお読みください)
送信エンドポイントは、近傍類似体の検索に dist(トポロジカルなグラフ編集距離、0~3)と adist(「anon」距離、スキャフォールドのホップ数)を受け付けます。
# Near-neighbor analogs. Keep dist small (1) and the timeout generous; expect FEW results.
try:
analogs = smiles_lookup("c1ccc(NC(=O)c2ccccc2)cc1", dist=1, adist=0, timeout=240)
print(f"Analogs: {len(analogs)}")
for h in analogs[:10]:
print(" ", h["zinc_id"], h["smiles"])
except TimeoutError as e:
print("Analog search timed out:", e)
稼働中の公開サーバーで確認した注意事項:
dist=0(完全一致)は高速で信頼性があります。dist >= 1は共有クラスター上の数十億の分子を対象に実行され、頻繁にタイムアウトします(例:dist=3は約 270 秒以内に応答しませんでした)。成功した場合でも、クエリと少数のほぼ同一の構造しか返されないことがよくあります。- 広範な分子全体の類似性(グラフ編集距離に基づく Tanimoto)については、CartBlanche がウェブサイト上で別個の Smallworld 検索を提供しています(
cartblanche22.docking.org→ Similarity → Smallworld、/similarity/sw)。これは対話型のフローであり、ここでは単純な GET/POST JSON エンドポイントとして公開されていません。大規模な類似性検索にはウェブ UI を使用してください。 - 類似体検索で豊富な候補が返されることを前提とした自動化パイプラインは構築しないでください。ワークフローは、正確な検索と、RDKit/ケモインフォマティクスによるローカルでの候補拡張を基本にしてください。
主な概念
ZINC22 ID
ZINC22 では、ZINCbq0000001gLA のような短い英数字の ID が導入されました(接頭辞の文字はサブデータベース/トランシェを表します)。ZINC000000029632 のような従来の 12 桁の ID(ZINC15/ZINC20 由来)も、/substance/{id}.json を通じて引き続き解決できます。ID は必ず提示されたとおりに渡してください。
非同期検索モデル
SMILES 検索はタスクとしてキューに追加されます。
POST /smiles.json(multipart フォーム:smiles、dist、adist、database)→{"task": "<uuid>"}- 実行中は
GET /search/result/{task}→{"status": "PROGRESS"}が返され、完了すると{"status": "SUCCESS", "result": {...}}が返されます - 成功時、ヒットがある場合の
resultはサブデータベース(zinc22、場合によってはzinc20)をキーとする dict で、ヒットがない場合は空のリストです。上記のヘルパーは、どちらの形式も正規化します。
トランシェ
ZINC は、重原子数/MW(H コード。例:H11)と logP(文字のビン)に基づき、分子を「トランシェ」の 2 次元グリッドに分類しています。CartBlanche の Tranches ブラウザー(cartblanche22.docking.org、「3D」/「2D」トランシェページ)では、物性領域を選択して、ドッキング用の SMILES/SDF を一括ダウンロードできます。トランシェの一括ダウンロードは対話型で(ファイルサイズも大きいため)、プログラムによる物性フィルタリングには、クエリ 4 の RDKit による方法を使うか、files.docking.org からトランシェファイルを取得してローカルでフィルタリングしてください。
結果フィールド
各検索/化合物レコードには、zinc_id、smiles、mol_formula、rings、hetero_atoms、db、catalogs[]、および tranche_details(mwt、logp、heavy_atoms、inchi、inchikey)が含まれます。SMILES 検索の行には、さらに sub_id、matched_smiles、tranche コードが含まれます。
一般的なワークフロー
ワークフロー 1:ヒットリストの購入可能性を確認する
目的:ドッキングで得られたヒットの SMILES を受け取り、それらの ZINC22 ID を解決し、購入可能なものを報告します。
import pandas as pd
hit_smiles = [
"CC(=O)Nc1ccc(O)cc1", # acetaminophen
"c1ccc(NC(=O)c2ccccc2)cc1", # benzanilide
]
rows = []
for smi in hit_smiles:
matches = smiles_lookup(smi) # exact
if not matches:
rows.append({"query_smiles": smi, "zinc_id": None, "purchasable": False})
continue
for m in matches:
cats = m.get("catalogs", [])
rows.append({
"query_smiles": smi,
"zinc_id": m["zinc_id"],
"mw": m["tranche_details"]["mwt"],
"n_catalogs": len(cats),
"purchasable": any(c.get("purchase") for c in cats) or bool(cats),
})
df = pd.DataFrame(rows)
print(df)
df.to_csv("hit_purchasability.csv", index=False)
print("Saved: hit_purchasability.csv")
ワークフロー 2:候補セットを解決し、ドッキング用に物性で絞り込む
目的:候補 SMILES のリスト(独自に列挙したもの、またはトランシェファイル)を受け取り、ZINC22 に登録されている/購入可能なものを残し、リード類似の領域に絞り込んで、ドッキング用の SMILES をエクスポートします。
import pandas as pd
from rdkit import Chem
from rdkit.Chem import Descriptors
candidates = [
"CC(=O)Nc1ccc(O)cc1",
"O=C([C@H]1CCCN1C(=O)Cc1c[nH]c2ccccc12)N1CCc2ccccc2C1",
"c1ccc(NC(=O)c2ccccc2)cc1",
]
rows = []
for smi in candidates:
m = Chem.MolFromSmiles(smi)
if m is None:
continue
mw, logp = Descriptors.MolWt(m), Descriptors.MolLogP(m)
if not (200 <= mw <= 500 and -1 <= logp <= 5): # lead-like local filter
continue
matches = smiles_lookup(smi) # confirm it exists in ZINC22
if not matches:
continue
m0 = matches[0]
rows.append({"zinc_id": m0["zinc_id"], "smiles": Chem.MolToSmiles(m),
"mw": round(mw, 1), "logp": round(logp, 2),
"purchasable": bool(m0.get("catalogs"))})
df = pd.DataFrame(rows).drop_duplicates(subset=["smiles"])
print(f"ZINC22-confirmed lead-like compounds: {len(df)}")
df[["smiles", "zinc_id"]].to_csv("docking_library.smi", sep=" ", index=False, header=False)
print("Saved: docking_library.smi")
print(df)
主要なパラメーター
| パラメーター | エンドポイント | デフォルト | 範囲/オプション | 効果 |
|---|---|---|---|---|
| (path) | GET /substance/{zinc_id}.json | — | ZINC ID | 物質を同期的に検索します |
smiles | POST /smiles.json | — | 有効な SMILES | 分子を検索します |
dist | POST /smiles.json | 0 | 0–3 | トポロジカル距離です。0 = 完全一致(信頼性あり)、>=1 = 類似体検索(低速で、タイムアウトする場合があります) |
adist | POST /smiles.json | 0 | 0–3 | 匿名グラフ距離(スキャフォールドホッピング)です。0 = オフ |
database | POST /smiles.json | zinc22 | "zinc22"、"zinc20" | 検索する ZINC ビルド |
task | GET /search/result/{task} | — | 送信時に取得した UUID | 非同期結果のポーリング用ハンドル |
ベストプラクティス
-
ZINC15 ではなく ZINC22(CartBlanche)を使用してください。
zinc15.docking.orgの REST API は CAPTCHA で保護されており、スクリプトからは利用できません。ここで扱うすべてのエンドポイントはcartblanche22.docking.orgを対象としています。 -
完全一致検索を活用し、類似体検索には依存しないでください。
dist=0は信頼性の高い基本機能です。類似体や類似性を調べる場合は、ローカルで検索範囲を広げる(RDKit、rdkit-cheminformatics)か、Smallworld のウェブ UI を使用してください。類似体 API は広範なクエリでタイムアウトします。 -
RDKit でプロパティをローカルにフィルタリングしてください。 ZINC22 には、サーバー側で MW/logP/HBD を指定して検索する機能はありません。候補を取得してから RDKit でフィルタリングしてください。PAINS/Brenk アラートを適用するのもこの段階です。
-
適切な間隔でポーリングしてください。 SMILES 検索は非同期です。数秒ごとに
/search/result/{task}をポーリングし、妥当なタイムアウトを設定して、PROGRESSからSUCCESSへの遷移(およびTimeoutError)を処理してください。 -
正規 SMILES で重複を排除してください。 同じ構造が複数の ZINC ID/カタログに登録されていることがあります。ドッキングの前に、RDKit(
Chem.MolToSmiles(Chem.MolFromSmiles(smi)))で正規化してください。 -
catalogsで購入可能か確認してください。purchase: 1のcatalogsエントリーは注文可能なものとして扱い、調達用にsupplier_codeとcatalog_nameを記録してください。 -
結果をキャッシュしてください。 ZINC のデータは定期的に更新されます。日付入りのファイル名で物質/検索の JSON をキャッシュし、プロジェクト内で再検索しないようにしてください。
よく使うレシピ
レシピ:SMILES から ZINC22 ID と購入可能かどうかを検索する
matches = smiles_lookup("CC(=O)Nc1ccc(O)cc1") # acetaminophen
for m in matches:
cats = m.get("catalogs", [])
print(f"{m['zinc_id']} | MW {m['tranche_details']['mwt']:.1f} | "
f"catalogs {len(cats)} | purchasable {bool(cats)}")
レシピ:ZINC ID のリストを一括で解決する
zinc_ids = ["ZINC000000029632", "ZINCbq0000001gLA"]
for zid in zinc_ids:
try:
c = get_substance(zid)
print(f"{zid}: {c['smiles']} MW {c['tranche_details']['mwt']:.1f}")
except requests.HTTPError as e:
print(f"{zid}: not found ({e.response.status_code})")
レシピ:ライブラリのプロパティ分布
import pandas as pd
from rdkit import Chem
from rdkit.Chem import Descriptors
df = pd.read_csv("docking_library.smi", sep=" ", names=["smiles", "zinc_id"])
df["mw"] = df["smiles"].map(lambda s: Descriptors.MolWt(Chem.MolFromSmiles(s)))
df["logp"] = df["smiles"].map(lambda s: Descriptors.MolLogP(Chem.MolFromSmiles(s)))
print(f"Library size: {len(df)}")
print(df[["mw", "logp"]].describe())
トラブルシューティング
| 問題 | 原因 | 解決策 |
|---|---|---|
zinc15.docking.org へのリクエストが /captcha/ にリダイレクトされる、または 403 が返される | 従来の ZINC15 REST API は CAPTCHA で保護されており、スクリプトからは使用できません | ここに示すように、すべての呼び出しを cartblanche22.docking.org(ZINC22 / CartBlanche)に切り替えてください |
GET /substances/{id}.json(複数形)が HTML を返す | パスが誤っています。これは SPA のフォールバック先です | 単数形の /substance/{id}.json を使用してください |
エンドポイントが content-type: application/json とともに 200 を返すが、本文は HTML | CartBlanche は不明なルートに対して SPA のシェルを返します(誤解を招く content-type) | 本文が実際に JSON として解析できることを確認してください。データを返すのは、ドキュメントに記載されたエンドポイントのみです |
POST /smiles.json が {"task": ...} を返すが、結果を期待していた | SMILES 検索は非同期です | status == "SUCCESS" になるまで GET /search/result/{task} をポーリングしてください |
400 No Valid SMILES, please try again | SMILES が拒否されたか、リクエスト形式が誤っています | RDKit で SMILES を検証し、マルチパートフォームのフィールド smiles/dist/adist/database を使って送信してください |
成功したタスクで KeyError: 'data' / 'zinc22' が発生する | ヒットがある場合、result は dict ですが、ヒットがない場合は空のリストです | どちらの形式にも対応してください(smiles_lookup ヘルパーを参照) |
アナログ検索(dist >= 1)がタイムアウトする、または完全一致のみを返す | 共有クラスター上で検索空間が非常に大きい | dist=0(完全一致)を使用してください。類似性検索には Smallworld の Web UI を使用し、RDKit でローカルにアナログを展開してください |
/substance/random.json が 400 を返す。/substance/random/{n}.json が {"status":"PENDING"} を返す | ランダムエンドポイントは非同期で、処理も簡単ではありません | スクリプトでの使用は不要です。代わりに既知の ZINC ID を選んでください |
化合物 ID に対して HTTP 404 が返される | ID の入力ミス、またはこのビルドに含まれていません | ZINC ID を確認してください。旧 ID の場合は database="zinc20" を試してください |
関連スキル
rdkit-cheminformatics— 取得した ZINC 化合物の特性を計算し、PAINS/Brenk フィルターを適用します(ここでの特性フィルタリングに必要)autodock-vina-docking— エクスポートした ZINC の SMILES/SDF ファイルを分子ドッキングのキャンペーンで使用しますchembl-database-bioactivity— ZINC のバーチャルスクリーニングで特定された化合物の生物活性データdrugbank-database-access— 承認済み医薬品の構造および注釈
参考資料
- CartBlanche / ZINC22 Web アプリ — 化合物の検索、SMILES 検索、トランシェブラウザー
- ZINC22 論文 — Tingle ら、「ZINC-22, A Free Multi-Billion-Scale Database of Tangible Compounds for Ligand Discovery」、J. Chem. Inf. Model. 2023
- docking.org wiki — CartBlanche の使用方法と ZINC22 のダウンロードに関するドキュメント
- files.docking.org — ZINC22 のトランシェファイル一括ダウンロード