onepot-python
Python client for the onepot API — find purchasable analogs of your query molecules, price exact molecules directly, with optional retrosynthesis decomposition and per-position building-block filtering.
Installation
uv add onepot
# or
pip install onepot
Quick start
from onepot import Client
client = Client(api_key="your-api-key")
resp = client.search(
smiles_list=["c1ccc(NC(=O)c2ccccc2)cc1"],
max_results=10,
)
for r in resp["queries"][0]["results"]:
print(r["smiles"], r["similarity"], r["price_usd"])
Features
- Search spaces — run a query against
core_v1(default) orcore_v2chemistry - Similarity search — Tanimoto nearest analogs from the onepot catalog
- Exact pricing — price the exact query molecule directly, with an opt-in stereo-relaxed fallback; fast, cheap bulk pricing of pre-enumerated libraries
- Substructure search — purchasable molecules containing a SMILES/SMARTS pattern
- Decomposition + BB filters — inspect the retro paths the system considered for your query, then refine which candidate BBs are eligible per position
- Risk and price filters — exclude results above a chemistry-risk, supplier-risk, or price threshold
- Streaming — single-molecule searches with SSE progress updates
- Ordering — submit results for synthesis quoting
Search
Basic
resp = client.search(smiles_list=["c1ccc(-c2ccccc2)cc1"], max_results=10)
curl -X POST https://api.onepot.ai/v1/search \
-H "Content-Type: application/json" \
-H "X-API-Key: your-api-key" \
-d '{"smiles_list": ["c1ccc(-c2ccccc2)cc1"], "max_results": 10}'
Search spaces
Every search runs against one space — a bundle of building blocks, reactions, and the feasibility model scoring them. space="core_v1" is the default and the current production chemistry. space="core_v2" is CORE 2 Triangulum: a larger space spanning 16 transformations, including multicomponent and multistep chemistry. It prices on its own schedule; the price_usd on a result always reflects the space the search ran in.
resp = client.search(
smiles_list=["c1ccc(NC(=O)c2ccccc2)cc1"],
max_results=10,
space="core_v2",
)
print(resp["space"]) # "core_v2"
curl -X POST https://api.onepot.ai/v1/search \
-H "Content-Type: application/json" \
-H "X-API-Key: your-api-key" \
-d '{"space": "core_v2", "smiles_list": ["c1ccc(-c2ccccc2)cc1"], "max_results": 10}'
Both search() and search_stream() take space, and both echo it back — on the response dict for the batch endpoint, and on the complete event when streaming. Credits are unaffected by the space you pick.
decompose and bb_filters are core_v1 only. core_v2 does not publish its retrosynthetic decompositions, and a reaction_class id exists nowhere but in a decompositions block — so neither knob has anything to work with there. Passing either alongside space="core_v2" drops it with a Python warning and runs the search without it; the results themselves are unaffected, since decomposition-based enumeration happens either way. Reaching the API directly with both, rather than through this client, returns the search plus a warnings list saying the same thing.
Within core_v1, reaction_class strings are only meaningful in the space that produced them, so a bb_filters entry has to come from a decompose=True call made in that same space (mixing them is rejected as 422).
Exact lookup
Use exact_lookup=True to price each query molecule directly and skip the analog/similarity search. Each query returns at most one result — the query molecule itself (similarity 1.0), priced from a catalog match or its cheapest single-step decomposition — or no result if it can't be made from catalog building blocks. It never substitutes an analog. This is the fast, cheap path for bulk pricing of a pre-enumerated library, and bills at 0.1× (see Pricing).
resp = client.search(
smiles_list=my_enumerated_library, # e.g. thousands of SMILES
exact_lookup=True,
include_chemistry_risk=True,
)
for q in resp["queries"]:
if q["results"]:
print(q["query_smiles"], q["results"][0]["price_usd"])
else:
print(q["query_smiles"], "not priceable")
curl -X POST https://api.onepot.ai/v1/search \
-H "Content-Type: application/json" \
-H "X-API-Key: your-api-key" \
-d '{"smiles_list": ["c1ccc(NC(=O)c2ccccc2)cc1"], "exact_lookup": true}'
The response uses the standard shape, with results holding 0 or 1 entry per query. Exact-lookup results are not annotated with reaction_class / bbs. Cannot be combined with substructure_search, decompose, or bb_filters (rejected as 422). Streaming supports it too via client.search_stream(..., exact_lookup=True).
To accept a racemate or unspecified-stereo structure when strict identity cannot
be priced, pass no_stereo=True. Strict identity is always attempted first. A
fallback result contains the stereochemistry-stripped smiles / inchikey and
"stereo_relaxed": true.
resp = client.search(
smiles_list=["N[C@@H](C)C(=O)O"],
exact_lookup=True,
no_stereo=True,
)
no_stereo=True also applies to the guaranteed exact candidate included in an
ordinary similarity search. It cannot be combined with substructure_search or
bb_filters.
Streaming
For single-molecule searches with real-time progress events. Status lifecycle: starting → synthesis → rescoring → complete (with pricing replacing the middle two under exact_lookup=True). The final event includes a space key and a results list with the same fields as the batch endpoint. A failure mid-stream arrives as a single event with status error.
for event in client.search_stream("c1ccc(NC(=O)c2ccccc2)cc1", max_results=10):
print(event["status"], event["message"])
if event["status"] == "complete":
results = event["results"]
curl -sN -X POST https://api.onepot.ai/v1/search/stream \
-H "Content-Type: application/json" \
-H "X-API-Key: your-api-key" \
-d '{"smiles": "c1ccc(NC(=O)c2ccccc2)cc1", "max_results": 5}'
Substructure search
Pass substructure_search=True to return purchasable molecules that contain the query as a substructure, instead of similarity hits. The query can be a SMILES or a SMARTS pattern.
resp = client.search(
smiles_list=["c1ccc(C(=O)N)cc1"],
max_results=10,
substructure_search=True,
)
curl -X POST https://api.onepot.ai/v1/search \
-H "Content-Type: application/json" \
-H "X-API-Key: your-api-key" \
-d '{"smiles_list": ["c1ccc(C(=O)N)cc1"], "max_results": 10, "substructure_search": true}'
Risk and price filters
All optional. When set, results that exceed the threshold are excluded.
| Parameter | Type | Values |
|---|---|---|
max_price |
int | USD, e.g. 200, 500 |
max_supplier_risk |
string | "low", "medium", "high" |
max_chemistry_risk |
string | "low", "medium", "high" |
Setting max_chemistry_risk automatically includes the chemistry_risk field in the response. Pass include_chemistry_risk_score=True for the raw probability score.
resp = client.search(
smiles_list=["c1ccc(-c2ccccc2)cc1"],
max_results=10,
max_price=500,
max_supplier_risk="medium",
max_chemistry_risk="low",
include_chemistry_risk_score=True,
)
Decompose & bb_filters
Available in core_v1 only — see Search spaces. Use decompose=True to receive the retrosynthetic paths the system considered for each query — every reaction_class it found and the BB SMILES of your query at each position. Then call back with bb_filters to constrain which candidate BBs are eligible per position. Every enumerated result is automatically tagged with the reaction_class it was made from and the bbs that built it.
Call 1 — discover.
resp = client.search(
smiles_list=["c1ccc(NC(=O)c2ccccc2)cc1"],
max_results=5,
decompose=True,
)
decompositions = resp["queries"][0]["decompositions"]
rxn = decompositions[0]["reaction_class"] # e.g. "rxn_5e820be4"
Call 2 — refine. Force the building block at position 1 to vary (Tanimoto ≤ 0.7 to the query's position-1 BB) while leaving position 0 free.
resp = client.search(
smiles_list=["c1ccc(NC(=O)c2ccccc2)cc1"],
max_results=10,
bb_filters=[{"reaction_class": rxn, "bb_index": 1, "max_similarity": 0.7}],
)
for r in resp["queries"][0]["results"]:
if r.get("reaction_class") == rxn:
bb_smiles = [b["smiles"] for b in r["bbs"]]
print(r["smiles"], "←", " + ".join(bb_smiles))
curl -X POST https://api.onepot.ai/v1/search \
-H "Content-Type: application/json" \
-H "X-API-Key: your-api-key" \
-d '{
"smiles_list": ["c1ccc(NC(=O)c2ccccc2)cc1"],
"max_results": 10,
"bb_filters": [
{"reaction_class": "rxn_<from-call-1>", "bb_index": 1, "max_similarity": 0.7}
]
}'
reaction_class values like "rxn_5e820be4" come from a prior decompose=True response and are stable across calls — pass them through as strings. Each bb_filters entry takes optional min_similarity and max_similarity (Tanimoto, 0.0–1.0); omit a bound to leave that side open. Combine multiple entries to constrain multiple positions in one call. bb_index is the 0-based position of the building block within the reaction, matching the ordering in the bbs field of a decomposition or annotated result. Unknown reaction_class or min_similarity > max_similarity is rejected as 422. Streaming searches accept the same parameters via client.search_stream(...).
When a retro decomposition produces multiple paths under the same reaction_class, filters apply to each path's candidates independently (similarity is measured against that path's BB SMILES, so the same SMILES can pass one path's filter and fail another's).
Response shape
{
"space": "core_v1",
"queries": [
{
"query_smiles": "c1ccc(NC(=O)c2ccccc2)cc1",
"query_inchikey": "...",
"results": [
{
"smiles": "...",
"inchikey": "...",
"similarity": 0.92,
"price_usd": 590,
"supplier_risk": "low",
"chemistry_risk": "medium", # if include_chemistry_risk=True
"chemistry_risk_score": 0.5, # if include_chemistry_risk_score=True
# present on enumerated results (synthesized analogs):
"reaction_class": "rxn_5e820be4",
"bbs": [
{"bb_index": 0, "smiles": "<bb-smiles>"},
{"bb_index": 1, "smiles": "<bb-smiles>"},
],
},
...
],
# if decompose=True:
"decompositions": [
{
"reaction_class": "rxn_5e820be4",
"bbs": [
{"bb_index": 0, "smiles": "<bb-smiles>"},
{"bb_index": 1, "smiles": "<bb-smiles>"},
],
},
...
],
},
...
],
"credits_used": 10,
"credits_remaining": 990,
}
Order
Submit results for synthesis quoting. Returns an order_id you can reference in followup.
order = client.order(
smiles=["CCO", "c1ccccc1"],
email="you@example.com",
notes="Optional notes",
)
# {"order_id": "a1b2c3d4-...", "molecule_count": 2}
curl -X POST https://api.onepot.ai/v1/order \
-H "Content-Type: application/json" \
-H "X-API-Key: your-api-key" \
-d '{
"smiles": ["CCO", "c1ccccc1"],
"email": "you@example.com",
"notes": "Optional notes"
}'
Pricing
Credits are charged per SMILES in the query, by mode and chemistry-risk tier:
| Tier | Full search | Exact lookup |
|---|---|---|
| Base | 1 | 0.1 |
include_chemistry_risk=True |
5 | 0.5 |
include_chemistry_risk_score=True |
10 | 1.0 |
decompose, bb_filters, and substructure_search don't change the price.
exact_lookup=True bills at 0.1× the full-search rate (it skips the analog search). The total is charged as a whole number per request — the per-SMILES rate × molecule count, rounded, with a minimum of 1 credit per request. So a 5,000-molecule exact base search costs 500 credits, while a single molecule costs 1.
Metadata
Release files for onepot 0.4.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| onepot-0.4.0.tar.gz | 8.3 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| onepot-0.4.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 17.8 kB
Release files / onepot-0.4.0.tar.gz
| Download URL | onepot-0.4.0.tar.gz |
|---|---|
| Size | 8.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
f6d18feec0b5cfc9e99842a0f89bb3dbb026a861ccf2763693ac6026b221637c
|
|
BLAKE2b-256 checksum How to use checksums |
6eaa2986aaf86bd6f45a018a60a08d0ace8f124e6cc3176a53dc555029c14302
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.9.18 {"installer":{"name":"uv","version":"0.9.18","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Release files / onepot-0.4.0-py3-none-any.whl
| Download URL | onepot-0.4.0-py3-none-any.whl |
|---|---|
| Size | 9.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
1af7115468ffb24af7602ca757ede2ccd1f1a1d0031a18863d9a872763e1425b
|
|
BLAKE2b-256 checksum How to use checksums |
bfcb36a1a7ad02f1d1774fc02eca415bebc36c1c365bcda9ac18b06bb6f258c8
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.9.18 {"installer":{"name":"uv","version":"0.9.18","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|