Python SDK

Use the official `omtx` package to score Om Accessible Space with LULA, order selected molecules with Wallet Credits, submit diligence and Hub jobs, upload artifacts, request signed artifact URLs, and load account-accessible Generated Data from Python.

Install

pipBash
pip install omtx
Quick startPython
from omtx import OmClient

client = OmClient(api_key="YOUR_API_KEY")

profile = client.users.profile()
print("Available Wallet Credits:", profile["available_credits"])

health = client.status()
print("API version:", health["version"])

models = client.models.catalog(limit=5)
print("Model count:", models["count"])

catalog = client.datasets.catalog()
print("Generated Data rows:", catalog["data_generated"]["count"])

gene_keys = client.diligence.list_gene_keys()
print("Sample gene keys:", [item["gene_key"] for item in gene_keys["items"][:5]])

Data access helpers

Combined loading (recommended for training sets)Python
loaded = client.load_data(
    protein_uuid="YOUR_GENERATED_PROTEIN_UUID",
    binders=50000,
    nonbinder_multiplier=5,  # default
    # nonbinders=200000,      # optional explicit override
    sample_seed=42,
)
binders = loaded["binders"]
nonbinders = loaded["nonbinders"]
print("Rows loaded:", len(binders), len(nonbinders))
binders.show(top_n=24)  # defaults: smiles + binding_score
Separate pool loading (explicit control)Python
binders = client.load_binders(
    protein_uuid="YOUR_GENERATED_PROTEIN_UUID",
    n=1000,
    sample_seed=42,
)
nonbinders = client.load_nonbinders(
    protein_uuid="YOUR_GENERATED_PROTEIN_UUID",
    n=10000,
    sample_seed=42,
)
# Omit n (or set n=None) to load the full pool.
print("Rows loaded:", len(binders), len(nonbinders))
binders.show(top_n=24)  # defaults: smiles + binding_score
Manual shard export (advanced)Python
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"]))

Data Generation orders

Create a Wallet Credits-funded orderPython
import requests

sequences = [{"name": "target", "sequence": "M" * 120}]

response = requests.post(
    "https://api.omtx.ai/v2/data-generation/orders",
    headers={
        "x-api-key": "YOUR_API_KEY",
        "Idempotency-Key": "dg-wallet-target-001",
        "Content-Type": "application/json",
    },
    json={"sequences": sequences},
    timeout=60,
)
response.raise_for_status()
order = response.json()

print(order["order_number"])
print(order["total_amount_cents"])

Molecule Fulfillment

Score Om Accessible Space, then order selected hitsPython
from pathlib import Path
from uuid import uuid4

import polars as pl
from omtx import OmClient

with OmClient(api_key="YOUR_API_KEY") as client:
    job = client.lula2.score(
        protein_sequence="YOUR_JAK2_V617F_PROTEIN_SEQUENCE",
        source="om",
        tier=50,
        n=50_000,
        top_k=10_000,
        idempotency_key="jak2-v617f-lula2-r1",
    )

    artifact_paths = []
    result_dir = Path("outputs/jak2-v617f-lula2-r1")
    for job_id in job["job_ids"]:
        client.jobs.wait(job_id, poll_interval=5, timeout=3600)
        artifact_paths.extend(
            client.jobs.download_all_artifacts(
                job_id,
                output_dir=result_dir / job_id,
                overwrite=True,
            )
        )

    score_tables = [
        pl.read_parquet(path)
        for path in artifact_paths
        if path.name == "top_hits.parquet"
    ]
    score_rows = pl.concat(score_tables).sort("score", descending=True)
    selected_hits = score_rows.head(100).to_dicts()

    addresses = client.molecules.shipping_addresses()
    order = client.molecules.order(
        items=selected_hits,
        shipping_address_id=addresses["default_shipping_address_id"],
        idempotency_key=f"jak2-v617f-round-1-{uuid4()}",
    )

print(len(selected_hits), order["order_number"])
Score Om Accessible Space locally with open-weight LULAPython
from uuid import uuid4

from omtx import OmClient
from omtx.lula import load_model

with OmClient(api_key="YOUR_API_KEY") as client:
    model = load_model("lula1.1")
    scores = model.score(
        protein_sequence="YOUR_PROTEIN_SEQUENCE",
        source="om",
        tier=50,
        n=50_000,
        client=client,
    )

    selected_hits = scores[:96]
    addresses = client.molecules.shipping_addresses()
    order = client.molecules.order(
        items=selected_hits,
        shipping_address_id=addresses["default_shipping_address_id"],
        idempotency_key=f"local-lula-order-{uuid4()}",
    )

print(len(selected_hits), order["order_number"])
Search, quote, and order arbitrary submitted SMILESPython
from omtx import OmClient

with OmClient(api_key="YOUR_API_KEY") as client:
    pricing = client.molecules.pricing()
    hits = client.molecules.search(
        smiles_list=["CC(=O)Oc1ccccc1C(=O)O"],
        max_results=5,
    )
    quote = client.molecules.quote(
        items=[{"smiles": "CC(=O)Oc1ccccc1C(=O)O", "quantity": 1}],
    )
    order = client.molecules.order(
        items=[{"smiles": "CC(=O)Oc1ccccc1C(=O)O", "quantity": 1}],
        shipping_address_id="addr_123",
        idempotency_key="molecule-wallet-order-001",
    )

print(pricing["provider"], quote["total_amount_cents"], order["order_number"])

Diligence jobs

Submit and waitPython
job = client.diligence.deep_diligence(
    query="BRAF clinical inhibitor landscape",
    preset="quick",
)

result = client.jobs.wait(
    job_id=job["job_id"],
    result_endpoint="/v2/jobs/deep-diligence/{job_id}",
    poll_interval=5,
    timeout=1800,
)

print("Claims:", result["result"]["total_claims"])

SDK Notes

  • Idempotency keys are generated automatically for POST and PUT calls. Diligence helpers also accept idempotency_key= when you want to reuse a specific key.
  • Use client.jobs.wait() for asynchronous calls that return job_id or job_ids.
  • Diligence wrappers include search, gather, and crawl in addition to deep_diligence/synthesize_report.
  • client.hub.submit(...) lets you start broad public Hub models from the SDK; use LULA and Om foundation-model workflows for model execution, and client.datasets.catalog() for account-accessible Generated Data.
  • client.wallet.topup(...) explicitly funds Wallet Credits by saved card or invoice; saved-card top-ups use the account's funding limit and require exact approval text plus a retry-stable idempotency_key.
  • Upload files with client.artifacts.upload(...) before starting Hub workflows that use uploaded structures.
  • For larger uploaded files, use client.artifacts.upload_via_signed_url(...).
  • For large result files, use client.jobs.get_artifact_url(...) instead of inlining artifact bytes.
  • Wallet Credits fund explicit Generated Data orders through /v2/data-generation/orders.
  • client.lula1.score(...) and client.lula2.score(...) submit async hosted scoring jobs for explicit SMILES or Om Accessible Space tiers; pass source="om", tier, and n for the current public Om-space path, then wait on job["job_ids"] and download completed artifacts for ranked rows.
  • Open-weight local LULA can score explicit SMILES without Om, but local scoring against source="om" requires an authenticated OmClient so the SDK can fetch orderable Om rows without sending your protein sequence to Om.
  • client.molecules.shipping_addresses() returns addresses, count, and default_shipping_address_id; order creation requires a saved shipping address id.
  • client.molecules.* wraps Molecule Fulfillment pricing, search, quote, shipping addresses, order history, and order status. Wallet-funded order creation uses /v2/molecules/fulfillment/orders; selected Om Accessible Space score rows carry their result source_metadata into order items.
  • Use client.jobs.history(limit=...) and cursor to page through recent jobs chronologically.
  • client.status() is the primary health helper.
  • load_data(...) loads binders and non-binders in one call (binders required, non-binders default to 5x multiplier).
  • load_binders(...) and load_nonbinders(...) are the primary dataframe loaders for separate training pools.
  • If n is omitted (or n=None), loaders pull the full pool; sampling occurs only when n is set.
  • OmData.show(...) uses smiles and binding_score by default.
  • For selectivity ranking, pass sort_by="selectivity_score".
  • OmData.show(...) displays inline in notebooks and returns None after successful display to avoid duplicate rendering.
  • binders.urls(...) returns flat binder_urls / non_binder_urls lists for quick iteration.
  • Use the client as a context manager to close sessions automatically.

Hub jobs and artifacts

Hub launch from uploaded structurePython
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)
print(status["job_type"], status["status"])
Large artifact flow via signed URLsPython
artifact = client.artifacts.upload_via_signed_url("target.cif")

job = client.hub.rfd3(
    pdb_artifact_id=artifact["artifact_id"],
    design_mode="monomer",
    contig="120",
    idempotency_key="rfd3-demo-20260331",
)

url_info = client.jobs.get_artifact_url(
    job["job_id"],
    "outputs/results.json",
)
print(url_info["download_url"])