Skip to main content

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_v2 (default) or core_v1 chemistry
  • 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_v2" is the default as of 0.5.0: CORE 2 Triangulum, spanning 16 transformations including multicomponent and multistep chemistry. It prices by reaction class — $250 for a two-component coupling, $375 for anything multicomponent — so building-block cost does not move the price.

space="core_v1" is the previous production chemistry, priced by building-block cost in risk-multiplied buckets starting at $125. Pass it explicitly if you were relying on that schedule, or on decompose.

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 — and core_v1 is no longer the default. Since 0.5.0 a plain decompose=True call runs against core_v2 and drops the knob with a warning; pass space="core_v1" to keep the old behaviour. 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.5.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for onepot 0.5.0
File Size Uploaded
onepot-0.5.0.tar.gz 8.4 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for onepot 0.5.0
File Interpreter ABI Platform
onepot-0.5.0-py3-none-any.whl Python 3 none any Details

Total release size: 18.0 kB

Release files / onepot-0.5.0.tar.gz

Download URL onepot-0.5.0.tar.gz
Size 8.4 kB
Tags Source
SHA-256 checksum
How to use checksums
8fc586d7227b1f24dabc28bb688508c5cef93710208c50535905d71b1da682df
BLAKE2b-256 checksum
How to use checksums
51c518b4f32ef1c583b3d76bc7b68cf5df207140e31dee68dbf4f35f85f8969e
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.5.0-py3-none-any.whl

Download URL onepot-0.5.0-py3-none-any.whl
Size 9.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
046d60d102551f8a5ec1319504d65ffd77d0f5342c7ee06631524ec481b23232
BLAKE2b-256 checksum
How to use checksums
8a7d2e99192bb6a9b50b62612ee28f91ea0473aae0e971d3e4cf62a0f59b1ab8
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 history Release notifications | RSS feed

This release

0.5.0 This release

2 release files

0.4.0

2 release files

0.3.0

2 release files

0.2.1

2 release files

0.2.0

2 release files

0.1.6

2 release files

0.1.5

2 release files

0.1.4

2 release files

0.1.3

2 release files

0.1.2

2 release files

0.1.1

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page