Om Python SDK (omtx)
Official Python SDK for the Om API.
The SDK talks to the public Om API /v2/* surface for Om-owned workflows and
to RCSB public file URLs for exact PDB structure downloads. It covers:
- Diligence workflows and job polling
- Active public Hub workflows through
client.hub.submit(...)and selected typed helpers - Hosted LULA-1 and LULA-2 protein-sequence plus SMILES scoring
- Artifact upload for artifact-backed Hub jobs
- Entitlement-scoped dataset catalog, shard exports, and Polars-backed
OmDataloaders - Private data-generation order helpers for subscription quota and invoice-backed orders
- Om Accessible Space molecule availability, quote, Wallet Credits order, and order status helpers
- Public RCSB PDB/mmCIF structure download helpers
- Health and Wallet Credits helpers
Public docs: https://omtx.ai/docs
Installation
pip install omtx
Compatibility
For best compatibility:
- Linux: modern Ubuntu on x86_64 or arm64
- macOS: Apple Silicon with a native
arm64Python interpreter - macOS Python distribution: Miniforge or Mambaforge recommended
The dataframe helpers in omtx use polars:
load_binders(...)load_nonbinders(...)load_data(...)
If you are on Apple Silicon, use a native arm64 shell and Python. Avoid
Rosetta / x86_64 Python for dataframe-backed workflows.
If you are on older x86_64 hardware, or if the default polars runtime fails
with CPU-feature errors, install the compatibility runtime:
pip install "polars[rtcompat]"
Or install both in one step:
pip install omtx "polars[rtcompat]"
JSON-based SDK methods may still work without rtcompat, but dataframe helpers
are not guaranteed on older x86_64 CPUs unless the compatibility runtime is
installed.
Setup
export OMTX_API_KEY="your-api-key"
The SDK targets https://api.omtx.ai.
Quick Start
from omtx import OmClient
with OmClient() as client:
print(client.status())
job = client.diligence.deep_diligence(
query="CRISPR applications in cancer therapy",
preset="quick",
)
result = client.jobs.wait(
job["job_id"],
result_endpoint="/v2/jobs/deep-diligence/{job_id}",
)
print(result.get("result", {}).get("total_claims"))
Hub Quick Start
from omtx import OmClient
with OmClient() as client:
artifact = client.artifacts.upload("target.cif")
job = client.hub.boltzgen(
protocol="protein_anything",
target_cif_artifact_id=artifact["artifact_id"],
target_chain_id="A",
binder_length_min=90,
binder_length_max=110,
idempotency_key="boltzgen-demo-20260325",
)
status = client.jobs.wait(job["job_id"], poll_interval=5, timeout=3600)
print(status["status"])
For active public Hub models without a dedicated typed helper, use
client.hub.submit(job_type="hub.<model>", payload=...).
Hosted LULA Scoring
from omtx import OmClient
with OmClient() as client:
scores = client.lula2.score(
protein_sequence="MEEPQSDPSV",
smiles=["CCO", "c1ccccc1"],
)
print(scores[0]["score"])
Hosted client.lula1.score(...) and client.lula2.score(...) accept only a
protein sequence and SMILES. The SDK does not expose mode; LULA-1 and LULA-2
are separate product namespaces. Legacy async client.hub.lula1(...) remains
available for current Hub job compatibility.
LULA score records include score, rank, and top_percentile_in_batch.
score is a bounded 0-1 model score intended for ranking and enrichment, not a
calibrated binding probability. Rank and percentile are numeric values computed
within the submitted batch.
Local LULA-1 Open-Weight Scoring
Install the optional local scorer only when you want to run LULA-1 weights on your own machine:
pip install "omtx[lula]>=2.0.16"
hf auth login
omtx lula download --model lula1.1
omtx lula verify
Model card, weights, license, and notebook:
huggingface.co/omtx/lula-1.1.
Quickstart notebook:
lula1_quickstart.ipynb.
LULA-1 open-weight releases are public gated on Hugging Face. By downloading,
accessing, or using LULA-1, you agree to the
Om LULA Community License 1.1.
Commercial use requires a separate Om commercial license; email dmc@omtx.ai
for commercial licensing.
This includes commercial hosted inference, paid API/SaaS access, resale, paid
support/deployment, bundling model access into a paid product, and competing
model services.
Use --model lula1 for LULA-1 and --model lula1.1 for LULA-1.1. In Python,
pass the same public selector to load_model(...).
Hugging Face login is required for gated LULA weights. In Colab, omtx>=2.0.16
does not pin NumPy below 2.
Score a real target:
from omtx.lula import load_model
CA2 = (
"MSHHWGYGKHNGPEHWHKDFPIAKGERQSPVDIDTHTAKYDPSLKPLSVSYDQATSLRIL"
"NNGHAFNVEFDDSQDKAVLKGGPLDGTYRLIQFHFHWGSLDGQGSEHTVDKKKYAAELHL"
"VHWNTKYGDFGKAVQQPDGLAVLGIFLKVGSAKPGLQKVVDVLDSIKTKGKSADFTNFDP"
"RGLLPESLDYWTYPGSLTTPPLLECVTWIVLKEPISVSSEQVLKFRKLNFNGEGEPEELM"
"VDNWRPAQPLKNRQIKASFK"
)
mols = [
"CC(=O)Nc1nnc(s1)S(N)(=O)=O",
"Cc1ccc(cc1)S(=O)(=O)N",
"CC(C)Cc1ccc(cc1)C(C)C(=O)O",
"CCN(CC)CCNC(=O)c1ccc(N)cc1",
"c1ccc(cc1)C(=O)O",
"CCO",
]
model = load_model("lula1.1")
for row in sorted(model.score(protein_sequence=CA2, smiles=mols), key=lambda r: r["rank"]):
print(row["rank"], round(row["score"], 4), row["smiles"])
Local scoring uses downloaded weights and cached encoder assets. It does not
contact Om or Hugging Face during scoring unless you explicitly run a download.
The local scorer returns the same customer-facing score fields as hosted LULA:
score, rank, and top_percentile_in_batch. The canonical score remains the
0-1 model score; rank and percentile are batch-ranking aids. Do not interpret
any LULA score field as an experimental binding probability unless you have
calibrated it against your own assay data.
If you pass --encoder-cache-dir to omtx lula download, pass the same value to
omtx lula score.
Molecule Fulfillment
from uuid import uuid4
from omtx import OmClient
from omtx.lula import load_model
protein_sequence = "MEEPQSDPSV"
candidate_smiles = ["CC(=O)Oc1ccccc1C(=O)O", "CCO"]
model = load_model("lula1.1")
scores = sorted(
model.score(protein_sequence=protein_sequence, smiles=candidate_smiles),
key=lambda row: row["rank"],
)
selected = [row.get("canonical_smiles") or row["smiles"] for row in scores[:10]]
with OmClient() as client:
hits = client.molecules.search(
smiles_list=selected,
max_results=5,
)
quote = client.molecules.quote(
items=[{"smiles": smiles, "quantity": 1} for smiles in selected]
)
addresses = client.molecules.shipping_addresses()
shipping_address_id = addresses["default_shipping_address_id"]
order = client.molecules.order(
items=[{"smiles": smiles, "quantity": 1} for smiles in selected],
shipping_address_id=shipping_address_id,
idempotency_key=f"molecule-order-{uuid4()}",
)
The molecule namespace is a thin Om API wrapper for Om Accessible Space
fulfillment. OnePot is the current provider behind this flow; additional
providers can be added behind the same Om fulfillment surface. Search is an
availability screen, while quote and order creation validate exact purchasable
matches. Shipping address reads call
/v2/molecules/fulfillment/shipping-addresses; order creation calls
/v2/molecules/fulfillment/orders. Orders are Wallet Credits-funded and require
an Idempotency-Key.
Data-Generation Orders
from omtx import OmClient
with OmClient() as client:
sequences = [{"name": "target", "sequence": "M" * 120}]
quota_order = client.data_generation.quota_order(
sequences=sequences,
idempotency_key="dg-quota-target-001",
)
invoice_order = client.data_generation.invoice_order(
sequences=sequences,
idempotency_key="dg-invoice-target-001",
)
Public API data-generation order creation is private by default. Extra data-generation orders are invoice-only through the SDK/API/MCP surface; card checkout remains a frontend/internal flow.
Data Access
Browse published protein-specific models with client.models.catalog().
Generated and covered raw-data access is entitlement-scoped; fetch a protein_uuid from
client.datasets.catalog() or client.datasets.generated_protein_uuids() for
the authenticated account before loading generated or otherwise covered data.
Primary training flow (single call):
loaded = client.load_data(
protein_uuid="YOUR_GENERATED_PROTEIN_UUID",
binders=50000, # required
nonbinder_multiplier=5, # optional, default 5x background negatives
# nonbinders=200000, # optional explicit override (wins over multiplier)
sample_seed=42, # optional: deterministic sampling
)
binders = loaded["binders"]
nonbinders = loaded["nonbinders"]
print(binders.shape, nonbinders.shape)
binders.show(top_n=24) # defaults: smiles_col="smiles", sort_by="binding_score"
binders.show(top_n=24, sort_by="selectivity_score")
# show() renders inline in notebooks; no extra display() wrapper needed.
load_data(...) samples non-binders from the Gateway-provided non-binder
source. New binder-only vintages use canonical NEGATIVE-control binder shards;
the SDK adaptively reads a bounded randomized NEGATIVE window distributed across
returned shard URLs, excludes molecules already seen as target binders, and
reads up to the full NEGATIVE pool only when needed. Requested non-binders are
capped at 20x requested binders; if the
anti-joined NEGATIVE pool is smaller than the requested count, the SDK returns
the available non-overlapping rows.
Canonical NEGATIVE responses must include explicit source metadata
(source_protein_uuid and source_vintage_id); missing metadata is treated as
a contract error. Legacy target-specific non-binder shards remain supported.
Explicit per-pool loading (advanced control):
binders = client.load_binders(
protein_uuid="YOUR_GENERATED_PROTEIN_UUID",
n=1000, # optional: random sample size
sample_seed=42, # optional: deterministic sampling
)
nonbinders = client.load_nonbinders(
protein_uuid="YOUR_GENERATED_PROTEIN_UUID",
n=10000, # optional: random sample size
sample_seed=42, # optional: deterministic sampling
)
print(binders.shape, nonbinders.shape)
# Omit n (or set n=None) to load the full pool.
# binders = client.load_binders(protein_uuid="...")
# nonbinders = client.load_nonbinders(protein_uuid="...") # legacy or derived pools
Manual shard export URLs (advanced use):
urls = client.binders.urls(
protein_uuid="YOUR_GENERATED_PROTEIN_UUID",
)
print("Binder shard URLs:", len(urls["binder_urls"]))
print("Non-binder shard URLs:", len(urls["non_binder_urls"]))
print("First binder URL:", urls["binder_urls"][0] if urls["binder_urls"] else None)
Generated proteins available now:
protein_uuids = client.datasets.generated_protein_uuids()
print("Generated protein UUIDs:", protein_uuids[:5])
Module-level convenience:
import omtx as om
loaded = om.load_data(
protein_uuid="YOUR_GENERATED_PROTEIN_UUID",
binders=50000,
nonbinder_multiplier=5,
sample_seed=42,
)
print(loaded["binders"].shape, loaded["nonbinders"].shape)
binders = om.load_binders(
protein_uuid="YOUR_GENERATED_PROTEIN_UUID",
n=1000,
sample_seed=42,
)
nonbinders = om.load_nonbinders(
protein_uuid="YOUR_GENERATED_PROTEIN_UUID",
n=10000,
sample_seed=42,
)
print(binders.shape, nonbinders.shape)
Idempotency
- Every non-GET call gets an idempotency key automatically.
- Auto-generated keys are per-call convenience and are not retry-stable.
- For retry dedupe, pass and reuse your own
idempotency_key(logical operation ID).
Example (retry-safe launch):
request_key = "search-protein-x-20260303-001"
job = client.diligence.search(
query="MKNK2 inhibitor landscape",
idempotency_key=request_key,
)
# If you retry the same logical launch, reuse the same idempotency key.
# retried = client.diligence.search(query="MKNK2 inhibitor landscape", idempotency_key=request_key)
Wallet Funding
Use client.wallet.topup(...) to explicitly fund Wallet Credits by saved card
or invoice. Saved-card top-ups are capped at $500 per request; invoice
top-ups can be larger. Wallet funding requires an explicit retry-stable
idempotency_key and exact user approval text.
confirmation = client.wallet.expected_topup_confirmation(
amount_cents=50_000,
payment_mode="saved_card",
)
topup = client.wallet.topup(
amount_cents=50_000,
payment_mode="saved_card",
user_approval_confirmation=confirmation,
idempotency_key="wallet-topup-target-x-001",
)
Helper Surface
diligence.deep_diligence(query, preset=None, idempotency_key=None)diligence.synthesize_report(gene_key, idempotency_key=None)diligence.search(query, idempotency_key=None)diligence.gather(query, preset=None, idempotency_key=None)diligence.crawl(url, preset=None, idempotency_key=None)diligence.list_gene_keys()artifacts.upload(file_path, content_type=None),artifacts.upload_bytes(...),artifacts.get(artifact_id)hub.submit(job_type, payload, idempotency_key=None)for the full active public Hub route set- selected typed
hub.<model>(...)helpers forboltz2,boltzgen,rosettafold3,chai1,rfd3,bindcraft,alphafold,proteinttt,diffdock,flowdock,neuralplexer,openfold3,lula1,protein_specific_models lula1.score(protein_sequence, smiles, idempotency_key=None, include_metadata=False)lula2.score(protein_sequence, smiles, idempotency_key=None, include_metadata=False)- optional local LULA-1 open-weight helpers through
omtx.lula.load_model(...)after installingomtx[lula] - optional CLI commands installed by the
omtxpackage:omtx lula download,omtx lula verify, andomtx lula score data_generation.quota_order(sequences, ...)data_generation.invoice_order(sequences, ...)molecules.pricing()molecules.search(smiles_list, ...)molecules.quote(items, ...)molecules.shipping_addresses()molecules.order(items, shipping_address_id, idempotency_key)molecules.orders(limit=20)molecules.status(order_number)models.catalog(protein_uuid=None, q=None, limit=100, offset=0)structures.pdb_download(pdb_id, format="pdb")structures.pdb_save(pdb_id, output_dir=".", format="pdb", overwrite=False)wallet.expected_topup_confirmation(amount_cents, payment_mode)wallet.topup(amount_cents, payment_mode, user_approval_confirmation, idempotency_key)vibe_video.submit(music_prompt, visual_prompt=None, artist_name=None, title_hint=None, duration_seconds=890, credit_artist=True, idempotency_key=None)vibe_video.list(limit=20)vibe_video.status(submission_id)jobs.history(...),jobs.status(job_id),jobs.wait(job_id, ...)binders.get_shards(...)binders.urls(...)load_binders(...)load_nonbinders(...)load_data(...)(combined binder/non-binder load)datasets.catalog()datasets.generated_protein_uuids()status()users.profile()
Visualization column contract:
OmData.show(...)is strict (no column fallback aliases).- Default columns are
smilesandbinding_score. - For selectivity views, pass
sort_by="selectivity_score".
Route policy:
/v2/diligence/getTargetDiligenceReportremains an alias route and is not a separate SDK helper./v2/rag/searchis intentionally not exposed in the SDK.- Public Hub coverage follows the active public model set in the canonical gateway route inventory.
hub.submit(...)covers the full active public model set.- Typed helpers are the selected subset listed above.
- Molecule fulfillment helpers call only the canonical
/v2/molecules/*Gateway routes. Public molecule orders are invoice-only. models.catalog(...)calls/v2/models/catalogfor published protein-specific model discovery.structures.pdb_download(...)fetches exact public structure files fromhttps://files.rcsb.org/download/{PDB_ID}.{format}. It accepts only canonicalpdborcifformats.- Data-generation order helpers call only the canonical
/v2/data-generation/*Gateway routes. Public extra orders are invoice-only; quota orders consume active subscription capacity. - Vibe video helpers call only the canonical
/v2/vibe-video/*Gateway routes.
Hub and Artifacts
artifact = client.artifacts.upload("target.pdb")
job = client.hub.diffdock(
protein_artifact_id=artifact["artifact_id"],
ligand_smiles="CCO",
idempotency_key="diffdock-demo-20260316",
)
status = client.jobs.wait(job["job_id"], poll_interval=5, timeout=1800)
history = client.jobs.history(limit=20)
Notes:
- Artifact-backed Hub workflows upload via
client.artifacts.*first, then pass artifact IDs into canonicalclient.hub.*request fields. client.hub.submit(...)is the generic escape hatch for active Hub models using canonicaljob_type="hub.<model>".- Active public models without a dedicated helper, such as
neuralplexer, are launched throughclient.hub.submit(...). jobs.history(...)returns recent user jobs newest-first and paginates withlimitandcursor.
Migration
Breaking changes in 2.0.0:
OMTXClientremoved.OmClientis now the only supported client class.- Legacy pricing helpers removed from SDK surface.
- Legacy binder batch-cost helper removed from SDK surface.
- Shard access now resolves latest accessible dataset by
protein_uuid. - Generated data access is granted by qualifying covered data-generation
sequences for the matching
protein_uuid, including legacy-public snapshots where the account has covered access. client.status()is the primary health helper.load_data(...)is the primary training-set helper; it samples target binders and background negatives from binder pools.load_binders(...)remains the primary binder loader;load_nonbinders(...)remains available for legacy target non-binders and canonical NEGATIVE-control derived pools.- Flat shard URL aliases are available as
binder_urls/non_binder_urls. - Core SDK runtime includes
polars+rdkit.
Migration mapping (1.x -> 2.x):
from omtx import OMTXClient->from omtx import OmClientOMTXClient(...)->OmClient(...)
Breaking changes in 1.0.0:
binders.get(...)removed from core SDK.binders.iter(...)removed from core SDK.pandasremoved from required dependencies.
Migration mapping (0.x -> 1.x):
binders.get(...)->client.load_binders(...)/client.load_nonbinders(...)orbinders.get_shards(...)binders.iter(...)->binders.get_shards(...)+ application-level streamingpip install omtx(with pandas) ->pip install omtx(with polars + rdkit)
Full details: see MIGRATION.md.
Requirements
- Python
>=3.9 - OMTX API key
License
MIT. See LICENSE.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file omtx-2.0.16.tar.gz.
File metadata
- Download URL: omtx-2.0.16.tar.gz
- Upload date:
- Size: 75.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/7.0.0 CPython/3.10.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6226194cb4df8c2f3274ffc6dc4425a8b6237e911a56ea54f8ad46fa35d3dbbe
|
|
| MD5 |
0722a7776b4e84cff9243d9575f76fef
|
|
| BLAKE2b-256 |
67066844dda236cc92e7eac48c94a8e0c25e57450e6b2727d128f60599e5ab0d
|
File details
Details for the file omtx-2.0.16-py3-none-any.whl.
File metadata
- Download URL: omtx-2.0.16-py3-none-any.whl
- Upload date:
- Size: 54.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/7.0.0 CPython/3.10.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
06dddb052d59a77a28e6a09a3ad05b08eb697f121ffa11a580f4c7ef6b392c59
|
|
| MD5 |
8f0e3d8193c3d23030bddecb95cd2fb8
|
|
| BLAKE2b-256 |
b038f8a1ab0365f2a275e926535b7075630fa02638bc3e32044d2741d3453774
|