Jobs¶
Predictions, designs and embeddings all create a job. This is how you track
it, read its output, and clean it up. Every call sends the key of the account
that submitted the job (see Authentication); another
account's job id answers 404.
The Job object¶
{
"object": "job",
"id": "9daefed38ec5b505b80f76f9a9e87323",
"kind": "predict", // "predict" | "design" | "embed"
"status": "running", // queued | running | succeeded | failed | canceled
"name": "myprotein",
"model": "boltz2", // null for designs
"protocol": null, // set for designs
"progress": 0.5, // 0..1, or null when indeterminate
"stage": "trunk", // human-readable current step
"done": 0, "total": 1, // sub-units completed / total
"params": {}, // the params the job was submitted with
"waiting_for_device": true, // present only while nothing is free to run it
"queue": { "reason": "all_chips_busy",
"accounts_ahead": 3,
"yours": { "position": 1, "of": 1 },
"fleet": { "chips": 128, "busy": 127, "out_of_service": 1,
"accounts_waiting": 41 } },
"attempt": 2, // present only on a retry: the box running it stopped answering
"error": null, // why it failed, or what a succeeded job could not deliver
"partial": { "unit": "designs", "requested": 12, "delivered": 10,
"shards_failed": 1, "shards": 6 }, // present only on a short delivery
"created_at": "2026-09-02T20:12:08Z",
"started_at": "...",
"finished_at": null,
"results_ready": false,
"links": { "self": "...", "results": "...", "archive": "...", "logs": "...", "usage": "..." }
}
links.usage is what the job was charged, once it has finished: the billed seconds and
amount in dollars (amount_micros in millionths of one), the same record GET /v1/usage lists. It answers 404 while the job
is still queued or running, and answers as soon as the job has ended. A job canceled before it
started, or one that failed, costs 0.
A job can succeed with less than it was asked for. A design runs as several shards, and a
prediction of several structures folds each on its own. When some of that work fails, the job
still ends succeeded with everything that did finish, and it carries partial: what you asked
for, what you got, and for a design how many shards failed. error says the same in a sentence,
with the first failure's reason. The results directory is named for the designs it holds
(final_10_designs/). Only the work that finished is billed. Submit again for the rest.
When a structure runs again¶
If the box folding a structure stops answering (it hung, or its agent died), the structure runs
again from the start on another box, and its progress starts over. The job then carries
"attempt": 2 (a job of several structures carries it on that structure's entry in targets).
A box that is restarted on purpose hands its structures back first; they start over too, without
counting as an attempt. A retry is not billed twice: you pay for the run that produced the
result. A structure that loses a second box this way fails with a message saying so, and nothing
is charged for it.
While a job waits¶
queue is present while any part of the job waits for a chip. It says why and
who is ahead, never how long: how long the work ahead takes is not known until it
ends.
| field | meaning |
|---|---|
reason |
all_chips_busy; no_galaxy_online, nothing is serving right now and the job starts when something is; or account_backlog, your own earlier jobs run first |
accounts_ahead |
accounts the next free chip goes to before yours |
yours |
this job's place among your own waiting jobs, which run in the order you sent them |
fleet |
chips online, busy and out of service, and how many accounts have work waiting |
The next free chip goes to the account holding the fewest chips, so a new
account's first job starts on the next chip that frees however long the queue
is. How that is decided and audited is in
Models and limits. GET /v1/cluster answers the same
fleet counts without a job.
A queued job and a running one are different states: a running job reports its
stage, done and total instead.
Email when it ends¶
A queue can be longer than you want to watch. Signed in, you can ask for one email when the job succeeds or fails, sent to your account's address:
curl -s -H "Authorization: Bearer $JAPANFOLD_API_KEY" -X POST https://api.japanfold.aiand.com/v1/jobs/$JOB/notify -d '{"lang": "en"}' # or "ja"
The job then carries "email_when_done": true. An account without an email
address gets 409, and so does a job that has already ended. A cancelled job
sends nothing. The workbench offers this once a job has waited a minute.
Wait for one job¶
returns the job's state now. Add ?wait=<seconds> (at most 60) and the request
is held until the job finishes or the time is up, whichever comes first. The
body is the same job either way, so a loop on it needs no sleep: each call
returns the moment the job is done.
while :; do
S=$(curl -s -H "Authorization: Bearer $JAPANFOLD_API_KEY" "https://api.japanfold.aiand.com/v1/jobs/$JOB?wait=60" \
| python3 -c 'import sys,json; print(json.load(sys.stdin).get("status"))')
case $S in queued|running) ;; *) break ;; esac
done
echo "$S"
The CLI does the same with japanfold wait $JOB. The header Prefer: wait=60 is
equivalent and also works on a create (see
Predictions). The hold is
a preference: when the service is already holding many requests it answers at once
with Retry-After, and Preference-Applied: wait on a response says the hold
happened.
List your jobs¶
GET /v1/jobs lists every job on the account, whichever key or browser
submitted it.
curl -s -H "Authorization: Bearer $JAPANFOLD_API_KEY" \
"https://api.japanfold.aiand.com/v1/jobs?limit=20"
Paginated cursor-style: limit defaults to 20 and clamps to 100, and
next_cursor goes back as ?cursor=... for the next page.
Cancel, delete¶
curl -s -H "Authorization: Bearer $JAPANFOLD_API_KEY" -X POST https://api.japanfold.aiand.com/v1/jobs/$JOB/cancel # stop a queued/running job
curl -s -H "Authorization: Bearer $JAPANFOLD_API_KEY" -X DELETE https://api.japanfold.aiand.com/v1/jobs/$JOB # delete the job and its data
Cancel answers with the job's status and finished_at. A queued job ends at once.
A running job ends when its run has stopped, usually within a few seconds, and the
cancel waits up to 15 s for that, so the answer normally reads canceled and the
job's usage answers in the next call. If the run is slower, the answer reads running
and its detail names GET /v1/jobs/$JOB?wait=60, which returns when it has ended.
canceled is false for a job that had already ended.
A running job cannot be deleted. DELETE cancels it and answers 409; the run
stops, the job reads canceled, and a second DELETE then removes it and its
files. A canceled job is billed for the chip time it ran, 0 if it was still queued;
GET /v1/jobs/$JOB/usage says which once the job reads canceled.
Results¶
Once results_ready (or status is succeeded), read the scores and the list
of downloadable artifacts:
curl -s -H "Authorization: Bearer $JAPANFOLD_API_KEY" https://api.japanfold.aiand.com/v1/jobs/$JOB/results
Ask earlier and you get {"job_id":"...","object":"results","ready":false,"status":"queued"},
so ready is safe to poll on directly.
You may not need this call. A succeeded job read by its id (GET /v1/jobs/{id},
including the poll that saw it finish) already carries its artifacts, the same
list as below, and download, one shell command that fetches every file into the
current directory with the key from JAPANFOLD_API_KEY:
{ "status": "succeeded", "results_ready": true,
"artifacts": [ { "path": "target_1.cif", "type": "structure", "url": "/v1/jobs/.../artifacts/target_1.cif" } ],
"download": "curl -fsSL -H \"Authorization: Bearer $JAPANFOLD_API_KEY\" --create-dirs -o target_1.cif https://api.japanfold.aiand.com/v1/jobs/.../artifacts/target_1.cif" }
A job with more than 20 files lists them only here, and its download fetches
and unzips the archive instead. Read /results for the scores.
A prediction result, from the 33-residue Boltz-2 fold in the quickstart:
{
"object": "results",
"job_id": "...",
"kind": "predict",
"ready": true,
"rows": [
{ "id": "target_1", "status": "ok",
"confidence_score": 0.827157,
"complex_plddt": 0.877438, "complex_iplddt": 0.877438,
"complex_pde": 0.431932, "complex_ipde": 0.0,
"ptm": 0.626033, "iptm": 0.0,
"protein_iptm": 0.0, "ligand_iptm": 0.0,
"chains_ptm": { "0": 0.626033 },
"pair_chains_iptm": { "0": { "0": 0.626033 } },
"runtime_s": 8.6 }
],
"artifacts": [
{ "path": "target_1.cif", "target": "target_1", "type": "structure",
"url": "/v1/jobs/.../artifacts/target_1.cif" }
],
"archive_url": "/v1/jobs/.../archive"
}
rows: one row per target.confidence_scoreis the headline number, a weighted blend of the others. The rest are the AlphaFold-family metrics: plDDT (plddt,complex_plddt) is predicted local accuracy per residue, pTM (ptm) the predicted TM-score for the whole structure, ipTM (iptm) the same restricted to a chain-chain interface, and PDE (complex_pde,complex_ipde) the predicted distance error. All are 0-1, higher is better except the error terms. Which fields are present depends on the model: the block above is Boltz-2's, an ESMFold-2 row instead carriesplddt(mean, 0-1),ptm,n_residues,n_chains,samplesandmsa, and a Boltz-2 affinity run adds the affinity fields (Predictions → Affinity).- A rough read: fold
complex_plddt> 0.7, interfaceiptm> 0.5. ipTM scores an interface, so on a single-chain input it comes back0.0rather than absent, as above. - A design result carries
designsinstead ofrows, ranked for BoltzGen and unranked for RFdiffusion3 and PXDesign. An embed result carriessequences; see Embeddings → Results. artifacts[].urlandarchive_urlare paths under the base URL. Prefix them withhttps://api.japanfold.aiand.com.
Download artifacts¶
One file. The API answers with a redirect to a short-lived link and the file
comes straight from storage, so pass -L. requests and urllib follow the redirect on
their own; httpx needs follow_redirects=True:
curl -sL -H "Authorization: Bearer $JAPANFOLD_API_KEY" https://api.japanfold.aiand.com/v1/jobs/$JOB/artifacts/target_1.cif -o target_1.cif
The link is good for a few minutes and for that one file. Do not forward your
API key to it: curl, requests and httpx already drop it when the host
changes, but Python's urllib.request does not, and the storage layer refuses
a request carrying two credentials.
Send Accept: application/json and you get the link instead of being redirected
to it, as {"url": ..., "size": ..., "expires_in": ...}. That is for a page in a
browser, which cannot follow this redirect: a cross-origin request redirected on
to a third origin arrives at storage with no origin, and the fetch is refused.
Fetch the url yourself in a second request. A file storage cannot hand over
itself is streamed by the API either way, so check the content type.
Everything as a zip. The archive is built as it is sent, so there is no
Content-Length and no progress bar:
curl -s -H "Authorization: Bearer $JAPANFOLD_API_KEY" https://api.japanfold.aiand.com/v1/jobs/$JOB/archive -o results.zip
unzip -oq results.zip -d results
Logs¶
Plain-text run log, useful while a job runs or to debug a failure:
curl -s -H "Authorization: Bearer $JAPANFOLD_API_KEY" https://api.japanfold.aiand.com/v1/jobs/$JOB/logs
Retention¶
A job and its result files are kept for 30 days after it finishes, then
deleted. A deleted or expired id returns 404. Download what you want to keep.