---
title: Dry-run jobs
description: Use dry-run jobs to get the usage in resource units and service-side validation a job would incur on a real QPU without spending QPU time.
source: https://quantum.cloud.ibm.com/docs/en/guides/dry-run-jobs
---

# Dry-run jobs

A dry-run job runs your workload through IBM Quantum® Compute Service without using any QPU time. Use a dry-run job to do the following before you commit to a real run:

- \*\*Get the usage that the job would incur on the real QPU.
- **Validate the job.** Quantum Compute checks the job's parameters, the same way it does for a real job.
- **Get the [circuit timing schedule](/docs/guides/qiskit-runtime-circuit-timing)**, if you request it, to debug the job before you run it for real.

Unlike dry-run jobs, local testing (fake backends and [Qiskit Aer](/docs/guides/simulate-with-qiskit-aer)) runs on your own machine and therefore cannot provide usage numbers, service-side validation, or service timing information. To compare the two approaches, see [Mock devices compared to fake backends](#mock-vs-fake).

> **Note**
>
> - `qiskit-ibm-runtime` v0.51.0 or later is required for dry-run jobs.
> - Dry-run jobs are available in all regions, and are only available on the Flex, Premium, and On-Prem plans. They are not available on the Open or Pay-As-You-Go plans.

## How it works

To run a dry-run job, pass `dry_run=True` to the primitive's `run()` method:

```python
backend = service.backend("ibm_phoenix")
isa_circuit = pm.run(circuit)  # transpiled for ibm_phoenix
sampler = Sampler(mode=backend)
job = sampler.run([isa_circuit], dry_run=True)
job.result()  # wait for completion
print(job.usage())
```

`dry_run` is an argument of the `run()` method (the default is `False`), not a runtime option. It is available to all primitives.

A dry-run job has the following characteristics:

- It runs in IBM Quantum Compute Service in the cloud, not on your machine.
- It uses the calibration data from the real QPU, not just a snapshot.
- Its results keep the expected schema and follow the expected shape of results from the real QPU, but contain randomized mock data. They are not actual or simulated measurement results, so do not use them to check physical correctness.
- Processing happens on the server, so the job might not finish immediately.

Circuit size, shots, and other limits are the same as for any job of that program.

`dry_run=True` also works when the backend is already a mock device.

## Read the usage

The usage of a completed dry-run job is the usage the same job would incur on the real QPU, except in rare situations where calibration data changes significantly after the dry-run job and before the job on a real QPU.

- `job.usage()` returns the usage in seconds.
- `job.metrics()["usage"]` returns the same data together with the usage status.

Dry-run usage is not charged, does not consume your plan allocation, and does not count against any limits.

> **Do not use job.usage\_estimation with dry-run jobs**
>
> Do not use `job.usage_estimation` on a dry-run job. Use `job.usage()`.

`job.usage()` returns `0` until the usage calculation is final, so wait for the job to finish before you read it.

## Circuit timing schedule

To request the circuit timing schedule, use the same option as for a real job. For full details, see [Visualize circuit timing](/docs/guides/qiskit-runtime-circuit-timing).

## Limitations

- **Job and batch modes only.** Dry-run jobs can only run in [job mode](/docs/guides/execution-modes#job-mode) and [batch mode](/docs/guides/execution-modes#batch-mode).
- **Flex, Premium, and On-Prem plans only.** Dry-run jobs are not available on the Open or Pay-As-You-Go plans.

## Mock devices

Mock devices are the implementation behind dry-run jobs. Dry-run jobs hide mock devices by default, but you encounter them in these cases:

- **Instances configured to access only mock devices.** Example: if you use `service.backend("ibm_phoenix")` to submit a dry-run job on `ibm_phoenix`, but your instance doesn't have access to `ibm_phoenix`,  you will get a `QiskitBackendNotFoundError`. Instead, select the mock device directly with `service.backend("mock_phoenix")`.
- **Compute resources.** Mock devices are hidden by default on the [Compute resources](/computers) page. The table on the page has a filter option to include them, but only the Flex, Premium, and On-Prem plans have access to mock devices.
- **Workloads table.** Jobs from mock devices are hidden by default on the [Workloads](/workloads) page. The table has a filter option (the funnel icon) to display them.
- **Python API.** `service.backends()` and `service.jobs()` exclude mock devices unless you call them with `include_mocks=True`. `IBMBackend.is_mock` indicates whether a backend is a mock device. `service.least_busy()` never returns a mock device.

## Mock devices compared to fake backends

The names are similar, but a mock device and a [fake backend](/docs/api/qiskit-ibm-runtime/fake-provider) are different things. A fake backend is a local object in your Python environment. A mock device is a cloud resource in IBM Quantum Compute Service.

|                                                      | Mock device (for example `mock_phoenix`)                                                       | Fake backend (for example `FakeManilaV2`)                                        |
| ---------------------------------------------------- | ---------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------- |
| What it is                                           | A device in IBM Quantum Compute Service                                                        | A Python class in `qiskit_ibm_runtime.fake_provider`                             |
| Where it runs                                        | Cloud, in the service                                                                          | Locally, on your machine                                                         |
| How you get it                                       | `service.backend("mock_phoenix")`, or automatically with `run(..., dry_run=True)`              | `from qiskit_ibm_runtime.fake_provider import ...`                               |
| Account and instance                                 | Specific plans only (Flex, Premium, or On-Prem)                                                | All instances and plans                                                          |
| Calibration                                          | Live, matches the real device at any time                                                      | Fixed snapshot shipped with the installed package                                |
| Results                                              | Randomized data with the expected schema. Not a simulation                                     | Noisy simulation that uses the snapshot's noise model                            |
| Physically meaningful results                        | No. Never use them to check correctness                                                        | Approximately, within the limits of the noise model                              |
| Usage reported                                       | Yes                                                                                            | None                                                                             |
| Charged or counted against plan allocation or limits | No                                                                                             | No                                                                               |
| Job validation                                       | Done by the service, same as a real job                                                        | Limited to what the client checks                                                |
| Timing schedule                                      | Available on request                                                                           | Not available                                                                    |
| Execution modes                                      | Job and batch mode only. Sessions are blocked                                                  | Session syntax accepted but ignored (local mode has no real sessions or batches) |
| Completion                                           | Asynchronous, on the server                                                                    | Runs locally and returns when finished                                           |
| Shown in platform interface and API lists            | Hidden by default (Compute resources, Workloads table, `backends()`, `jobs()`). Opt in to show | Never, because nothing is sent to the service                                    |

### How to choose

- To find out **how much usage the job will consume, or whether the service will accept it**, use a dry-run job.
- To find out **what the output might look like**, or to work offline, use a fake backend or Aer.
- To do both, iterate on circuits and results with a fake backend, then dry-run as the last check before the real QPU.

### Common points of confusion

- **A mock device is not a simulator.** Its output is random, so sampling a Bell circuit on `mock_phoenix` does not return a Bell distribution. If you want realistic noisy output, use a fake backend.
- **A fake backend is not the live device.** Its calibration is a snapshot and might be out of date. A mock device tracks the real calibration, so transpiled circuits and timing match what the QPU sees today.
- **Neither runs on real hardware.** Neither consumes QPU time, but only a dry-run job goes through the service.
- **`usage_estimation` is not the dry-run estimate.** For a dry-run job, `job.usage()` returns the seconds of usage your job would be charged on a real QPU.

## Use cases

### Students and courses

An administrator restricts an instance to mock devices only, using the QPU customization toggle when [creating or editing the instance](/docs/guides/instances#add-instance). Students then validate their payloads end-to-end without consuming real QPU time or RUs. Because the instance has access only to mock devices, students select the backend by its mock name:

```python
backend = service.backend("mock_phoenix")
sampler = Sampler(mode=backend)
job = sampler.run([isa_circuit], dry_run=True)
```

### Project planning

A team about to start a project dry-runs representative jobs to see the RUs the project needs, before choosing a plan or allocation:

```python
backend = service.backend("ibm_phoenix")
sampler = Sampler(mode=backend)
job = sampler.run([isa_circuit], dry_run=True)
job.result()
print(job.usage()) 
```

### Final check before running on a real QPU

Validate parameters and inspect the timing schedule with `dry_run=True`, then drop the argument and run on the real QPU. Nothing else in your code changes:

```python
sampler = Sampler(mode=backend)

# Pre-flight check
job = sampler.run([isa_circuit], dry_run=True)
job.result()

# Real run - same code, without dry_run
job = sampler.run([isa_circuit])
```

## Next steps

> **Recommendations**
>
> - Test programs on your own machine with [Local testing mode](/docs/guides/local-testing-mode).
> - Learn how [workload usage](/docs/guides/estimate-job-run-time) is measured.
> - Visualize the [circuit timing schedule](/docs/guides/qiskit-runtime-circuit-timing).
> - Understand [resource units](/docs/guides/plans-overview#rus) and the available plans.
