Skip to main content

Python SDK

The atomiverse Python package lets you submit calculations and retrieve results from code. It handles authentication, serialization, and polling — you describe the job and call submit.

Installation

pip install https://atomiverse.com/packages/atomiverse

Requirements: Python 3.10 or newer.

Creating an API Key

You need an API key before running any calculations.

  1. Sign in at atomiverse.com.
  2. Open Settings from the top navigation bar.
  3. Under API Keys, click New Key.
  4. Give the key a descriptive name (e.g. research-laptop, colab-notebook) and click Create.
  5. Copy the key immediately — it is only shown once.

You can create multiple keys with different names. Each key can be revoked individually from the same Settings page without affecting your other keys.

Configuring the SDK

The SDK looks for your API key in this order:

  1. Explicit call — pass your key directly:
import atomiverse

atomiverse.configure(api_key="your-api-key")
  1. Environment variable — set ATOMIVERSE_API_KEY:
export ATOMIVERSE_API_KEY=your-api-key
  1. Key file — store it in ~/.atomiverse/api_key:
mkdir -p ~/.atomiverse
echo "your-api-key" > ~/.atomiverse/api_key
chmod 600 ~/.atomiverse/api_key

Once configured via environment variable or key file, no code change is needed — the SDK picks it up automatically.

Quick Start

Generate a 3D structure from a SMILES string and run a calculation:

import atomiverse
from atomiverse import Atoms, JobFailedError, SinglePointEnergy
from atomiverse.levels import GFN2_XTB

water = Atoms.from_smiles("O")

job = SinglePointEnergy(
atoms=water,
level_of_theory=GFN2_XTB,
)
job.submit()

try:
result = job.require_result()
except JobFailedError as exc:
print(f"Job failed: {exc.failure.error}")
else:
print(f"Energy: {result.energy} Hartree")

Atoms.from_smiles returns an ASE-compatible Atoms object with 3D coordinates ready for the rest of the SDK workflow. See Atoms from SMILES for details on multi-molecule systems and reaction SMILES.

You can also call atoms.visualize() to open the generated structure directly in the Atomiverse web viewer.

If you want to start from a named molecule or an external database identifier, use resolve_molecule() to look up a single molecule from PubChem / ChEMBL and then convert it into an Atoms object. If you want that as a one-step SDK entry point, use Atoms.from_molecule_resolve().

from atomiverse import Atoms, resolve_molecule

mol = resolve_molecule("aspirin")
print(mol.name)
print(mol.canonical_smiles)

atoms = mol.to_ase()

same_atoms = Atoms.from_molecule_resolve("aspirin")

If you prefer ASE's calculator interface, you can also attach CloudCalculator directly to an ase.Atoms object and evaluate energies or forces through standard ASE calls.

Listing Jobs

Use cursor-based pagination to retrieve job summaries from your history.

import atomiverse

# First page
page = atomiverse.list_jobs(limit=50)
for job in page.jobs:
print(job["job_id"], job["state"])

# Follow the cursor for the next page
if page.has_more:
page2 = atomiverse.list_jobs(limit=50, cursor=page.next_cursor)

The returned :class:atomiverse.JobsPage carries lightweight metadatajob_id, name, state, job_type, created_at, billed_core_seconds, and molecule_preview. Full result payloads are not included (use :meth:Job.refresh or :meth:Job.require_result for those).

Iterating over all jobs

For scripts that need every job without manual cursor tracking, use :func:atomiverse.iter_jobs:

for job in atomiverse.iter_jobs(limit=50):
print(job["job_id"], job["state"])

You can cap the total with max_jobs to avoid pulling unbounded history:

for job in atomiverse.iter_jobs(limit=50, max_jobs=500):
print(job["job_id"])

Configuring the timeout

If you encounter ReadTimeout errors against a slow or remote server, increase the HTTP timeout:

atomiverse.configure(api_key="...", timeout=120.0)

You can also set the ATOMIVERSE_TIMEOUT environment variable:

export ATOMIVERSE_TIMEOUT=120

Renaming and Deleting Jobs

The SDK supports post-submission job organization:

# Rename a submitted job
job.rename("water-baseline-v2")

# Delete a terminal job (DONE / FAILED / CANCELLED)
job.delete()

You can also use module-level helpers:

atomiverse.rename_job(job.job_id, "water-baseline-v2")
atomiverse.delete_job(job.job_id)

See Job Management for full behavior, validation rules, and error handling guidance.

Checking Your Balance

import atomiverse

hours = atomiverse.balance()
print(f"{hours} core-hours remaining")

Compute Resources

CPU cores and memory are allocated automatically for every job based on the molecule and method. No resource configuration is needed.

If you use Bring Your Own Compute, you can inspect your controllers and estimate capacity from the SDK:

# List all registered controllers and their free capacity
controllers = atomiverse.list_compute_targets()
for c in controllers:
print(c.name, c.online, c.free_cores, c.free_memory_mb, c.running_jobs)

# Estimate how many jobs fit into a specific controller
estimate = atomiverse.estimate_capacity(
jobs=[spe_job, opt_job],
compute_target="my-cluster",
)
for e in estimate.estimates:
print(f"{e['n_cores']} cores, {e['memory_mb']} MB → max {e['max_concurrent']} concurrent")

See Estimating Controller Usage for batch-submission patterns and cost estimation.

Next Steps