RoRo Quantum docs
Build a quantum circuit, run it on real Qiskit simulation, and read an honest result — from the console, the SDK, or the REST API.
#Introduction
RoRo Quantum is a quantum cloud. You design circuits — visually in the console or in code with standard Qiskit — submit them as runs, and get back normalized measurement results. Every run executes for real: simulator targets run on real simulators — Qiskit Aer on our instant local tier, partner-grade simulators in the cloud — and QPU targets queue and execute on real superconducting quantum hardware. The counts you read are the real thing.
Want the real depth? Jump to Core concepts and The math — every section below layers from simple to rigorous.
There are three ways in:
- Console — the visual circuit builder, run history, lessons, and team management.
- SDKs — Python (write a Qiskit
QuantumCircuit, submit it, read the result) or Kotlin for JVM codebases. - REST API — language-agnostic HTTP endpoints, authenticated with an API key.
#Architecture
Every entry point speaks to the same core API. The core owns identity, credits, and run records; it hands the actual circuit off to a stateless quantum service, which either simulates it (Qiskit Aer for local simulator targets) or brokers it to a cloud simulator or real quantum hardware, then stores the normalized result.
#Quickstart
Create an API key in the console (API keys → Create), then submit your first run.
# 1. install pip install roro-quantum # 2. run a Bell state from roro import RoRoClient from qiskit import QuantumCircuit qc = QuantumCircuit(2, 2) qc.h(0) qc.cx(0, 1) qc.measure([0, 1], [0, 1]) roro = RoRoClient(api_key="qcs_live_…") job = roro.submit_run("roro.sim.sv", shots=5000, circuit=qc, wait=True) print(job["status"], job["counts"]) # completed {'00': 2481, '11': 2519}
#Core concepts
| Term | What it means |
|---|---|
| Run | One submission of a circuit to a target, with a number of shots. Runs are async jobs with a status. |
| Target | The machine a run executes on, identified by a targetId such as roro.sim.sv. |
| Shots | How many times the circuit is sampled. More shots → smoother statistics, higher cost. |
| Credits | Your balance. Each run costs a small amount per shot, debited when the run is accepted. |
| Workspace | A container for runs and budgets — one per project, class, or team. |
| Organization | Your account's top level: members, roles (owner/admin/member), and the credit pool. |
#Authentication
The REST API and SDK authenticate with an API key. Create one in the console; it's shown once, so store it safely. Send it as a Bearer token:
Authorization: Bearer qcs_live_…qcs_live_… and are tied to your organization. Treat them like passwords — never commit them to source control. Revoke and rotate keys anytime in the console.#Building circuits
A circuit is a set of gates on qubit wires, ending in measurements. You can build one visually in the console, or describe it in code and export QASM 2.0 — the format RoRo runs.
# the QASM 2.0 RoRo runs OPENQASM 2.0; include "qelib1.inc"; qreg q[2]; creg c[2]; h q[0]; cx q[0],q[1]; measure q -> c;
#Running circuits
A run needs a target, a shot count, and a circuit as QASM 2.0. The SDK can derive QASM from a Qiskit QuantumCircuit, or you can pass it directly.
roro = RoRoClient(api_key="qcs_live_…") qasm = """OPENQASM 2.0; include "qelib1.inc"; qreg q[2]; creg c[2]; h q[0]; cx q[0],q[1]; measure q -> c;""" job = roro.submit_run("roro.sim.sv", shots=2000, qasm=qasm, wait=True)
#Run lifecycle
When you submit a run, RoRo validates it, reserves and debits credits, executes it on the quantum service, and stores the normalized result. If your balance is too low, the run is rejected before it ever executes.
Credits, failures & refunds
Billing is settled at the front of the lifecycle, and every resolution is recorded on the run itself:
- Debit on acceptance. Credits are debited the moment a run is accepted, before it executes — from your member budget first, then the workspace budget, then the org pool. A malformed circuit is rejected with
400before anything is created or charged, and an insufficient balance rejects the run with402. - Machine-side failures are refunded automatically. If a paid hardware run fails on the provider side — backend unavailable, timeout, abnormal termination, anything that isn't your circuit's fault — the charge is returned to the account it was debited from at the moment the failure is recorded, so by the time you see
failedthe credits are already back. The refund is written down as a system-approved refund request, making the resolution auditable. A live job stuck past the 6-hour safety deadline is failed and refunded the same way. refundablemarks the remaining refund-eligible failures. When a failed, charged run has a machine-side error but wasn't refunded automatically (for example a simulator run interrupted mid-flight), the run record carriesrefundable: true— request the refund from the console or withPOST /v1/runs/{id}/refund, and an operator resolves it.refundStatustracks the resolution:null(no refund involved) ·pending·approved·rejected. Automatic refunds appear asapproved, resolved bysystem.- Failures caused by the circuit keep the charge. A circuit the target can't execute (connectivity/topology mismatch, rejected by backend validation) still consumed real capacity — those failures are not refund-eligible and
refundablestaysfalse. So doesinsufficient_credits, where nothing was debited in the first place. - Cancelling refunds. Cancelling a queued or running hardware run (
POST /v1/runs/{id}/cancel) stops the provider job, refunds the charge, and marks the runcancelled.
Watching a live run
Every run record carries four fields beyond the basics that make the lifecycle observable. The two live ones are populated for hardware queues and stay null for simulator runs, which complete instantly:
| Field | Meaning |
|---|---|
| queuePosition | Jobs ahead of yours on the hardware backend while the run is queued. null for instant simulator runs. |
| providerStatus | The raw, live status string from the hardware backend while the run is in flight. null for instant simulator runs. |
| refundable | true when a failed, charged run has a machine-side error — you may request a refund for it. |
| refundStatus | null · pending · approved · rejected — the audit trail of a refund, including automatic ones. |
Poll GET /v1/runs/{id}, or subscribe to GET /v1/runs/{id}/stream (Server-Sent Events) to get every change — including queuePosition and providerStatus — pushed until the run is terminal.
#Reading results
A completed run returns normalized counts (a histogram of measured bitstrings) and probabilities. The console also derives the most-likely outcome and the Shannon entropy.
{
"id": "run_…",
"status": "completed",
"shots": 2000,
"counts": { "00": 1001, "11": 999 },
"probabilities": { "00": 0.5005, "11": 0.4995 }
}#The math, briefly
You don't need the math to use RoRo — but here's what's happening underneath. A qubit is a vector, a gate is a matrix, and a measurement is a probability.
Superposition. A Hadamard gate creates an equal blend of 0 and 1 — a fair quantum coin:
Entanglement. A Hadamard followed by a CNOT produces a Bell state — two qubits whose outcomes are perfectly correlated:
Measurement (Born rule). The probability of each outcome is the squared amplitude — exactly what your shot counts estimate:
Entropy. The console reports the Shannon entropy of the outcome distribution — how spread-out the result is. A fair coin gives 1 bit; a certain outcome gives 0:
#Credits & pricing
Credits work like tokens. Each run costs a small amount per shot, debited from your balance when the run is accepted (failed machine-side runs are refunded — see the run lifecycle). You start with 100 free credits; top up anytime, and allocate budgets to members and workspaces. Package prices are public — see pricing on the homepage.
# cost of a run
cost = ceil(shots × target.costPerShot)- If your balance is too low, the run is rejected before it executes (HTTP
402). - Every debit is recorded in an append-only ledger — you can audit where each credit went.
- Owners and admins can allocate budgets to members and workspaces.
#Targets
Every run executes on a target (a machine), identified by its targetId — such as roro.sim.sv. The catalog changes as machines come and go, so list what's available to you with GET /v1/machines rather than hard-coding ids; when you submit a run, pass the id in the body field machineTargetId (targetId is accepted as an alias).
Machine types
Each machine has a type that tells you what it really is — the honest difference between an exact simulator, a hardware test device, and a real quantum processor. Pick by what you're doing:
| type | What it is | Use it when… |
|---|---|---|
aer | An instant, local, exact simulator. No queue, no device noise. | Learning, prototyping, and verifying a circuit is correct before you spend on hardware. |
qsimulator | A cloud simulator. Still exact/statistical, but scales to more qubits than a local one. | Bigger circuits than the local tier can handle, still with no hardware queue. |
qtester | A hardware-adjacent test device — mirrors a processor's gate set, priced like a simulator. | Rehearsing a hardware run (gates, topology) without paying hardware prices. |
qpu | A real quantum processor. Results carry genuine device noise and may queue. | You want real hardware results — after the circuit already works on a simulator. |
aer, scale on a qsimulator, then move the same circuit to a qpu. You never rewrite anything — the platform transpiles your circuit to whatever gate set the target runs.Reading a machine
Beyond type, each machine in GET /v1/machines tells you what it can do and whether it's ready right now. The capability fields are vendor-neutral — they describe the physics, not a brand:
| Field | Meaning |
|---|---|
qubits | How many qubits you can address. |
modality | The technology: simulator, superconducting, trapped-ion, or neutral-atom. |
connectivity | How qubits couple: all-to-all, nearest-neighbour, programmable, or limited. |
nativeGates | The basis the hardware physically runs. Informational — you can build with any gate; the platform transpiles. |
availability | A short human note on how the machine is reached and when it may be busy. |
Live status & queue
Hardware isn't always up, and popular processors form a queue. Three fields on each machine tell you what to expect before you submit:
| Field | Meaning |
|---|---|
online | true when the machine can accept runs right now. Simulators are effectively always online. |
status | online (ready), busy (in high demand — visible but not runnable at the moment), or offline. |
queueDepth | Jobs already queued ahead on that backend, when the machine reports it. Higher = longer wait. null when unknown (e.g. simulators). |
Once a run is in flight you can watch its own place in the queue live via queuePosition and providerStatus.
What a run costs
Every machine sets a costPerShot in credits. A run's price is simply ceil(shots × costPerShot), debited when the run is accepted — so more shots (smoother statistics) cost proportionally more, and a bigger, faster, or busier processor costs more per shot than a simulator. Simulators are cheap or free; real processors carry the real cost of the hardware. Preview the exact charge for any (machine, shots) without spending using POST /v1/runs/quote, and see the general model in Credits & pricing.
Example targets
Two always-on simulators to start with — but again, read the live list from GET /v1/machines rather than hard-coding these. For the full fleet and how to call each one, see Machines & how to call them.
| Target ID | Type | Description |
|---|---|---|
| roro.sim.sv | Simulator | Statevector simulator — fast, exact sampling. The default for learning and prototyping. |
| roro.sim.noisy | Simulator | Simulator with a basic noise model, for more realistic statistics. |
#Machines & how to call them
Every machine has a codename — a short, stable id like quera.aquila or roro.sim.sv. The codename is the machine's targetId: it's the one thing you pass to run() / submit_run() in the SDK (or machineTargetId over the REST API) to run there. Copy a codename from the Machines page in the console, or read them live from GET /v1/machines.
roro.* codenames. The famous third-party QPUs and simulators reached through Amazon Braket keep their real vendor names — QuEra, Rigetti, IQM, AQT, Amazon — with matching codenames like iqm.garnet.The live machines
A snapshot of the current fleet. Prices and availability change — always read the authoritative costPerShot, online and status from GET /v1/machines before you rely on them.
| Name | Type | Qubits | Per-shot price | SDK codename |
|---|---|---|---|---|
| RoRo Simulator | Simulator (aer) | 8 | 0.005 cr | roro.sim.sv |
| RoRo Noisy Simulator | Simulator (aer) | 8 | 0.01 cr | roro.sim.noisy |
| RoRo Quantum | QPU · superconducting | 60 | 0.20 cr | roro.qpu.q1 |
| Amazon SV1 (state-vector simulator) | Cloud simulator (qsimulator) | 34 | 0.01 cr | amazon.sv1 |
| Amazon DM1 (density-matrix simulator) | Cloud simulator (qsimulator) | 17 | 0.01 cr | amazon.dm1 |
| QuEra Aquila | QPU · neutral-atom | 256 | 0.30 cr | quera.aquila |
| Rigetti Cepheus-1 | QPU · superconducting | 108 | 0.30 cr | rigetti.cepheus |
| IQM Garnet | QPU · superconducting | 20 | 0.30 cr | iqm.garnet |
| IQM Emerald | QPU · superconducting | 54 | 0.30 cr | iqm.emerald |
| AQT Ibex-Q1 | QPU · trapped-ion | 12 | 0.30 cr | aqt.ibex |
The Amazon SV1/DM1 simulators and the QuEra / Rigetti / IQM / AQT processors are reached through Amazon Braket; roro.* machines are RoRo's own tiers. Every one is called the same way — by its codename.
Call one by codename
Point submit_run at any codename. Develop on a simulator, then move the same circuit to a QPU by swapping only the codename — the platform transpiles it to whatever gate set the target runs.
# pip install roro-quantum from roro import RoRoClient from qiskit import QuantumCircuit roro = RoRoClient() # reads your RORO_API_KEY qc = QuantumCircuit(2, 2) qc.h(0); qc.cx(0, 1); qc.measure([0, 1], [0, 1]) # Run on a machine by its codename — here Amazon Braket's SV1 cloud simulator: job = roro.submit_run("amazon.sv1", shots=1000, circuit=qc, wait=True) print(job["status"], job["counts"]) # Same circuit, real hardware — just change the codename, e.g. "iqm.garnet": job = roro.submit_run("iqm.garnet", shots=1000, circuit=qc, wait=True)
To discover codenames at runtime instead of hard-coding them, list the live fleet — each entry's targetId is its codename:
for m in roro.machines(): print(m["targetId"], "·", m["displayName"], "·", m["qubits"], "qubits · ", m["costPerShot"], "cr/shot")
Preview the exact charge for a (codename, shots) pair without spending via POST /v1/runs/quote, and see Targets for the full field-by-field breakdown of what each machine reports.
#REST API
Base URL https://api.roroquantum.com. All endpoints require the Authorization: Bearer header and return JSON.
List targets
curl https://api.roroquantum.com/v1/machines \
-H "Authorization: Bearer qcs_live_…"Submit a run
Body fields: machineTargetId (string — the target's targetId from the targets list; the field name targetId is accepted as an alias), shots (int), qasm (string, QASM 2.0).
curl -X POST https://api.roroquantum.com/v1/runs \ -H "Authorization: Bearer qcs_live_…" \ -H "Content-Type: application/json" \ -d '{ "machineTargetId": "roro.sim.sv", "shots": 5000, "qasm": "OPENQASM 2.0; include \"qelib1.inc\"; qreg q[2]; creg c[2]; h q[0]; cx q[0],q[1]; measure q -> c;" }'
List & fetch runs
curl https://api.roroquantum.com/v1/runs/run_123 \
-H "Authorization: Bearer qcs_live_…"Cancel & stream a run
Cancel stops a queued/running run (hardware jobs are stopped at the provider and the charge is refunded). Stream delivers live run updates over Server-Sent Events until the run is terminal — see Watching a live run.
#Run statuses
| Status | Meaning |
|---|---|
| queued | Accepted and waiting to execute. |
| running | Executing on the quantum service. |
| completed | Finished — counts and probabilities are available. |
| failed | Could not complete. Machine-side failures are refunded automatically; failures caused by the circuit keep the charge — see Credits, failures & refunds. |
| cancelled | Stopped by you before it finished; the charge is refunded. |
#Errors
Errors use standard HTTP status codes with a JSON body: { "error": "message" }.
| Code | Meaning |
|---|---|
| 400 | Bad request — invalid QASM, missing field, or bad shots value. |
| 401 | Missing or invalid API key. |
| 402 | Insufficient credits for the requested run. |
| 404 | Run or resource not found. |
| 429 | Too many requests — slow down and retry. |
#Python SDK
The official SDK wraps the REST API with a small, Qiskit-friendly client. It's open source and published on PyPI.
PyPI · roro-quantum v0.3.0 Support
# REST client only pip install roro-quantum # + Qiskit BackendV2 provider pip install "roro-quantum[qiskit]"
from roro import RoRoClient from qiskit import QuantumCircuit roro = RoRoClient(api_key="qcs_live_…") # discover targets for m in roro.machines(): print(m["targetId"], m["qubits"]) # build + submit a GHZ state, block until it finishes qc = QuantumCircuit(3, 3) qc.h(0); qc.cx(0, 1); qc.cx(1, 2) qc.measure([0, 1, 2], [0, 1, 2]) job = roro.submit_run("roro.sim.sv", shots=4000, circuit=qc, wait=True) print(job["counts"]) # or submit without blocking, then poll yourself job = roro.submit_run("roro.sim.sv", shots=4000, circuit=qc) job = roro.wait_for_run(job["id"]) # or roro.run(job["id"])
Already on Qiskit? Use RoRo as a drop-in BackendV2 provider:
from qiskit import QuantumCircuit from roro.provider import RoRoProvider qc = QuantumCircuit(2, 2) qc.h(0); qc.cx(0, 1); qc.measure_all() backend = RoRoProvider(api_key="qcs_live_…").get_backend("roro.sim.sv") result = backend.run(qc, shots=5000).result() print(result.get_counts())
curl or any HTTP client.#Kotlin SDK
A typed, dependency-light Kotlin/JVM client for the same REST API — one class, net.mertnode.roroqs.sdk.RoRoClient, built on the JDK's java.net.http plus kotlinx-serialization (Java 21+). It authenticates with the same qcs_live_… API key; circuits are submitted as OpenQASM 2.0 strings.
import net.mertnode.roroqs.sdk.RoRoClient val roro = RoRoClient(apiKey = "qcs_live_…") // discover targets (typed) roro.machines().forEach { println("${it.targetId} ${it.qubits}q ${it.costPerShot} cr/shot") } // submit a Bell state (OpenQASM 2.0) and block until it finishes val bell = """ OPENQASM 2.0; include "qelib1.inc"; qreg q[2]; creg c[2]; h q[0]; cx q[0],q[1]; measure q -> c; """.trimIndent() val run = roro.submitRun("roro.sim.sv", shots = 5000, qasm = bell, wait = true) println(run.status) // completed println(run.counts) // {00=2497, 11=2503}
Runs are plain data — poll, stream, cancel and paginate with the same client:
// submit without blocking… val job = roro.submitRun("roro.sim.sv", shots = 4000, qasm = bell) // …then poll until terminal (completed / failed / cancelled) val done = roro.waitForRun(job.id) // or stream every change over SSE (incl. queuePosition / providerStatus) roro.streamRun(job.id) { r -> println("${r.status} ${r.queuePosition ?: ""}") } // cancel a queued/running run (stops the provider job, refunds the charge) roro.cancelRun(job.id) // history, newest first roro.runs(limit = 20).forEach { println("${it.id} ${it.status} ${it.cost}") }
Types: Machine(targetId, displayName, provider, kind, qubits, costPerShot, online, status, queueDepth) and Run(id, machineTargetId, shots, status, cost, counts, probabilities, error, createdAt, qasm, providerJobId, providerStatus, queuePosition). Any non-2xx response throws RoRoException(status, message), and roro.raw("/v1/…") is the escape hatch for any other GET endpoint.
#JavaScript / TypeScript SDK
Roadmap An isomorphic client for Node and the browser. It isn't published on npm yet — until it ships, call the REST API directly (it's a couple of fetch calls). Here's the shape it will have:
// (coming soon) npm install @roro/quantum import { RoRoClient } from "@roro/quantum"; const roro = new RoRoClient({ apiKey: "qcs_live_…" }); // submit a Bell state (OpenQASM 2.0) const qasm = `OPENQASM 2.0; include "qelib1.inc"; qreg q[2]; creg c[2]; h q[0]; cx q[0],q[1]; measure q -> c;`; const job = await roro.submitAndWait("roro.sim.sv", { shots: 5000, qasm }); console.log(job.status, job.counts); // completed { '00': 2519, '11': 2481 }
#Glossary
| Term | Definition |
|---|---|
| Qubit | A quantum bit — the basic unit of quantum information; can be in superposition of 0 and 1. |
| Superposition | A qubit being a blend of 0 and 1 until measured. A Hadamard (H) gate creates an equal one. |
| Entanglement | A correlation between qubits so the result of one constrains another — e.g. a Bell state. |
| Gate | An operation on one or more qubits — H, X, CX (CNOT), measurement, and more. |
| Target | The machine a run executes on, identified by its targetId (e.g. roro.sim.sv). The run-submit body names this field machineTargetId; targetId is accepted as an alias. |
| QASM | OpenQASM 2.0, the text format describing a circuit, which RoRo executes. |
| Aer | Qiskit's high-performance simulator — it powers RoRo's instant local simulator targets (machine type aer). |
| Machine type | Every machine reports a type: aer = instant local simulator · qsimulator = partner-grade simulator · qtester = hardware test device · qpu = real quantum processor. |
| QPU | A real quantum processor. Runs on qpu targets queue and execute on real superconducting hardware — results include genuine device noise. |