Python SDK

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

Install

Install or update the omtx CLI and SDKBash
curl -fsSL https://omtx.ai/install.sh | sh
Pin a release (ZenCloud needs omtx 3.0.0 or later)Bash
curl -fsSL https://omtx.ai/install.sh | OMTX_VERSION=3.0.0 sh
  • The one-line installer needs no administrator rights. It installs omtx with its own Python, so it works on a machine without Python.
  • Run the same command again to update.
  • Use pip when you want the SDK inside an existing Python project or notebook environment.
  • Check the install with omtx --help.
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

  • ZenCloud start/run require an explicit stable idempotency_key. Persist the original key and inputs for each intended operation. Diligence helpers also accept idempotency_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.zencloud.describe() returns scientific image contracts and reservation prices. New Hub launches are retired; use client.jobs for historical results.
  • For saved-card funding, client.wallet.prepare_topup(...) prepares without charging. After fresh approval, pass its issued ID and exact confirmation to client.wallet.topup(...). Retain original arguments and keys for unknown-outcome recovery. Invoice funding retains invoice approval and settlement.
  • Upload files with client.artifacts.upload(...) before reserving ZenCloud time for images 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.

ZenCloud and historical Hub results

Inspect scientific image contracts before reserving timePython
catalog = client.zencloud.describe()
print(catalog)
# Review the selected image's inputs and price; stage artifacts first.
# Use client.zencloud.start/run with an explicit key per operation.
Read an existing Hub resultPython
status = client.jobs.status("existing-job-id")
print(status["status"])
# Select an artifact name from this job's results.
url_info = client.jobs.get_artifact_url("existing-job-id", "outputs/results.json")
print(url_info["download_url"])