Batch Submission
Submit up to 1000 jobs in a single HTTP request. The SDK's
:func:atomiverse.submit_batch sends all payloads at once and returns
per-job results — submitted, cache-hit, or failed with an error.
import atomiverse
from atomiverse.levels import GFN2_XTB
atomiverse.configure(api_key="av_...")
# Build your job objects
molecules = [atomiverse.Atoms.from_smiles(s) for s in ["O", "CCO", "CCN"]]
jobs = [
atomiverse.SinglePointEnergy(atoms=mol, level_of_theory=GFN2_XTB)
for mol in molecules
]
# Serialise and submit all at once
payloads = [job.model_dump() for job in jobs]
result = atomiverse.submit_batch(payloads)
print(f"{result['submitted']}/{result['total']} submitted, "
f"{result['failed']} failed, {result['cache_hits']} cache hits")
for jr in result["jobs"]:
if jr["status"] == "submitted":
print(f"Job {jr['job_id']}: {jr['state']}")
else:
print(f"Failed: {jr['error']}")
Response format
:func:atomiverse.submit_batch returns a dict:
{
"total": 3,
"submitted": 2,
"failed": 1,
"cache_hits": 0,
"jobs": [
{"job_id": "abc123...", "state": "pending", "status": "submitted"},
{"job_id": "def456...", "state": "pending", "status": "submitted"},
{"job_id": None, "state": None, "status": "failed",
"error": "Invalid job payload: ..."},
]
}
Each entry has a status field:
| Status | Meaning |
|---|---|
submitted | Job was accepted and is pending execution |
cache_hit | A matching completed result already exists |
failed | Job was rejected — see error for details |
Partial success
The batch uses partial-success semantics: invalid or over-capacity
jobs are rejected individually while the rest proceed. The response
includes a per-job status and error so you can inspect exactly
what happened.
Common failure reasons:
- Invalid job payload (wrong
job_type, missing fields) - BYOC controller at capacity
- Managed compute disabled
Balance pre-flight check
For managed compute, the SDK estimates the total cost of the batch
before submission. If your balance is insufficient, the request is
rejected with HTTP 402. This prevents partial submissions where
the balance runs out mid-batch.
The estimate uses a conservative 1-hour runtime per job multiplied by the allocated core count and billing rate.
BYOC capacity tracking
For Bring Your Own Compute controllers, capacity is tracked cumulatively across the batch. If the controller has room for 5 SPE jobs and you submit 8, the first 5 succeed and the remaining 3 fail with a capacity error.
Batch size limits
The maximum batch size is 1000 jobs by default (configurable by Atomiverse administrators). Send multiple batches if you need to submit more.
# Submit 2500 jobs in 3 batches
chunk_size = 1000
for i in range(0, len(payloads), chunk_size):
chunk = payloads[i:i + chunk_size]
result = atomiverse.submit_batch(chunk)
print(f"Batch {i // chunk_size + 1}: {result['submitted']} submitted")
Rate limiting
Batch submissions are rate-limited separately from single-job submissions. The default limit is 2 batches per minute per user.
HTTP API
If you are calling the API directly (without the SDK), the endpoint is:
POST /api/v1/jobs/submit-batch
Authorization: Bearer <workspace-api-key>
Content-Type: application/json
{
"jobs": [
{"job": {"job_type": "single_point_energy", ...}, "compute_target": null},
{"job": {"job_type": "single_point_energy", ...}, "compute_target": "local"}
]
}
The compute_target field on each entry is optional:
nullor omitted → managed cloud compute"managed"→ managed cloud compute (explicit)"local"→ run on the current machine (auto-sets up a local controller)- Any other string → a registered BYOC controller name
Tracking submitted jobs
After a successful batch submission, track individual jobs by their
job_id:
result = atomiverse.submit_batch(payloads)
# Poll until all are done
from atomiverse.state import State
job_ids = [j["job_id"] for j in result["jobs"] if j["job_id"]]
while job_ids:
done = []
for jid in job_ids:
status = atomiverse.Job.load(...) # load from a saved snapshot
# Or: poll via the API
# status = atomiverse._client_jobs.status(jid)
if status["state"] in ("done", "failed", "cancelled"):
done.append(jid)
for jid in done:
job_ids.remove(jid)
Batch submission does not support wait=True. You must poll
individual jobs or use the dashboard to track completion.