Skip to content

Examples

One worked example per capability, in curl and Python. Parameter detail lives on the endpoint pages: Predictions, Designs, Embeddings.

Fold a protein

BASE=https://api.japanfold.aiand.com
KEY=your_key_here    # from japanfold.aiand.com/account/token
H=(-H "Authorization: Bearer $KEY")

# Name the submission. A retry carrying the same key returns the job the first
# call made, so a timeout on a busy fleet never costs you the fold twice. Bump
# the name for a fresh run: a key stays bound to its first job for good.
RUN=myprotein-$(date +%s)

JOB=$(curl -s "${H[@]}" -X POST $BASE/v1/predictions \
  -H 'Content-Type: application/json' -H "Idempotency-Key: $RUN" \
  -d '{"model":"boltz2","name":"myprotein","sequence":"MKTAYIAKQRQISFVKSHFSRQLEERLGLIEVQ"}' \
  | python3 -c 'import sys,json; r=json.load(sys.stdin); print(r["id"]) if "id" in r else sys.exit(r["detail"])')

# poll (Prefer: wait blocks up to 60s per call)
until curl -s "${H[@]}" -H 'Prefer: wait=60' $BASE/v1/jobs/$JOB \
  | grep -qE '"status":"(succeeded|failed|canceled)"'; do sleep 2; done

curl -s "${H[@]}" $BASE/v1/jobs/$JOB/results
curl -s "${H[@]}" $BASE/v1/jobs/$JOB/archive -o myprotein.zip && unzip -oq myprotein.zip -d myprotein

The same thing in Python, stdlib only:

import json, os, time, urllib.request

BASE = "https://api.japanfold.aiand.com"
KEY = os.environ["JAPANFOLD_API_KEY"]  # from japanfold.aiand.com/account/token
HEADERS = {"Content-Type": "application/json",
           "Authorization": f"Bearer {KEY}"}

def api(method, path, body=None, idem=None):
    data = json.dumps(body).encode() if body is not None else None
    headers = {**HEADERS, **({"Idempotency-Key": idem} if idem else {})}
    req = urllib.request.Request(BASE + path, data=data, method=method, headers=headers)
    with urllib.request.urlopen(req) as r:
        return json.load(r)

def wait(job_id):
    while True:
        job = api("GET", f"/v1/jobs/{job_id}")
        if job["status"] in ("succeeded", "failed", "canceled"):
            return job
        time.sleep(5)

# The same `idem` on a retry returns the job the first call made rather than
# launching a second one you also pay for. Name it after the run and bump the
# name for a fresh one; a random key per call would defeat the point.
job = api("POST", "/v1/predictions",
          {"model": "boltz2", "name": "myprotein",
           "sequence": "MKTAYIAKQRQISFVKSHFSRQLEERLGLIEVQ"},
          idem="myprotein-1")
job = wait(job["id"])
assert job["status"] == "succeeded", job.get("error")

results = api("GET", f"/v1/jobs/{job['id']}/results")
for row in results["rows"]:
    # Boltz-2 reports confidence_score and complex_plddt; ESMFold-2 reports plddt.
    print(row["id"], "confidence=", row.get("confidence_score"),
          "plddt=", row.get("complex_plddt") or row.get("plddt"))

# urlretrieve takes no headers, so open the archive directly
req = urllib.request.Request(BASE + results["archive_url"], headers=HEADERS)
with urllib.request.urlopen(req) as r, open("myprotein.zip", "wb") as f:
    f.write(r.read())

Co-fold a protein + ligand, with affinity

Only Boltz-2 does affinity. Pass the complex as a Boltz YAML input with a ligand chain and a properties: affinity block naming the binder.

import os, time, httpx

BASE = "https://api.japanfold.aiand.com"
AUTH = {"Authorization": f"Bearer {os.environ['JAPANFOLD_API_KEY']}"}
YAML = (
    "sequences:\n"
    "  - protein: {id: A, sequence: MKTAYIAKQRQISFVKSHFSRQLEERLGLIEVQ}\n"
    "  - ligand: {id: L, smiles: \"CC(=O)Oc1ccccc1C(=O)O\"}\n"
    "properties:\n"
    "  - affinity: {binder: L}\n"
)

with httpx.Client(base_url=BASE, headers=AUTH, timeout=180, follow_redirects=True) as c:
    job = c.post("/v1/predictions", headers={"Idempotency-Key": "prot-ligand-1"}, json={
        "model": "boltz2", "name": "prot-ligand",
        "input": YAML, "params": {"use_msa_server": True}}).json()
    while job["status"] not in ("succeeded", "failed", "canceled"):
        time.sleep(5)
        job = c.get(f"/v1/jobs/{job['id']}").json()
    print(c.get(f"/v1/jobs/{job['id']}/results").json()["rows"])

The result row carries affinity_pred_value, predicted log10(IC50) in μM where lower binds harder, and affinity_probability_binary, P(binder) from 0 to 1. See Predictions.

Design binders (BoltzGen)

POST /v1/designs with a protocol and a YAML spec. Results carry ranked designs.

import os, time, httpx

BASE = "https://api.japanfold.aiand.com"
AUTH = {"Authorization": f"Bearer {os.environ['JAPANFOLD_API_KEY']}"}

with httpx.Client(base_url=BASE, headers=AUTH, timeout=300, follow_redirects=True) as c:
    job = c.post("/v1/designs", headers={"Idempotency-Key": "my-nanobodies-1"}, json={
        "protocol": "nanobody-anything", "name": "my-nanobodies",
        "spec": "entities:\n  - protein: {id: B, sequence: 80..120}\n"
                "  - protein: {id: A, msa: empty, sequence: MKTAYIAKQRQISFVKSHFSRQLEERLGLIEVQ}\n",
        "params": {"num_designs": 10, "budget": 10, "fast": True}}).json()
    while job["status"] not in ("succeeded", "failed", "canceled"):
        time.sleep(10)
        job = c.get(f"/v1/jobs/{job['id']}").json()
    results = c.get(f"/v1/jobs/{job['id']}/results").json()
    print(results.get("designs"))
    with open("designs.zip", "wb") as f:
        f.write(c.get(results["archive_url"]).content)

Design against a structure (RFdiffusion3)

Same endpoint, but the input is a pasted structure plus a contig saying what stays fixed and what gets designed. Results are unranked mmCIFs.

import os, time, httpx

BASE = "https://api.japanfold.aiand.com"
AUTH = {"Authorization": f"Bearer {os.environ['JAPANFOLD_API_KEY']}"}
structure = open("target.pdb").read()  # chain A holds the target

with httpx.Client(base_url=BASE, headers=AUTH, timeout=300, follow_redirects=True) as c:
    job = c.post("/v1/designs", headers={"Idempotency-Key": "my-rfd3-binders-1"}, json={
        "protocol": "rfd3-binder", "name": "my-rfd3-binders",
        "structure": structure,
        "contig": "A1-150,60-80",  # keep target residues 1-150, design a 60-80 aa binder
        "params": {"num_designs": 4, "num_timesteps": 100}}).json()
    while job["status"] not in ("succeeded", "failed", "canceled"):
        time.sleep(10)
        job = c.get(f"/v1/jobs/{job['id']}").json()
    results = c.get(f"/v1/jobs/{job['id']}/results").json()
    print([d["id"] for d in results.get("designs", [])])
    with open("designs.zip", "wb") as f:
        f.write(c.get(results["archive_url"]).content)

Embed sequences

See Embeddings for the ESMC/SaProt example.