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}
roro-quantum is the REST client on PyPI today, and it is deprecated in favour of the platform SDK (roro-quantum-platform 1.0, import roro_platform). The platform SDK already runs in the Lab; its PyPI release is pending. Until then the client above keeps working unchanged.#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/educator/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. Our own simulators (roro.sim.*, free: true in the machine list) are free — a run there charges nothing. On every other machine a run costs a small amount per shot — plus, on a machine priced by device time (priceModel: "per-second"), an amount per second of the device time its quote projects — 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 the pricing page.
# cost of a run on a machine priced per shot (0 on a machine whose `free` is true) cost = ceil(shots × target.costPerShot) # on a machine priced by device time the time terms join in; the seconds are the quote's estimatedSeconds, # so read the price itself from POST /v1/runs/quote cost = max(target.minJobCredits, target.costPerJob + shots × target.costPerShot + ceil(seconds) × target.costPerSecond)
- 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.
- A run with resilience is billed in shot-equivalents: the quote shows the total before you spend.
#Resilience (error mitigation)
Resilience is an optional run option: you ask the platform to reduce the effect of noise on your result, and it runs a small set of extra circuits in the same run to do it. It is mitigation, not error correction — it post-processes what was measured; it does not protect the qubits while your circuit runs. Your counts and probabilities always stay exactly as the server received them, and the derived distribution arrives separately, in mitigated.
| level | What runs | Circuits |
|---|---|---|
0 (default) | Nothing extra — a plain run. | 1 |
1 | Readout mitigation. Two calibration circuits — every measured qubit prepared in 0, then in 1 — run in the same set on the same qubits, and the measured flip rates are inverted bit by bit (a tensored model). This is not TREX: each bit is modelled on its own, so correlated readout errors are not captured. Nothing is cached; the calibration is measured with your run. | 3 |
2 | Readout mitigation + zero-noise extrapolation (ZNE). Your circuit also runs gate-folded at scales 3 and 5 (a scale-k circuit is the same circuit made k× longer), readout mitigation is applied to each scale, and the result is extrapolated linearly to zero noise. Without an observable each outcome's probability is extrapolated from the scales — read that as a heuristic. | 5 |
Instead of a level you can set the methods yourself: "readout": true and "zne": {"scales": [1, 3, 5], "extrapolator": "linear", "observable": "ZZ"}. Scales are 2 to 4 odd integers in ascending order, starting at 1, each at most 7; richardson is offered only with an observable, which may contain only Z and I (its rightmost character is classical bit 0 — the rightmost character of a counts key). Explicit fields override the level. An unknown key inside resilience is a 400, because a typo would otherwise re-price your run.
What resilience costs
Resilience is billed in shot-equivalents at the machine's per-shot price: readout adds two calibration circuits, and ZNE bills each scale k× because the folded circuit is k× longer. The quote shows the total before you spend. ZNE circuits that ran are charged even when extrapolation is skipped — you receive their counts. Circuits of a set that did not run are refunded automatically when the run completes.
# what a resilience run bills (the server computes it — read it from the quote) effectiveShots = shots × (sum of ZNE scales, or 1) + 2 × calibrationShots # + 2 × … only with readout cost = effectiveShots × target.costPerShot # per-shot machines; a device-time machine adds its time terms # e.g. level 2 at 1024 shots, calibration 1024 shots: 1024 × (1+3+5) + 2 × 1024 = 11264 shot-equivalents
The platform chooses calibrationShots (never more than your shots); the quote's effectiveShots, circuits, baseCostMilli and resilience block show exactly what will run and what it costs. A machine also publishes resilienceMaxEffectiveShots — the most shot-equivalents one resilience run may bill there.
Where it is offered
Resilience is offered on the noise-model simulators when enabled, and readout on IBM hardware when the operator switches it on. Every machine in GET /v1/machines (and its /properties) says what it offers right now, and why when it offers nothing:
| Field | Meaning |
|---|---|
resilience | The methods you can request here right now, e.g. ["readout", "zne"]; [] when none. |
resilienceState | offered · switched-off-by-operator (the machine could offer it, but it is switched off or not available right now) · not-offered (this machine does not offer it — for example the exact simulator, which has no noise to mitigate). |
resilienceNote | The reason, in one sentence, whenever something is not offered. |
resilienceMaxEffectiveShots | The shot-equivalent ceiling of one resilience run; null when nothing is offered. |
A request a machine cannot honour is refused at the quote (valid: false with validationError) and, with the same text, at submit (400) — nothing is charged. If the platform cannot plan the set right now, the quote is invalid and submit answers 503 resilience quote unavailable — try again; it never guesses a price.
Reading a mitigated result
| Field | Meaning |
|---|---|
countsProvenance | Where counts came from: measured — the raw measured counts; provider-corrected — results from provider-corrected hardware are labelled as received (the provider applied its own readout correction, so a second one is not offered); null — as received (older runs, and machines that do not report it). |
effectiveShots | The shot-equivalents billed (equal to shots on a plain run). |
resilience | The resolved plan and how it settled: status is applied, zne-measured-not-extrapolated (the folds ran and are charged; you get their counts), readout-measured-not-applied, partially-applied (some circuits did not run and were refunded — refundedMilli) or not-applied-refunded; plus notes. |
mitigated | The derived result: probabilities, method (readout · zne · readout+zne), negativityClipped, calibration (source: "measured-this-run", per-bit p01/p10) and zne (scales, values, extrapolation), and warnings. |
resilienceDetail | The raw histograms behind it — the two calibration histograms and the per-scale counts. Only on GET /v1/runs/{id} and the stream's final done event. |
curl -X POST https://api.roroquantum.com/v1/runs/quote \ -H "Authorization: Bearer qcs_live_…" \ -H "Content-Type: application/json" \ -d '{ "machineTargetId": "roro.sim.noisy", "shots": 1024, "qasm": "OPENQASM 2.0; …", "resilience": { "level": 2 } }' # → {"valid": true, "effectiveShots": 11264, "circuits": 5, "costCredits": …, "resilience": {"calibrationShots": 1024, "zneScales": [1, 3, 5], …}, …} # or {"valid": false, "validationError": "…why this machine can't run it…"}
Submitting is the same body on POST /v1/runs. Read the result by its labels: counts are what countsProvenance says they are (measured reads “measured (raw)”, anything else “as received”), and mitigated is null whenever nothing was applied — check resilience.status before you use it:
curl -X POST https://api.roroquantum.com/v1/runs \ -H "Authorization: Bearer qcs_live_…" \ -H "Content-Type: application/json" \ -d '{ "machineTargetId": "roro.sim.noisy", "shots": 1024, "qasm": "OPENQASM 2.0; …", "resilience": { "level": 1 } }' # then GET /v1/runs/{id} until "status": "completed": # "countsProvenance": "measured" → counts are the raw measured histogram # "resilience": {"status": "applied", …} → "mitigated": {"probabilities": {…}, "method": "readout", …} # any status where nothing was applied → "mitigated": null
resilience argument yet — the published Python client is roro-quantum 0.4.1, and the JavaScript and Kotlin clients are not on a public registry at all. The platform SDK (rq.run(…, resilience={"level": 1})) carries it and runs in the Lab today; its PyPI release is pending. The JavaScript client ignores an option it does not know, so a run submitted through it with resilience is a plain run. Until an SDK release carries the argument, send the REST field above from any HTTP client.#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 and a free flag. On a machine priced per shot (priceModel: "per-shot") a run's price is ceil(shots × costPerShot); a machine priced by device time (priceModel: "per-second") also lists costPerSecond and minJobCredits, and its price adds the seconds its quote projects. Either way the price is 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. Our own simulators are free (free: true, costPerShot: 0); partner simulators are cheap; 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, free. The default for learning and prototyping. |
| roro.sim.noisy | Simulator | Simulator with a basic noise model, free, for more realistic statistics. |
#Machines & how to call them
Every machine has a codename — a short, stable id like iqm.emerald 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 — Rigetti, IQM, Amazon — with matching codenames like iqm.emerald.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 Quantum A1 | QPU · superconducting | 60 | 0.25 cr | roro.qpu.q1 |
| RoRo Simulator | Simulator (aer) | 22 | free | roro.sim.sv |
| RoRo Noisy Simulator | Simulator (aer) | 22 | free | roro.sim.noisy |
| RoRo Superconducting Profile | Simulator (aer) | 22 | free | roro.sim.noisy.sc |
| RoRo Trapped-Ion Profile | Simulator (aer) | 22 | free | roro.sim.noisy.ion |
| RoRo Neutral-Atom Profile | Simulator (aer) | 22 | free | roro.sim.noisy.atom |
| IQM Emerald | QPU · superconducting | 54 | 8.00 cr | iqm.emerald |
| Rigetti Cepheus-1 | QPU · superconducting | 108 | 2.125 cr | rigetti.cepheus |
| 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 |
| IBM Kingston (Heron r2) | QPU · superconducting | 156 | 20.00 cr | ibm.qpu.kingston |
| IBM Fez (Heron r2) | QPU · superconducting | 156 | 20.00 cr | ibm.qpu.fez |
| IBM Marrakesh (Heron r2) | QPU · superconducting | 156 | 20.00 cr | ibm.qpu.marrakesh |
The Amazon SV1/DM1 simulators and the Rigetti / IQM 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.emerald": job = roro.submit_run("iqm.emerald", 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(): rate = "free" if m["free"] else f"{m['costPerShot']} cr/shot" print(m["targetId"], "·", m["displayName"], "·", m["qubits"], "qubits ·", rate)
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), and optionally resilience (object — see Resilience).
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.
Price a run without submitting it
Same body as POST /v1/runs, same validation and pricing engine, nothing charged. Returns costCredits, valid (+ validationError), balanceCredits and sufficient. (POST /v1/quote is the same handler under the path the SDKs called first — both work.)
curl -X POST https://api.roroquantum.com/v1/runs/quote \ -H "Authorization: Bearer qcs_live_…" \ -H "Content-Type: application/json" \ -d '{ "machineTargetId": "roro.sim.sv", "shots": 5000, "qasm": "OPENQASM 2.0; …" }' # → {"costCredits": 25.0, "valid": true, "sufficient": true, "balanceCredits": 100.0, …}
Batch, properties, refund
Batch takes {"runs": [ …1–20 run bodies… ]}; each item is validated, priced and charged on its own and reported in its results slot (ok, run or error + errorStatus) — a failed item never rolls the others back (partial: true). Properties returns what to compile against: nativeGates, acceptedGates, minShots/maxShots, connectivity. Refund opens a request for a run that failed on the machine's side and was charged (refundable: true); it returns 201 with the ticket (status: pending) and an operator resolves it — see Credits, failures & refunds.
RoRo Copilot
One metered chat turn, billed to your credits by token usage. {"messages": [{"role": "user", "content": "…"}]} in the Anthropic Messages shape (system, tools, max_tokens, thinking optional; the model is chosen server-side). The reply carries content blocks and a usage object with credits_milli (charged) and balance_milli (after). Add "stream": true for Server-Sent Events: the model's events verbatim, then one trailing roro_usage frame.
#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. |
| 503 | A machine or a resilience plan is unavailable right now (resilience quote unavailable — try again) — nothing was charged; retry. |
#Platform SDK (roro-quantum-platform 1.0)
The Python SDK going forward: one Platform object for circuits, free local simulators, real machines, server-side prices, error mitigation, run history and RoRo Copilot (with an API key — the browser kernels carry the plugin but do not run it, because a copilot turn spends credits and they have no step that asks first) — the same package the Lab runs in your browser. It imports as roro_platform; its command line is roro.
pip install roro-quantum-platform.from roro_platform import Platform rq = Platform(api_key="qcs_live_…") # or RORO_API_KEY; without a key the local simulators still run BELL = 'OPENQASM 2.0; include "qelib1.inc"; qreg q[2]; creg c[2]; h q[0]; cx q[0],q[1]; measure q -> c;' print(rq.run(BELL, shots=1000).counts) # free, local est = rq.estimate(BELL, shots=1000, target="iqm.emerald")[0] # the server's quote; nothing is charged result = rq.run(BELL, shots=1000, target="iqm.emerald") # real hardware: queued, then streamed to the end for r in rq.runs(limit=5): # your run history, every surface print(r.id, r.status, r.cost_credits)
Example data that says it is example data: roro_platform.synthetic generates business tables (a shift roster, supplier costs, orders, routes) and noisy quantum counts from a stated noise model, each printed under a SYNTHETIC banner and refused by the functions that expect measured data. roro_platform.solve ranks the plans of a yes/no business decision — every plan up to 20 options, a heuristic search above that, and it says which — then optionally samples the decision with QAOA and re-scores every sampled plan exactly. Against an exact ranking the sampling can confirm it or fall short of it, never beat it; against a heuristic one it can find a better plan, and the comparison says the heuristic missed it.
#Python REST client (roro-quantum, deprecated)
The original SDK wraps the REST API with a small, Qiskit-friendly client. It's open source and published on PyPI. It is deprecated in favour of the platform SDK: it keeps working and gets no new features. Its last release, 0.6.0, also says so with a DeprecationWarning; the 0.4.1 on PyPI today predates that warning, and 0.6.0 is published after the platform SDK.
PyPI · roro-quantum roro-quantum 0.4.1 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"])
Real hardware is asynchronous — price first, then watch the run live, or stop it:
# what would this cost? (nothing is charged) q = roro.quote("iqm.emerald", shots=1000, circuit=qc) print(q["costCredits"], q["sufficient"]) job = roro.submit_run("iqm.emerald", shots=1000, circuit=qc) # comes back "queued" # stream every change over SSE — one connection, no poll loop for r in roro.stream_run(job["id"]): print(r["status"], r.get("queuePosition"), r.get("providerStatus")) # or block with a progress line, or cancel (the provider job stops, the charge is refunded) done = roro.wait_for_run(job["id"], timeout=600, progress=True) roro.cancel_run(job["id"]) # also: submit_batch([...]), machine_properties(target), request_refund(id), ai_chat(messages)
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 ${if (it.free) "free" else "${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}") }
Also on the same client: quote(target, shots, qasm) (price without submitting), submitBatch(listOf(RunSpec(…))) (1–20 independent runs), machineProperties(target), requestRefund(id), and aiChat(listOf(ChatMessage("user", "…"))) / streamAiChat(…) for a metered RoRo Copilot turn. A circuit is mandatory: submitRun throws before touching the network when qasm is null or blank.
Types: Machine(targetId, displayName, provider, kind, qubits, costPerShot, online, status, queueDepth, type, modality, connectivity, nativeGates, availability, analog) and Run(id, machineTargetId, machineName, shots, status, cost, counts, probabilities, error, createdAt, qasm, providerJobId, providerStatus, queuePosition, refundable, refundStatus), plus Quote, MachineProperties, BatchRunResponse, RefundRequest, ChatUsage. Any non-2xx response throws RoRoException(status, message), and roro.raw("/v1/…") is the escape hatch for any other GET endpoint.
#JavaScript / TypeScript SDK
An isomorphic Node and browser client for the same REST API — one class, RoRoClient, on the runtime's own fetch, typed end to end, with optional React hooks under @roroquantum/sdk/react. It authenticates with the same qcs_live_… API key; circuits are submitted as OpenQASM 2.0 strings.
sdks/js (npm install && npm pack) and install that file, or request a copy via support. The API below is the real, current surface and maps to the REST endpoints one-to-one — and it's a couple of fetch calls to the REST API if you'd rather not wait.import { RoRoClient } from "@roroquantum/sdk"; 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 }
Also on the same client: machines() and machineProperties(target), quote(target, { shots, qasm }) (price without submitting), submitRun / submitBatch (1–20 independent runs), waitForRun(id) and streamRun(id, { onUpdate }) (SSE), cancelRun(id), requestRefund(id), runs({ limit, offset }), and aiChat / streamAiChat for a metered RoRo Copilot turn. Every waiting call takes an AbortSignal. A circuit is mandatory: submitRun throws before touching the network when qasm is missing or blank. Non-2xx responses throw RoRoError (with .status); idempotent GETs retry a network blip or a 5xx, POSTs never do, so a run is never double-submitted.
In React, @roroquantum/sdk/react adds useRoRo, useMachines and useRun over the same client.
#AI assistants (MCP)
RoRo Quantum runs a public Model Context Protocol server, so ChatGPT, Claude, Cursor, VS Code and any other MCP client can read RoRo Academy lessons and the machine catalogue while you chat. It is read-only: no account, no API key, and it never runs a circuit or charges anything.
https://api.roroquantum.com/mcp
Transport: Streamable HTTP. Authentication: none. Tools:
| Tool | What it does |
|---|---|
search_lessons | Finds Academy lessons by topic — superposition, Bell states, Grover, error mitigation — in English, Turkish or Arabic. |
get_lesson | Reads one lesson as text: explanations, formulas and example circuits, with a link to the interactive lesson. Quiz answers are never returned. |
list_learning_tracks | The Academy's tracks and modules with lesson counts. |
list_quantum_computers | The simulators and quantum hardware on RoRo Quantum: qubits, technology, connectivity and live status. |
about_roro_quantum | What RoRo Quantum offers, with links. |
Claude
On claude.ai or Claude Desktop: Settings → Connectors → Add custom connector, name it RoRo Quantum and paste the URL above. In Claude Code:
claude mcp add --transport http roro-quantum https://api.roroquantum.com/mcp
ChatGPT
Until the RoRo Quantum plugin appears in ChatGPT's plugin directory, turn on developer mode in ChatGPT's app settings and create a connector with the URL above and no authentication.
Cursor and VS Code
Cursor — ~/.cursor/mcp.json:
{
"mcpServers": {
"roro-quantum": { "url": "https://api.roroquantum.com/mcp" }
}
}VS Code — .vscode/mcp.json:
{
"servers": {
"roro-quantum": { "type": "http", "url": "https://api.roroquantum.com/mcp" }
}
}Then ask, for example: “Teach me Grover's algorithm with a RoRo Quantum lesson” or “Which quantum computers can I use on RoRo Quantum?”. What the server receives is described in the privacy policy; for anything else, contact us.
#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. |
| Resilience | Optional error mitigation on a run (readout mitigation, zero-noise extrapolation) — billed as shot-equivalents, quoted first. It is not error correction. See Resilience. |
| 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. |