"type": "ionq.qasm3.v1" and put your program in input.data, and our compiler will optimize it into IonQ native gates just as it does for circuits submitted in our JSON circuit format.
IonQ supports a subset of OpenQASM 3, not the entire language. This page states exactly which constructs are accepted. Anything outside the subset is rejected, with an error naming the construct — your circuit will not be silently approximated.
This page is written for submitting jobs directly to the IonQ API. If you build your circuits in an SDK such as Qiskit or Cirq, the SDK constructs and submits the job payload for you and you do not need to write OpenQASM 3 by hand.
failed with the reason in its failure field. A circuit that fails validation never reaches a QPU and consumes no execution time.
Language version
WriteOPENQASM 3.0; as your first statement. That is the conventional header and it is what every example on this page uses.
The version number is not currently validated, however, and IonQ does not target a specific OpenQASM 3 minor revision. A program declaring OPENQASM 3.1; or OPENQASM 3.2; is neither rejected for being too new nor given access to anything extra, and a program with no header at all compiles the same way. The tables on this page are the whole answer to what IonQ accepts — the version header does not widen or narrow it.
One consequence is worth stating explicitly: writing OPENQASM 2.0; does not put the compiler into an OpenQASM 2 mode. The header is ignored and the body is read as the same subset described here. In practice a simple OpenQASM 2 program often still compiles, because the subset accepts qreg, creg, and measure q[0] -> c[0]; alongside their OpenQASM 3 spellings. What it will not accept is a gate that is missing from the tables below — including much of qelib1.inc, such as ccx and u3 — regardless of which header the program declares.
Before you begin
You’ll need an IonQ Quantum Cloud account and an API key stored asIONQ_API_KEY. See our guide to managing API keys if you haven’t set one up.
Submitting a job
input.data holds the OpenQASM 3 program as a single string. The qubit count is read from the program itself, so there is nothing else to declare. This example prepares a Bell state:
Supported gates
IonQ recognizes the fixed list of gate names below. Anything else fails withUnknown gate: <name> unless you define it yourself.
Single-qubit gates
Multi-qubit gates
Gate modifiers
A controlled gate must have exactly one target qubit, and only one modifier may be applied to a gate.
Defining your own gates
Thegate keyword works, and is the supported way to use an operation that isn’t in the tables above. Definitions are inlined at the call site:
def subroutines are a separate feature and are not supported.
Supported language constructs
Declarations
Measurement and reset
Every measurement needs its own classical bit. Assigning two measurements to the same bit is rejected:
bit[1] m; bit[2] c;.
Definitions and aliases
Barrier, timing, and directives
Expressions
Gate angles are resolved at compile time and must be constant.Input and output parameters
OpenQASM 3 definesinput and output directives for supplying parameters to a program and for naming the values it returns. Neither is supported. Both fail preflight validation as an unrecognized statement:
- Instead of
input, substitute the concrete parameter values into the program before submitting. Gate angles must be constant expressions in any case — see Expressions. - Instead of
output, declare ordinarybitregisters and measure into them. The job’s results report the final measured state of the qubits — see Reading results.
Reading results
Results do not come back in the OpenQASM program. Read them from theionq.result.probabilities.json.v2 artifact, which a completed job lists in its results object from the Get job endpoint:
id from the artifact endpoint. It returns the measured distribution keyed by bitstring — a 100-shot run of the Bell program above returned:
q[0] is the leftmost character of the bitstring. A three-qubit circuit applying x q[0]; and measuring all three returns {"100": 1.0}.output_all is a fixed key holding the final state across every qubit in the circuit. Alongside it, each classical register you declare appears under its own name. The mid-circuit measurement example below declares bit[1] m; and bit[2] c; over two qubits, so its results carry three registers:
output_all reports the qubits rather than what you measured into, it can differ from the classical registers when a circuit measures mid-circuit, as it does here. The ionq.result.histogram.json.v2 and ionq.result.shots.json.v2 artifacts carry the same registers, as counts and as per-shot bit arrays.
See Results formats for the full catalog of result artifact formats.
Mid-circuit measurement
Mid-circuit measurement (MCM) means measuring a qubit and then continuing to operate on other qubits. This is supported, but not on every target — it is a hardware capability, so availability depends on the backend you choose. The simulator supports it. Targets that do not will fail the job:simulator unless you have confirmed your target accepts MCM.
This example measures q[0], then continues working with q[1], giving each measurement its own classical bit:
Measuring mid-circuit is fine. Branching on the result is not — see Loops and branches.
Loops and branches
These constructs are part of the OpenQASM 3 language and the compiler accepts them, so they are caught by the capability check in preflight rather than by the language validator. The job fails before execution and costs nothing.for loops are compiled into runtime loops rather than being unrolled, which is why an ordinary loop over gates is affected too.
Unroll your loops and resolve your branches before submitting. Instead of:
continue, end, for loops that do not start at 0 or step by 1, for loops over a discrete set, and while conditions using <, <=, >, or >=. Since none of these run in any case, this only matters when you are interpreting an error message.
Examples
Bell state
GHZ state
Toffoli
ccx is not a recognized gate name — use the control modifier instead:
Common errors
Every error below arrives the same way: the API accepts your submission and returns a job ID, then preflight validation fails the job before it executes. Read the job’sfailure field to see which one you hit.
Language and subset violations come back as PreflightError, with the specific problem in the message — for example, Circuit failed preflight validation: Unknown gate: ccx.
These two are target capability checks rather than language errors, so they come back as
UnsupportedFeature on a circuit that compiled successfully:

