This page covers how to submit quantum jobs through the QbraidProvider — from single
one-off submissions, to batching multiple circuits in a single job, to grouped workflows
that span multiple devices and providers.For provider setup and authentication, see QbraidProvider - Usage.
When running related quantum experiments — parameter sweeps, algorithm comparisons,
cross-device benchmarks — you often end up with many independent jobs that are logically
part of the same workflow. Without grouping, these jobs are scattered across your job history
with no way to track or retrieve them together.GroupJobSession solves this by grouping any number of jobs under a single group ID.
Key benefits:
Cross-device, cross-provider: submit jobs to different backends (AWS SV1, IonQ, qBraid simulators) within the same group
Unified tracking: all jobs share a group QRN visible in the qBraid dashboard
Aggregated results: retrieve all results at once with group.results()
Lifecycle management: auto-close with TTL, cancellation, completion callbacks
The simplest way to use group jobs. All jobs submitted inside the with block are
automatically tagged with the group ID. The group closes when the block exits.
from qbraid.runtime import GroupJobSession, QbraidProviderprovider = QbraidProvider()bell = """OPENQASM 3.0;include "stdgates.inc";qubit[2] q;bit[2] c;h q[0];cx q[0], q[1];c = measure q;"""# All jobs inside this block belong to the same groupwith GroupJobSession(name="Bell State Sweep") as group: # Submit to different devices within the same group sv1 = provider.get_device("aws:aws:sim:sv1") job1 = sv1.run(bell, shots=100) tn1 = provider.get_device("aws:aws:sim:tn1") job2 = tn1.run(bell, shots=100) print(f"Group ID: {group.group_id}") print(f"Jobs in group: {len(group.jobs)}")# Group ID: group:abc-123456# Jobs in group: 2
For interactive workflows like Jupyter notebooks, you can open and close the group
manually across multiple cells.
from qbraid.runtime import GroupJobSession, QbraidProviderprovider = QbraidProvider()# Create and open the group manuallygroup = GroupJobSession(name="Notebook Experiment")group.open()# Cell 2: submit jobs (can be in separate notebook cells)device = provider.get_device("qbraid:qbraid:sim:qir-sv")job1 = device.run(circuit_1, shots=100)job2 = device.run(circuit_2, shots=100)# Cell 3: close when done submittinggroup.close()
Set a time-to-live so the group automatically closes after a duration, even if you
forget to call close() or your kernel crashes. Defaults to 1 hour (3600s) if not specified.
# Group auto-closes after 60 seconds (default: 3600s / 1 hour)with GroupJobSession(name="Quick Sweep", max_ttl=60) as group: device = provider.get_device("aws:aws:sim:sv1") job = device.run(bell, shots=10) print(f"TTL: {group.max_ttl}s")
The max_ttl parameter accepts values from 1 to 86400 seconds (24 hours). If
not specified, the backend defaults to 3600 seconds (1 hour).
After closing a group, retrieve all job results at once. group.results() blocks until
every job reaches a terminal state (completed, failed, or cancelled).
with GroupJobSession(name="Result Demo") as group: device = provider.get_device("qbraid:qbraid:sim:qir-sv") job1 = device.run(bell, shots=100) job2 = device.run(bell, shots=100)# Wait for all jobs and collect resultsresults = group.results(timeout=300)print(results)for job_id, result in results.results.items(): print(f"{job_id}: {result.data.get_counts()}")
You can also filter results by outcome:
# Only successful resultssuccessful = results.successful()# Only failed resultsfailed = results.failed()
Register a callback that fires automatically when all jobs complete. The callback
runs at context exit, after the group is closed.
def analyze(results): """Process results when all jobs finish.""" for job_id, result in results.items(): counts = result.data.get_counts() print(f"{job_id}: {counts}")with GroupJobSession(name="With Callback") as group: device = provider.get_device("qbraid:qbraid:sim:qir-sv") device.run(bell, shots=100) device.run(bell, shots=100) # Callback fires automatically when the context exits group.on_all_complete(analyze, timeout=600)
group = GroupJobSession(name="Cancellable")group.open()device = provider.get_device("aws:aws:sim:sv1")job1 = device.run(bell, shots=1000)job2 = device.run(bell, shots=1000)# Cancel the entire group and reset the sessiongroup.cancel()print(group.status())# GroupStatus.CANCELLED
Submit multiple circuits as a single job to a device that supports batched execution.
Unlike group jobs, which coordinate independent jobs across devices, batch
jobs send all circuits in one request and return a unified result.
Pass a list of programs with as_batch=True to submit them as a single job:
from qiskit import QuantumCircuitfrom qbraid.runtime import QbraidProviderprovider = QbraidProvider()device = provider.get_device("qbraid:equal1:sim:bell-1")# Define multiple circuitsbell = QuantumCircuit(2, 2)bell.h(0)bell.cx(0, 1)bell.measure([0, 1], [0, 1])ghz = QuantumCircuit(3, 3)ghz.h(0)ghz.cx(0, 1)ghz.cx(1, 2)ghz.measure([0, 1, 2], [0, 1, 2])x_gate = QuantumCircuit(1, 1)x_gate.x(0)x_gate.measure(0, 0)# Submit all three circuits as a single batch jobjob = device.run([bell, ghz, x_gate], shots=1000, as_batch=True)print(type(job))# <class 'qbraid.runtime.native.job.QbraidJob'>
A batch submission returns a single QbraidJob, not a list. All circuits share the same
job ID, device, and shot count.
as_batch=True requires a device that supports batch execution. You can check
this via the device profile’s batch_job_support flag. Passing
as_batch=True to an unsupported device raises a ValueError.
In a batch job, individual circuits can fail while others succeed. Failed circuits
carry their own error message, accessible through their per-circuit result:
result = job.result()for i, circuit_result in enumerate(result.results): if not circuit_result.success: print(f"Circuit {i} failed: {circuit_result.details.get('statusMsg')}") else: print(f"Circuit {i}: {circuit_result.data.get_counts()}")