> ## Documentation Index
> Fetch the complete documentation index at: https://docs.ionq.com/llms.txt
> Use this file to discover all available pages before exploring further.

# OpenQASM 3

> Submitting circuits to IonQ using the OpenQASM 3 language

[OpenQASM 3](https://openqasm.com/) is a supported input format for IonQ jobs. Submit a job with `"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](/api-reference/v0.4/jobs/create-job).

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.

<Note>
  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.
</Note>

All of the restrictions on this page are checked during **preflight validation**, server-side, before your circuit is executed. Submitting is a separate step: the API accepts your request and returns a job ID, then the job moves to `failed` with the reason in its `failure` field. A circuit that fails validation never reaches a QPU and consumes no execution time.

<Warning>
  Two restrictions apply on top of everything below. Neither is a language limitation — both are target capability checks, caught in preflight before execution.

  * **Mid-circuit measurement** is supported on some targets and not others. See [Mid-circuit measurement](#mid-circuit-measurement).
  * **Dynamic circuit execution — loops and branches — is not supported on any target**, including the simulator. See [Loops and branches](#loops-and-branches).
</Warning>

***

## Language version

Write `OPENQASM 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](https://cloud.ionq.com) account and an API key stored as `IONQ_API_KEY`. See our [guide to managing API keys](/guides/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:

<CodeGroup>
  ```bash curl theme={null}
  curl --location 'https://api.ionq.co/v0.4/jobs' \
    --header "Authorization: apiKey $IONQ_API_KEY" \
    --header 'Content-Type: application/json' \
    --data '{
      "type": "ionq.qasm3.v1",
      "name": "bell state",
      "backend": "simulator",
      "shots": 500,
      "input": {
        "data": "OPENQASM 3.0;qubit[2] q;bit[2] c;h q[0];cx q[0], q[1];c[0] = measure q[0];c[1] = measure q[1];"
      }
    }'
  ```

  ```python Python theme={null}
  import os
  import requests

  program = """
  OPENQASM 3.0;
  qubit[2] q;
  bit[2] c;
  h q[0];
  cx q[0], q[1];
  c[0] = measure q[0];
  c[1] = measure q[1];
  """

  response = requests.post(
      "https://api.ionq.co/v0.4/jobs",
      headers={"Authorization": f"apiKey {os.environ['IONQ_API_KEY']}"},
      json={
          "type": "ionq.qasm3.v1",
          "name": "bell state",
          "backend": "simulator",
          "shots": 500,
          "input": {"data": program},
      },
  )
  print(response.json())
  ```
</CodeGroup>

<Tip>
  Submit with `"dry_run": true` to compile your circuit without spending QPU time. Once the job reports `"status": "completed"`, the compiled circuit is listed under `output.compilation.compiled_circuits` in several formats — including `ionq.qasm3.v1`, so you can read your compiled circuit back as OpenQASM 3.
</Tip>

***

## Supported gates

IonQ recognizes the fixed list of gate names below. Anything else fails with `Unknown gate: <name>` unless you [define it yourself](#defining-your-own-gates).

<Warning>
  `include "stdgates.inc";` does **not** determine which gates are available. It is optional — your program is valid without it — and including it neither adds nor removes gates. It is accepted because SDK exporters such as Qiskit's emit it automatically, so their output can be submitted unchanged.

  Do not read the include as a statement of support. Several gates that belong to `stdgates.inc` are not available on IonQ, including `ccx`, `ch`, `cp`, `crx`, `cry`, `crz`, `cswap`, and `cu`. The tables below are the authoritative list.
</Warning>

### Single-qubit gates

| Gate                      | Aliases | Supported                                                            |
| ------------------------- | ------- | -------------------------------------------------------------------- |
| `x`                       | `not`   | Yes                                                                  |
| `y`                       |         | Yes                                                                  |
| `z`                       |         | Yes                                                                  |
| `h`                       |         | Yes                                                                  |
| `s`                       |         | Yes                                                                  |
| `sdg`                     | `si`    | Yes                                                                  |
| `t`                       |         | Yes                                                                  |
| `tdg`                     | `ti`    | Yes                                                                  |
| `sx`                      | `v`     | Yes                                                                  |
| `vdg`                     | `vi`    | Yes                                                                  |
| `rx(θ)`, `ry(θ)`, `rz(θ)` |         | Yes                                                                  |
| `p(λ)`                    |         | Yes                                                                  |
| `id`                      |         | Yes                                                                  |
| `xphase(θ)`               |         | Yes — IonQ extension, equivalent to `rx(θ)`                          |
| `yphase(θ)`               |         | Yes — IonQ extension, equivalent to `ry(θ)`                          |
| `gphase(γ)`               |         | Partial — accepted and ignored; global phase does not affect results |
| `sxdg`                    |         | No — use `vdg`                                                       |
| `U(θ,φ,λ)`                |         | No                                                                   |
| `u1`, `u2`, `u3`          |         | No — these are OpenQASM 2 compatibility gates                        |

### Multi-qubit gates

| Gate                                                      | Aliases | Supported               |
| --------------------------------------------------------- | ------- | ----------------------- |
| `cx`                                                      | `cnot`  | Yes                     |
| `cy`                                                      |         | Yes                     |
| `cz`                                                      |         | Yes                     |
| `swap`                                                    |         | Yes                     |
| `ccx`                                                     |         | No — use `ctrl(2) @ x`  |
| `crz(θ)`                                                  |         | No — use `ctrl @ rz(θ)` |
| `ch`, `cp(λ)`, `crx(θ)`, `cry(θ)`, `cswap`, `cu(θ,φ,λ,γ)` |         | No                      |

### Gate modifiers

| Modifier              | Supported                                                      |
| --------------------- | -------------------------------------------------------------- |
| `ctrl @`, `ctrl(n) @` | Partial — only on `x`/`not`, `z`, `rz`, `xphase`, and `yphase` |
| `negctrl @`           | No                                                             |
| `inv @`               | No                                                             |
| `pow(k) @`            | No                                                             |

A controlled gate must have exactly one target qubit, and only one modifier may be applied to a gate.

<Tip>
  The `ctrl @` modifier covers some of the missing controlled gates but not all of them — its base gate must be `x`, `z`, `rz`, `xphase`, or `yphase`, so `ctrl @ h` fails. For anything else, wrap your own decomposition in a [`gate` definition](#defining-your-own-gates).
</Tip>

### Defining your own gates

The `gate` 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:

```
gate mych a, b { h b; ctrl @ z a, b; h b; }
mych q[0], q[1];
```

Argument and qubit counts must match the definition exactly. `def` subroutines are a separate feature and are not supported.

***

## Supported language constructs

### Declarations

| Construct                                            | Supported                                                            |
| ---------------------------------------------------- | -------------------------------------------------------------------- |
| `qubit[n] q;`                                        | Yes                                                                  |
| `qreg q[n];`                                         | Yes                                                                  |
| `bit[n] c;`                                          | Yes                                                                  |
| `creg c[n];`                                         | Yes                                                                  |
| `qubit q;` (scalar)                                  | No — use `qubit[1] q;`                                               |
| `bit c;` (scalar)                                    | No — use `bit[1] c;`                                                 |
| `int i = 3;`                                         | Partial — declared, but the initial value is discarded               |
| `uint`, `float`, `bool`, `angle`, `complex`, `array` | No                                                                   |
| `duration`, `stretch`                                | No                                                                   |
| `const`                                              | No                                                                   |
| `input` / `output` parameters                        | No — see [Input and output parameters](#input-and-output-parameters) |

### Measurement and reset

| Construct                         | Supported                                      |
| --------------------------------- | ---------------------------------------------- |
| `c[i] = measure q[j];`            | Yes                                            |
| `measure q[j] -> c[i];`           | Yes — the OpenQASM 2 spelling is also accepted |
| `reset q[j];`                     | Yes                                            |
| `c = measure q;` (whole register) | No — measure one bit at a time                 |
| `measure q[j];` (no destination)  | No                                             |
| `reset q;` (whole register)       | No — reset one qubit at a time                 |

Every measurement needs its own classical bit. Assigning two measurements to the same bit is rejected:

```
IonQ hardware does not support reusing classical measurement bit c[0].
This circuit assigns another measurement result to c[0] at line 6, column 1.
The first assignment to c[0] was at line 5, column 1.
Use a separate classical bit for each measurement when targeting IonQ hardware.
```

Declaring several registers is the usual fix: `bit[1] m; bit[2] c;`.

### Definitions and aliases

| Construct                               | Supported                                                                    |
| --------------------------------------- | ---------------------------------------------------------------------------- |
| `gate name(params) qubits { … }`        | Yes — inlined at the call site; argument and qubit counts must match exactly |
| `let a = q[0];`                         | Yes                                                                          |
| `let a = q[0] ++ q[1];` (concatenation) | No                                                                           |
| `def` subroutines                       | No                                                                           |
| `extern`                                | No                                                                           |

### Barrier, timing, and directives

| Construct                           | Supported                                                            |
| ----------------------------------- | -------------------------------------------------------------------- |
| `barrier q;`                        | Yes                                                                  |
| `barrier q[0];` (partial)           | Partial — accepted, but widened to a full-circuit barrier            |
| `delay[100ns]`, `box`, `durationof` | No                                                                   |
| `defcal`, `cal`                     | No                                                                   |
| `#pragma`                           | No                                                                   |
| `input` / `output`                  | No — see [Input and output parameters](#input-and-output-parameters) |
| `@annotation`                       | Partial — accepted and ignored                                       |
| `include "…"`                       | Partial — accepted and ignored                                       |

### Expressions

Gate angles are resolved at compile time and must be constant.

| Expression                       | Supported                                            |
| -------------------------------- | ---------------------------------------------------- |
| `pi`                             | Yes — the only named constant                        |
| `pi/2`, `2*pi`                   | Yes — `*` and `/`                                    |
| Literals, unary `-`              | Yes                                                  |
| `pi+1`, `pi-1`                   | No — `+` and `-` are not available in gate arguments |
| A classical variable as an angle | No                                                   |
| `q[0:1]` range indexing          | No — index qubits one at a time                      |
| `q[0] ++ q[1]` concatenation     | No                                                   |

***

## Input and output parameters

OpenQASM 3 defines [`input` and `output` directives](https://openqasm.com/versions/3.0/language/directives.html#input-output) for supplying parameters to a program and for naming the values it returns. **Neither is supported.** Both fail preflight validation as an unrecognized statement:

```
Circuit failed preflight validation: Unrecognized statement: IODeclaration(…)
```

* Instead of `input`, substitute the concrete parameter values into the program before submitting. Gate angles must be constant expressions in any case — see [Expressions](#expressions).
* Instead of `output`, declare ordinary `bit` registers and measure into them. The job's results report the final measured state of the qubits — see [Reading results](#reading-results).

***

## Reading results

Results do not come back in the OpenQASM program. Read them from the `ionq.result.probabilities.json.v2` artifact, which a completed job lists in its `results` object from the [Get job](/api-reference/v0.4/jobs/get-job) endpoint:

```json theme={null}
"results": {
  "ionq.result.probabilities.json.v2": {
    "id": "b1c4c1a9-2670-4fbd-8783-4b265474bd8f",
    "format": "ionq.result.probabilities.json.v2",
    "media_type": "application/json"
  }
}
```

Fetch it by its `id` from the [artifact endpoint](/api-reference/v0.4/jobs/get-job-artifact). It returns the measured distribution keyed by bitstring — a 100-shot run of the Bell program above returned:

```json theme={null}
{ "probabilities": { "registers": { "output_all": { "00": 0.51, "11": 0.49 } } } }
```

<Note>
  `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}`.
</Note>

`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](#mid-circuit-measurement) below declares `bit[1] m;` and `bit[2] c;` over two qubits, so its results carry three registers:

```json theme={null}
{ "probabilities": { "registers": {
  "m": { "1": 1.0 },
  "c": { "10": 0.52, "11": 0.48 },
  "output_all": { "10": 0.52, "00": 0.48 }
} } }
```

Since `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](/api-reference/v0.4/schemas/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:

```json theme={null}
{
  "code": "UnsupportedFeature",
  "message": "Mid-circuit measurement is not supported on this target"
}
```

MCM circuits are valid OpenQASM 3 and compile successfully for every target, so this is caught by the target capability check in preflight rather than by the language validator. The job fails before execution and costs nothing. Use `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:

```json theme={null}
{
  "type": "ionq.qasm3.v1",
  "name": "mid-circuit measurement",
  "backend": "simulator",
  "shots": 500,
  "input": {
    "data": "OPENQASM 3.0;qubit[2] q;bit[1] m;bit[2] c;x q[0];m[0] = measure q[0];h q[1];c[0] = measure q[0];c[1] = measure q[1];"
  }
}
```

<Note>
  Measuring mid-circuit is fine. *Branching* on the result is not — see [Loops and branches](#loops-and-branches).
</Note>

***

## Loops and branches

<Warning>
  Dynamic circuit execution is **not supported on any IonQ target**, including the simulator, and is not planned for any live system. This covers `if`, `else`, `while`, `switch`, **and `for`** — including a `for` loop that does not depend on any measurement result.

  ```json theme={null}
  {
    "code": "UnsupportedFeature",
    "message": "Dynamic circuit execution (loops, branches) is not supported"
  }
  ```
</Warning>

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:

```
for int i in [0:2] { h q[0]; }
```

write:

```
h q[0];
h q[0];
h q[0];
```

For reference, the compiler additionally rejects `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

```
OPENQASM 3.0;
qubit[2] q;
bit[2] c;
h q[0];
cx q[0], q[1];
c[0] = measure q[0];
c[1] = measure q[1];
```

### GHZ state

```
OPENQASM 3.0;
qubit[3] q;
bit[3] c;
h q[0];
cx q[0], q[1];
cx q[0], q[2];
c[0] = measure q[0];
c[1] = measure q[1];
c[2] = measure q[2];
```

### Toffoli

`ccx` is not a recognized gate name — use the control modifier instead:

```
OPENQASM 3.0;
qubit[3] q;
bit[1] c;
h q[0];
h q[1];
ctrl(2) @ x q[0], q[1], q[2];
c[0] = measure q[2];
```

***

## 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's `failure` 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`.

| Message                                                              | Cause                                                                                                          |
| -------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------- |
| `Unknown gate: <name>`                                               | The gate is not supported and was not defined with `gate`. Including `stdgates.inc` does not make it available |
| `Unknown gate for multi-control: <name>`                             | `ctrl @` was applied to a gate other than `x`, `z`, `rz`, `xphase`, or `yphase`                                |
| `Expected ctrl modifier, got …`                                      | `inv`, `pow`, or `negctrl` was used                                                                            |
| `IonQ hardware does not support reusing classical measurement bit …` | Two measurements write to the same classical bit                                                               |
| `Expected Bit or IntType classical register, not …`                  | A classical type other than `bit` or `int` was declared                                                        |
| `Unrecognized statement: …`                                          | The construct is outside the supported subset                                                                  |
| `Unrecognized statement: IODeclaration(…)`                           | The program declares an `input` or `output` parameter                                                          |
| `for_loop start must be 0`                                           | A `for` range does not start at 0 or does not step by 1                                                        |
| `Unimplemented op +`                                                 | A gate angle uses `+` or `-`                                                                                   |
| `Pragmas not supported`                                              | The program contains a `#pragma`                                                                               |
| `Expected to find attribute size in …`                               | A scalar `qubit q;` or `bit c;` was declared instead of a sized register                                       |

These two are target capability checks rather than language errors, so they come back as `UnsupportedFeature` on a circuit that compiled successfully:

| Failure                                                                            | Cause                                                                    |
| ---------------------------------------------------------------------------------- | ------------------------------------------------------------------------ |
| `UnsupportedFeature: Dynamic circuit execution (loops, branches) is not supported` | The circuit contains `if`, `else`, `while`, `switch`, or `for`           |
| `UnsupportedFeature: Mid-circuit measurement is not supported on this target`      | The circuit measures and then continues, on a target without MCM support |
