Skip to main content

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 OmData loaders
  • Private data-generation order helpers for subscription quota and invoice-backed orders
  • OnePot-backed molecule availability, quote, checkout, and order helpers
  • Public RCSB PDB/mmCIF structure download helpers
  • Health and Wallet Credits helpers

Public docs: https://docs.omtx.ai

Installation

pip install omtx

Compatibility

For best compatibility:

  • Linux: modern Ubuntu on x86_64 or arm64
  • macOS: Apple Silicon with a native arm64 Python 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.11"
omtx lula download
omtx lula verify

Model card, weights, license, and notebook: huggingface.co/omtx/lula-1. Quickstart notebook: lula1_quickstart.ipynb. LULA-1 is public ungated on Hugging Face. By downloading, accessing, or using LULA-1, you agree to the Om LULA Community License 1.0. Commercial hosted inference, paid API/SaaS access, resale, paid support/deployment, bundling model access into a paid product, and competing model services require a separate Om commercial license.

No Hugging Face login is required for the public LULA-1 weights. In Colab, omtx>=2.0.11 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()
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 omtx import OmClient

with OmClient() as client:
    scores = client.lula2.score(
        protein_sequence="MEEPQSDPSV",
        smiles=["CC(=O)Oc1ccccc1C(=O)O"],
    )
    selected = [row["smiles"] for row in scores]

    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"]
    invoice = client.molecules.checkout(
        items=[{"smiles": smiles, "quantity": 1} for smiles in selected],
        shipping_address_id=shipping_address_id,
    )

The molecule namespace calls the canonical Om API /v2/molecules/* Gateway routes, including /v2/molecules/fulfillment/shipping-addresses for saved shipping address reads. routes. Provider matching, quote validation, invoice creation, and order state are owned by the Gateway and Wallet services.

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 for boltz2, 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 installing omtx[lula]
  • optional CLI commands installed by the omtx package: omtx lula download, omtx lula verify, and omtx 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.checkout(items, shipping_address_id, ...)
  • 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 smiles and binding_score.
  • For selectivity views, pass sort_by="selectivity_score".

Route policy:

  • /v2/diligence/getTargetDiligenceReport remains an alias route and is not a separate SDK helper.
  • /v2/rag/search is 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/catalog for published protein-specific model discovery.
  • structures.pdb_download(...) fetches exact public structure files from https://files.rcsb.org/download/{PDB_ID}.{format}. It accepts only canonical pdb or cif formats.
  • 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 canonical client.hub.* request fields.
  • client.hub.submit(...) is the generic escape hatch for active Hub models using canonical job_type="hub.<model>".
  • Active public models without a dedicated helper, such as neuralplexer, are launched through client.hub.submit(...).
  • jobs.history(...) returns recent user jobs newest-first and paginates with limit and cursor.

Migration

Breaking changes in 2.0.0:

  • OMTXClient removed.
  • OmClient is 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 OmClient
  • OMTXClient(...) -> OmClient(...)

Breaking changes in 1.0.0:

  • binders.get(...) removed from core SDK.
  • binders.iter(...) removed from core SDK.
  • pandas removed from required dependencies.

Migration mapping (0.x -> 1.x):

  • binders.get(...) -> client.load_binders(...) / client.load_nonbinders(...) or binders.get_shards(...)
  • binders.iter(...) -> binders.get_shards(...) + application-level streaming
  • pip 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

omtx-2.0.11.tar.gz (73.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

omtx-2.0.11-py3-none-any.whl (52.3 kB view details)

Uploaded Python 3

File details

Details for the file omtx-2.0.11.tar.gz.

File metadata

  • Download URL: omtx-2.0.11.tar.gz
  • Upload date:
  • Size: 73.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.10.14

File hashes

Hashes for omtx-2.0.11.tar.gz
Algorithm Hash digest
SHA256 5fd536dbcaede221b58f40c3ac0efabb7af7fbce876543bc5dd87eb13b5374c2
MD5 266250819d8ca0a6b6c5daad4ab98781
BLAKE2b-256 69538e652b45a3fa3eb8391cdfe39d3be808435c9bcd2204821667ac985abefc

See more details on using hashes here.

File details

Details for the file omtx-2.0.11-py3-none-any.whl.

File metadata

  • Download URL: omtx-2.0.11-py3-none-any.whl
  • Upload date:
  • Size: 52.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.10.14

File hashes

Hashes for omtx-2.0.11-py3-none-any.whl
Algorithm Hash digest
SHA256 cda0b16bb9f08b9114d196c26967cc70617a4b6589951541ba6c1767f6278ab5
MD5 6ce083d6e2a0016e47ef17afd70efe39
BLAKE2b-256 4faf69c8a4cb91b553f5cf127fbdc87bcd93a5a1f22e540ab41c70e9da7b9402

See more details on using hashes here.

Release history Release notifications | RSS feed

2.0.23

2 files

2.0.22

2 files

2.0.21

2 files

2.0.20

2 files

2.0.19

2 files

2.0.18

2 files

2.0.17

2 files

2.0.16

2 files

2.0.15

2 files

2.0.14

2 files

2.0.13

2 files

2.0.12

2 files

This release

2.0.11 This release

2 files

2.0.10

2 files

2.0.9

2 files

2.0.8

2 files

2.0.7

2 files

2.0.6

2 files

2.0.5

2 files

2.0.4

2 files

2.0.2

2 files

2.0.1

2 files

2.0.0

2 files

0.4.2

2 files

0.4.1

2 files

0.4.0

2 files

0.3.4

2 files

0.3.3

2 files

0.3.2

2 files

0.3.1

2 files

0.3.0

2 files

0.1.0

2 files

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page