Most quantum code lives on a simulator. Moving it to real hardware is where the work starts: each provider has its own account, keys, billing and SDK objects, and comparing two quantum computers on your own circuit means setting up both. This post walks through a setup where the circuit is written once, the device is a string, and one API key with one prepaid balance works across many quantum providers: today IonQ, IQM, Rigetti, AQT and IBM, plus cloud simulators, with more providers planned.
It uses two pieces: eigenkit, a Python library that describes a circuit once and runs it on many backends, and EigenOrbit Router, a hosted service that sends eigenkit jobs to the quantum providers for you. Both are ours, so read the rest with that in mind.
What usually stands between a circuit and a QPU
To run one circuit on quantum computers from two or three providers with your own credentials, you usually need:
- an account with each cloud or hardware provider, often with its own contract;
- cloud permissions, storage for results, the right region for each device and spending limits;
- a separate API key and SDK setup per provider;
- a way to know what a run costs before it lands on the bill.
For a team with existing cloud contracts this is a one-time cost. For a student, a researcher trying a second device or a small team comparing providers, it is often where the experiment stops.
The circuit, written once
eigenkit describes a circuit as data, separate from any SDK. Here is a small variational circuit with two qubits:
import numpy as np
from eigenkit import Circuit
from eigenkit.core.blocks import AngleEncoding, BasicVariational, assemble
from eigenkit.core.observables import all_z
spec = assemble([
AngleEncoding(in_features=2),
BasicVariational(depth=1),
])
weights = np.linspace(-0.9, 1.1, spec.n_weights)
inputs = np.array([0.2, 0.6])
On a local simulator you pass a provider name:
print(Circuit(spec, all_z(2), provider="numpy")(weights, inputs))
The device as a string
With the router extra, the same circuit runs on cloud simulators and QPUs through one key:
pip install "eigenkit[router]"
export EIGENORBIT_ROUTER_API_KEY=eo_live_...
from eigenorbit_router_client import RouterConfig
# Every device ID is listed in the public catalog
devices = {
"SV1 simulator": RouterConfig(device="braket/sv1", shots=1000),
"IQM Garnet": RouterConfig(device="braket/iqm_garnet", shots=100),
"IBM Fez": RouterConfig(device="ibm/ibm_fez", shots=4000),
}
for name, provider in devices.items():
circuit = Circuit(spec, all_z(2), provider=provider)
print(name, circuit(weights, inputs))
The loop is the whole comparison. The circuit, the observables and the post-processing stay the same, and the results have the same shape, dtype and bit order as a run with each provider's own credentials. We checked this on real runs on two cloud simulators and on QPUs from IQM and IBM. When a new provider is added to the router, it appears as one more device ID, and the code around it does not change.
The key comes from the router web app: sign in, top up a prepaid USD balance and create a key. Jobs on IBM devices run on your own IBM Quantum account, which you connect once in the app; the router charges nothing for them, and IBM bills your account directly.
Knowing the price first
Building a circuit on a paid device asks the router for an estimate and shows a CostWarning with the price of one job, split into the provider's cost and the EigenOrbit fee, and your available balance. circuit.dry_run_info() returns the same estimate as a dict (estimate_usd_micro, available_usd_micro and the gate counts of the circuit) without sending a job, which is handy in a notebook before a long batch.
For QPUs priced per job and per shot, the estimate equals the final charge: 100 shots on IQM Garnet come to about $0.53. Cloud simulators are billed by run time, and a short simulator job costs well under a cent.
The router reserves the estimate on the balance before it sends the job, so a job your balance cannot cover never starts. After the run it charges what the provider reported and returns the rest.
Waiting for a long queue
A QPU queue can be long. Instead of a blocking call you can submit the job and keep working:
handle = circuit.submit(weights, inputs)
print(handle.job_id(), handle.status())
# later in the same session
values = handle.result()
handle.status() is one non-blocking request, and handle.result() waits for the job. The router watches the provider's queue itself, so the job keeps running if you close the notebook: its result stays on the Jobs page of the web app, which lists jobs from every provider with status, cost and result.
Small things that help in practice
- Spending limit per API key. A key can go to a student or into CI with a fixed budget.
- Retries without double charges. The client retries dropped connections with the same idempotency key, so a flaky network never creates a second paid job.
-
Errors you can branch on. Every failure is a
RouterErrorwith a stablecodesuch asinsufficient_balance,device_unavailableorshots_invalid. -
Provider-specific options where they matter. For example, error suppression options on IBM devices go through
primitive_options, in the same shape as eigenkit's own IBM config. - A public device catalog. router.eigenorbit.com/devices shows every device ID, price, shot range and availability window without sign-in.
When to use your own credentials instead
If your organization already has a contract with a quantum cloud provider at negotiated prices, or you need hybrid jobs or reserved sessions, use eigenkit with your own credentials: the circuit code stays the same and only the provider changes. The router fits best when you want to try several devices without opening several accounts, give hardware access to students or a team with a budget, or keep the cost of each run visible before it happens.
Providers and devices
The catalog changes as providers and devices are added, so check router.eigenorbit.com/devices for the current list. In November 2026 it has:
| Quantum provider | Devices | Qubit technology |
|---|---|---|
| IonQ | Forte 1, Forte Enterprise 1 | Trapped ions |
| AQT | IBEX Q1 | Trapped ions |
| IQM | Garnet, Emerald | Superconducting |
| Rigetti | Cepheus-1-108Q | Superconducting |
| IBM (on your own IBM account) | ibm_fez, ibm_kingston, ibm_marrakesh | Superconducting |
| Cloud simulators | SV1, DM1 | State vector and density matrix |
IonQ, AQT, IQM, Rigetti and the simulators are reached through Amazon Braket and paid from the EigenOrbit balance; IBM devices go through IBM Quantum.
More providers and devices are coming soon. Follow EigenOrbit to see each one as it is added.
Links
- Router: router.eigenorbit.com
- Device catalog and prices: router.eigenorbit.com/devices
- Router client: PyPI
- eigenkit: GitHub, docs, PyPI
Top comments (0)