> For the complete documentation index, see [llms.txt](https://docs.bugbug.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.bugbug.io/integrations/public-api.md).

# Public API

Run and manage BugBug tests from your own scripts and CI: base URL, API token, running a test or suite and waiting for the result, statuses, pagination, errors and limits.

The BugBug Public API lets you run and manage tests from your own scripts, CI/CD pipelines and internal tools. Use it when you need more control than the [CLI](/integrations/cli.md) or an incoming webhook gives you.

{% hint style="info" %}
**Quick facts**

* Base URL: `https://api.bugbug.io/v2/`
* Authentication: `Authorization: Token <api-token>`, with the project API token from **Integrations → API Token**.
* JSON with `camelCase` field names.
* Rate limit: 5 requests per second per token.
* Available on the **Business** plan. See [pricing](https://bugbug.io/pricing/).
* Endpoint reference: [GitBook](broken://pages/a6db6c569698f717f625a369380fe1e5e750b1bf), [Swagger](https://app.bugbug.io/docs/swagger/?version=v2), [Redoc](https://app.bugbug.io/docs/redoc/?version=v2).
  {% endhint %}

### Base URL and versions

All requests go to:

```
https://api.bugbug.io/v2/
```

Use API v2. API v1 is deprecated and only kept for existing integrations.

### Get your API token

1. Open your project in BugBug.
2. Go to **Integrations → API Token**.
3. Copy the **API Token**.

The token belongs to one project. Every request made with it works on that project, so you don't need to pass a project ID. To work with several projects, use one token per project.

Treat the token like a password. Keep it in your CI's secret store, not in the repository.

### Get a new API token

You can't regenerate or rotate the token in the app. If the token has leaked or you need a new one, contact BugBug support through the [contact form](https://bugbug.io/contact/) or at <support@bugbug.io>, and we'll generate a new token for the project. Then update the token everywhere you use it, for example in your CI secrets.

### Authenticate requests

Send the token in the `Authorization` header with the `Token` prefix:

```bash
curl \
  -H "Authorization: Token $BUGBUG_API_TOKEN" \
  "https://api.bugbug.io/v2/tests/"
```

### Request and response format

* Requests and responses are JSON. Field names are `camelCase`, for example `testId` and `runProfileId`. The API also accepts `snake_case` in request bodies.
* IDs are UUIDs.
* Each endpoint path ends with a slash, for example `/v2/testruns/`.

***

### Run a test and wait for the result

This is the typical CI flow: start a run in the cloud, check its status until it finishes, then read the result.

**1. Find the test ID.** List tests and take the `id` of the one you want:

```bash
curl -H "Authorization: Token $BUGBUG_API_TOKEN" \
  "https://api.bugbug.io/v2/tests/"
```

**2. Start the run.**

```bash
curl -X POST \
  -H "Authorization: Token $BUGBUG_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"testId": "8f0c2a4e-1b7d-4c5e-9a3f-2d6b1e0c7a91"}' \
  "https://api.bugbug.io/v2/testruns/"
```

The response describes the new run:

```json
{
  "id": "3c9e7b21-5d4a-4f8e-b6c0-91a2d7e4f350",
  "status": "queued",
  "finishedSteps": 0,
  "totalSteps": 12,
  "duration": null,
  "modified": null,
  "tags": [],
  "webappUrl": "https://app.bugbug.io/organizations/.../runs-history/tests/3c9e7b21-.../"
}
```

`webappUrl` opens the run in the app, which is useful to print in CI logs.

**3. Check the status** every few seconds until it's final:

```bash
curl -H "Authorization: Token $BUGBUG_API_TOKEN" \
  "https://api.bugbug.io/v2/testruns/3c9e7b21-5d4a-4f8e-b6c0-91a2d7e4f350/status/"
```

The response has the same shape as in step 2. `finishedSteps` and `totalSteps` show the progress.

**4. Read the result.** The full run, with each step's result, browser and error code:

```bash
curl -H "Authorization: Token $BUGBUG_API_TOKEN" \
  "https://api.bugbug.io/v2/testruns/3c9e7b21-5d4a-4f8e-b6c0-91a2d7e4f350/"
```

For CI test reporting, download the JUnit report from `/v2/testruns/{id}/report/junit/`.

#### Run options

| Field          | Value                                                                                                                                           |
| -------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |
| `testId`       | Required. The test to run.                                                                                                                      |
| `runProfileId` | The [profile](/editing-tests/variables.md) to run with. If omitted, the test's default profile is used. List profiles with `GET /v2/profiles/`. |
| `variables`    | Values to override for this run only, as a list of `{"key": ..., "value": ...}`. Each key must already exist as a variable in the project.      |
| `tags`         | [Tag](/organizing-tests/tags.md) names to assign to the run. Tags that don't exist yet are created.                                             |

```json
{
  "testId": "8f0c2a4e-1b7d-4c5e-9a3f-2d6b1e0c7a91",
  "runProfileId": "a1b2c3d4-0000-4000-8000-000000000001",
  "variables": [
    { "key": "email", "value": "qa+ci@example.com" }
  ],
  "tags": ["ci", "release-2.4"]
}
```

#### Run a suite

Suites work the same way, with `suiteId` instead of `testId`:

| Step         | Request                                                                                  |
| ------------ | ---------------------------------------------------------------------------------------- |
| Start        | `POST /v2/suiteruns/` with `{"suiteId": ...}` and the same optional fields as a test run |
| Check status | `GET /v2/suiteruns/{id}/status/`, with `finishedTests` and `totalTests`                  |
| Read result  | `GET /v2/suiteruns/{id}/`, with the result of each test                                  |
| JUnit report | `GET /v2/suiteruns/{id}/report/junit/`                                                   |
| Stop         | `POST /v2/suiteruns/{id}/stop/`                                                          |

A running test is stopped with `POST /v2/testruns/{id}/stop/`.

#### Run statuses

| Status    | Meaning                                                   | Final |
| --------- | --------------------------------------------------------- | ----- |
| `queued`  | Waiting for a free cloud browser.                         | no    |
| `running` | In progress.                                              | no    |
| `passed`  | All steps passed.                                         | yes   |
| `failed`  | A step failed. See the step results for the error code.   | yes   |
| `error`   | The run ended with an error before it could be completed. | yes   |
| `stopped` | Stopped by a user or through the API.                     | yes   |
| `skipped` | Not run.                                                  | yes   |

Stop polling when the status is final. In CI, treat `passed` (and `skipped`, if you use it) as success and everything else as failure.

***

### Pagination

List endpoints return one page at a time:

```json
{
  "count": 42,
  "next": "https://api.bugbug.io/v2/testruns/?page=2",
  "previous": null,
  "page": 1,
  "results": [ ... ]
}
```

* `page` selects the page, starting at 1.
* `pageSize` sets the number of items per page: 10 by default, up to 100.
* Follow `next` until it's `null` to read everything.

Run lists can be filtered, for example `GET /v2/testruns/?testId=<id>&status=failed`. The [endpoint reference](broken://pages/a6db6c569698f717f625a369380fe1e5e750b1bf) lists the filters for each endpoint.

### Errors

Errors return an HTTP status code and a JSON body with a `message` and a `code`. Validation errors are grouped by field:

```json
[
  {
    "runProfileId": [
      { "message": "Run profile not found.", "code": "invalid", "params": {} }
    ]
  }
]
```

| HTTP status | Meaning                                                                                                                                                                    |
| ----------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `400`       | The request is invalid: a missing field, an unknown ID, a variable that doesn't exist (`cantOverrideUndefinedVariables`), or a full run queue (`runningQueueIsFullError`). |
| `401`       | The token is missing or invalid.                                                                                                                                           |
| `402`       | The plan doesn't include API access (`noApiAccess`), or the action isn't available on your plan.                                                                           |
| `403`       | The token has no access to this resource, or the project is inactive.                                                                                                      |
| `404`       | The resource doesn't exist in this project.                                                                                                                                |
| `429`       | Too many requests: more than 5 per second with this token. Wait a moment and retry.                                                                                        |

### Limits

* **Request rate.** Up to 5 requests per second per token. When you exceed the limit, the API returns `429`. Retry after a short pause, and poll run statuses every few seconds rather than continuously. The test inbox endpoints have a lower limit.
* **Cloud run queue.** An organization can have up to 1000 tests queued or running in the cloud at once. A run that would go over this limit is rejected with `runningQueueIsFullError`. A suite run counts every test in the suite.
* **Run time.** A single cloud test run can take up to 30 minutes on the Business plan.

***

### What you can do with the API

* List, create, update and delete tests, suites, groups, steps and reusable components.
* Run tests and suites in the cloud, check their status, stop them and read the results.
* Download JUnit reports, screenshots, logs and debug artifacts of runs.
* Read and update notes on test runs and suite runs.
* Manage [tags](/organizing-tests/tags.md) and assign them to tests, suites and runs.
* Manage variables and profiles.
* [Export and import](/export-import-and-backup/import-export.md) tests and projects as YAML or ZIP.
* Read and update project settings.
* Manage visual regression reference screenshots.
* Manage the project [Files](/organizing-tests/files.md) library: upload, rename, move, download and delete files used by upload steps.
* Read and clear the [Testing Inbox](/editing-tests/testing-inbox.md) of a test run.

### Public API, CLI or webhooks?

| Use                             | When                                                                                 |
| ------------------------------- | ------------------------------------------------------------------------------------ |
| **Public API**                  | You're building a custom integration and need full programmatic control.             |
| [**CLI**](/integrations/cli.md) | You want a ready-made command in a CI/CD script, without writing API calls yourself. |
| **Incoming webhooks**           | Another tool only needs to start a test or suite with one `POST` request.            |
| **Outgoing webhooks**           | BugBug should send run events to another system.                                     |

***

### FAQ

<details>

<summary>Where do I find my API token, and is it per project or per user?</summary>

In **Integrations → API Token** in your project. The token belongs to the project, not to a user. Use a separate token for each project.

</details>

<details>

<summary>How do I rotate or regenerate my API token?</summary>

Not in the app. Contact BugBug support through the [contact form](https://bugbug.io/contact/) or at <support@bugbug.io>, and we'll generate a new token for the project. Then update it in your CI secrets and other integrations. See [Get a new API token](#get-a-new-api-token).

</details>

<details>

<summary>Which plan includes the Public API?</summary>

The Business plan. Requests on other plans return `402` with the code `noApiAccess`.

</details>

<details>

<summary>What is the API rate limit?</summary>

5 requests per second per token. Over that, the API returns `429`; wait a moment and retry. The test inbox endpoints have a lower limit.

</details>

<details>

<summary>How do I run a test with a different profile or different variable values?</summary>

Pass `runProfileId` to pick the profile, and `variables` to override single values for that run. A variable you override must already exist in the project; otherwise the API returns `cantOverrideUndefinedVariables`.

</details>

<details>

<summary>How do I know when a run has finished?</summary>

Poll `GET /v2/testruns/{id}/status/` (or `/v2/suiteruns/{id}/status/`) every few seconds until `status` is `passed`, `failed`, `error`, `stopped` or `skipped`.

</details>

<details>

<summary>Should I use API v1 or v2?</summary>

Use v2: `https://api.bugbug.io/v2/`. API v1 is deprecated.

</details>


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.bugbug.io/integrations/public-api.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
