Ranomics
Pipetting in a wet lab
Platform API / Quickstart

Quickstart: submit a yeast display experiment

Six API calls from candidates in hand to enriched hits in JSON. The full sequence, in Python.

Prerequisites

Before you start

  • A Ranomics tools account (the login flow on tools.ranomics.com handles signup).
  • A Bearer token (rk_live_...) minted at tools.ranomics.com/account/api-keys.
  • A list of candidate amino acid sequences from your design pipeline, plus a target antigen (sequence or PDB).
Step 1

Configure the client

No SDK is required. We use the standard requests library in this walkthrough.

import os
import requests

API = "https://tools.ranomics.com/api/v1"
TOKEN = os.environ["RANOMICS_API_KEY"]
HEADERS = {"Authorization": f"Bearer {TOKEN}"}
Step 2

Submit the experiment

A minimal yeast display submission needs a target, a library design, and a sequences dictionary. The response includes the experiment_id you will use for every subsequent call.

spec = {
    "name": "her2-mpnn-pool-r1",
    "webhook_url": "https://my-agent.example/hooks/ranomics",
    "experiment_spec": {
        "experiment_type": "yeast_display",
        "target": {"custom": {
            "name": "HER2 ECD",
            "antigen_sequence": her2_ecd_aa,
        }},
        "library_design": {
            "mode": "designed_panel",
            "diversity_estimate": len(candidates),
        },
        "sequences": candidates,  # {"des_0001": "MASR...", ...}
    },
}

r = requests.post(f"{API}/experiments", headers=HEADERS, json=spec)
exp = r.json()
exp_id = exp["experiment_id"]
print(exp["status"])  # "Draft"
Step 3

Get a cost estimate

Non-binding ballpark, derived from the spec alone. Calibrated targets return a concrete number; custom targets return a placeholder range and route to a human-scoped quote.

r = requests.post(
    f"{API}/experiments/cost-estimate",
    headers=HEADERS,
    json={
        "experiment_type": "yeast_display",
        "candidate_count": len(candidates),
        "library_diversity": len(candidates),
        "target_kind": "custom",
    },
)
print(r.json())
# {"requires_human_quote": true,
#  "estimated_range_usd": [12000, 28000],
#  "scoping_url": "..."}
Step 4

Retrieve and confirm the quote

Wait for status to advance past QuoteSent, then pull the quote, review the line items, and confirm. Confirmation moves the experiment to WaitingForMaterials and triggers library construction.

quote = requests.get(
    f"{API}/experiments/{exp_id}/quote",
    headers=HEADERS,
).json()
print(quote["total_usd"], quote["line_items"])

# When the budget owner approves:
requests.post(
    f"{API}/quotes/{quote['quote_id']}/confirm",
    headers=HEADERS,
)
Step 5

Poll for status or subscribe via webhook

Either pattern works. Polling is cheap because GET /experiments/{id} returns only the status fields, not the result payload. If you passed a webhook_url at submission, you also get a signed POST on every status transition.

import time

while True:
    state = requests.get(
        f"{API}/experiments/{exp_id}",
        headers=HEADERS,
    ).json()
    print(state["status"], state["results_status"])
    if state["results_status"] in ("partial", "all"):
        break
    time.sleep(300)  # 5 min between polls is plenty
Step 6

Fetch the enrichment results

Per-sequence enrichment, called-hit flag, percentile within the experiment, and signed download URLs for the raw NGS reads, hits FASTA, and enrichment table CSV.

results = requests.get(
    f"{API}/experiments/{exp_id}/results",
    headers=HEADERS,
).json()

hits = [s for s in results["sequences"] if s["called_hit"]]
print(f"{len(hits)} called hits from {len(results['sequences'])} candidates")

# Ranked top-K for handoff to BLI / SPR
top = sorted(
    results["sequences"],
    key=lambda s: s["log2_enrichment"],
    reverse=True,
)[:100]