Skip to main content

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:

StatusMeaning
submittedJob was accepted and is pending execution
cache_hitA matching completed result already exists
failedJob 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:

  • null or 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)
note

Batch submission does not support wait=True. You must poll individual jobs or use the dashboard to track completion.