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

# Bulk QR creations

> Create up to 100 QR codes at once and download them as a ZIP archive.

> [!NOTE]
>
> The GetQR API is in beta and is available to selected workspaces. Details may change before the general release.

A bulk creation turns a list of names and URLs into website QR codes with one shared design. It runs in the background: start it, poll its progress, then download the ZIP archive.

| Method | Path | Description |
| - | - | - |
| `GET` | `/api/v1/bulk-qr-creations` | [Get the current bulk creation](#get-the-current-bulk-creation) |
| `POST` | `/api/v1/bulk-qr-creations` | [Create from JSON](#create-from-json) |
| `POST` | `/api/v1/bulk-qr-creations/csv` | [Create from a CSV file](#create-from-a-csv-file) |
| `GET` | `/api/v1/bulk-qr-creations/{id}` | [Get a bulk creation](#get-a-bulk-creation) |
| `DELETE` | `/api/v1/bulk-qr-creations/{id}` | [Abort a bulk creation](#abort-a-bulk-creation) |
| `GET` | `/api/v1/bulk-qr-creations/{id}/download` | [Download the archive](#download-the-archive) |

## How it works

**1. Start the bulk creation**

Send your rows with [Create from JSON](#create-from-json) or [Create from a CSV file](#create-from-a-csv-file). The API answers `202` with the bulk creation and its `id`.

**2. Poll its progress**

Call [Get a bulk creation](#get-a-bulk-creation) every few seconds until `status` is `completed` or `failed`.

**3. Download the archive**

When `status` is `completed`, call [Download the archive](#download-the-archive) to get a ZIP of the QR code images.

* **Limit.** Up to 100 rows per bulk creation.
* **One at a time.** Only one bulk creation runs per workspace. Starting another while one is running returns the running one.
* **Idempotency.** Every request needs an `idempotencyKey`. Repeating a request with the same key and the same body returns the bulk creation already started. The same key with a different body returns `422`.
* **Rows.** Each row needs a `name` and a `url`. See [Rows](#rows).
* **Plan.** Bulk creation isn't available on the Single QR plan: the request returns `403 exceeded_limit`.

## Rows

* **Name.** Becomes the QR code's title. Trimmed and cut to 40 characters. Repeated names (ignoring case) get a number: `Menu`, `Menu 2`, `Menu 3`.
* **URL.** Becomes the destination. It must be an `http` or `https` address with a real domain, such as `example.com/menu`, and contain no spaces. A URL without a scheme gets `https://`.
* **Empty rows.** Rows with neither a name nor a URL are skipped. A row with only one of them is rejected.

Every QR code is a `website` QR code on a short link. See [QR types](https://docs.getqr.com/api/guides/qr-types.md).

## Headers

**Authorization** `string` *required*

`Bearer` followed by your API key. See [Authentication](https://docs.getqr.com/api/authentication.md).

**Content-Type** `string`

`application/json` for [Create from JSON](#create-from-json), `multipart/form-data` for [Create from a CSV file](#create-from-a-csv-file).

## The bulk creation object

**id** `string`

ID of the bulk creation, for example `cm2k7x9qb0003ax8eb4f6gh2t`.

**status** `string`

`queued`, `processing`, `packaging` or `completed`. A failed or aborted bulk creation goes through `rollingBack` to `failed`.

**sourceFilename** `string`

Name of the source file.

**totalCount** `integer`

Number of rows.

**createdCount** `integer`

QR codes created so far.

**renderedCount** `integer`

QR code images rendered so far.

**percent** `integer`

Overall progress, 0 to 100. Creating takes up to 40, rendering up to 90, packaging is 95, and `completed` is 100.

**stalled** `boolean`

`true` when a running bulk creation hasn't made progress for 2 minutes. Keep polling: [Get a bulk creation](#get-a-bulk-creation) restarts a stalled bulk creation.

**error** `object | null`

When `status` is `failed`: `code` and `message`. An aborted bulk creation has the code `cancelled_by_user`. A failed bulk creation is rolled back, so none of its QR codes are kept.

**downloadUrl** `string | null`

Path of [Download the archive](#download-the-archive), once `status` is `completed`.

**createdAt** `string`

Start date, ISO 8601.

**updatedAt** `string`

Last update date, ISO 8601.

**completedAt** `string | null`

Completion date, ISO 8601.

**failedAt** `string | null`

Failure date, ISO 8601.

## Get the current bulk creation

`GET /api/v1/bulk-qr-creations`

Returns the bulk creation still running in your workspace. When none is running, it returns the latest finished one (`completed` or `failed`) until it's dismissed: downloading its archive or dismissing it in the dashboard does that. Otherwise it returns `null`. Use it to resume tracking after a restart.

```bash cURL theme={null}
curl "https://getqr.com/api/v1/bulk-qr-creations" \
  -H "Authorization: Bearer $GETQR_API_KEY"
```

```json 200 theme={null}
{
  "id": "cm2k7x9qb0003ax8eb4f6gh2t",
  "status": "processing",
  "sourceFilename": "campaign-links.csv",
  "totalCount": 50,
  "createdCount": 18,
  "renderedCount": 12,
  "percent": 26,
  "stalled": false,
  "error": null,
  "downloadUrl": null,
  "createdAt": "2026-08-14T08:00:00.000Z",
  "updatedAt": "2026-08-14T08:00:20.000Z",
  "completedAt": null,
  "failedAt": null
}
```

```json 200 (nothing to resume) theme={null}
null
```

## Create from JSON

`POST /api/v1/bulk-qr-creations`

Starts a bulk creation from a list of rows. Answers `202 Accepted`. The bulk creation runs in the background.

**rows** `object[]` *required*

QR codes to create, up to 100. Each row is `{ "name": "...", "url": "..." }` and takes no other fields. See [Rows](#rows).

**design** `object` *required*

Design applied to every QR code: `styles`, `frameOptions` and `logoOptions`, all optional. Send `{}` for the default design. Other fields are rejected. See [Customization](https://docs.getqr.com/api/guides/customization.md).

**sourceFilename** `string` *required*

Name shown in the dashboard and used for the archive, 1 to 255 characters.

**idempotencyKey** `string` *required*

Your own key for this bulk creation, 8 to 100 characters.

```bash cURL theme={null}
curl -X POST "https://getqr.com/api/v1/bulk-qr-creations" \
  -H "Authorization: Bearer $GETQR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "rows": [
      { "name": "Menu", "url": "https://example.com/menu" },
      { "name": "Wine list", "url": "example.com/wine" }
    ],
    "design": { "styles": { "dotsOptions": { "type": "rounded" } } },
    "sourceFilename": "campaign-links.csv",
    "idempotencyKey": "campaign-2026-08-14"
  }'
```

```json 202 theme={null}
{
  "id": "cm2k7x9qb0003ax8eb4f6gh2t",
  "status": "queued",
  "sourceFilename": "campaign-links.csv",
  "totalCount": 2,
  "createdCount": 0,
  "renderedCount": 0,
  "percent": 0,
  "stalled": false,
  "error": null,
  "downloadUrl": null,
  "createdAt": "2026-08-14T08:00:00.000Z",
  "updatedAt": "2026-08-14T08:00:00.000Z",
  "completedAt": null,
  "failedAt": null
}
```

## Create from a CSV file

`POST /api/v1/bulk-qr-creations/csv`

Same as [Create from JSON](#create-from-json), but takes the rows as a CSV file sent as `multipart/form-data`. Answers `202 Accepted`.

The file must be a `.csv` of at most 1,000,000 bytes. Its header row must be exactly `name,url`, followed by up to 100 data rows. See [CSV format](https://docs.getqr.com/bulk/csv-format.md).

**file** `file` *required*

The CSV file.

**design** `string` *required*

The design object, serialized as JSON, for example `{"styles":{"dotsOptions":{"type":"rounded"}}}`. Send `{}` for the default design.

**idempotencyKey** `string` *required*

Your own key for this bulk creation, 8 to 100 characters.

**sourceFilename** `string`

Name shown in the dashboard and used for the archive, up to 255 characters. Defaults to the uploaded file's name.

```bash cURL theme={null}
curl -X POST "https://getqr.com/api/v1/bulk-qr-creations/csv" \
  -H "Authorization: Bearer $GETQR_API_KEY" \
  -F "file=@campaign-links.csv" \
  -F 'design={"styles":{"dotsOptions":{"type":"rounded"}}}' \
  -F "idempotencyKey=campaign-2026-08-14"
```

```csv campaign-links.csv theme={null}
name,url
Menu,https://example.com/menu
Wine list,example.com/wine
```

```json 202 theme={null}
{
  "id": "cm2k7x9qb0003ax8eb4f6gh2t",
  "status": "queued",
  "sourceFilename": "campaign-links.csv",
  "totalCount": 2,
  "createdCount": 0,
  "renderedCount": 0,
  "percent": 0,
  "stalled": false,
  "error": null,
  "downloadUrl": null,
  "createdAt": "2026-08-14T08:00:00.000Z",
  "updatedAt": "2026-08-14T08:00:00.000Z",
  "completedAt": null,
  "failedAt": null
}
```

## Get a bulk creation

`GET /api/v1/bulk-qr-creations/{id}`

Returns the status and progress of a bulk creation. Poll it while `status` is `queued`, `processing` or `packaging`. `downloadUrl` appears once the archive is ready.

**id** `string` *required*

ID of the bulk creation. Take it from the create or current-bulk-creation response.

```bash cURL theme={null}
curl "https://getqr.com/api/v1/bulk-qr-creations/cm2k7x9qb0003ax8eb4f6gh2t" \
  -H "Authorization: Bearer $GETQR_API_KEY"
```

```json 200 theme={null}
{
  "id": "cm2k7x9qb0003ax8eb4f6gh2t",
  "status": "completed",
  "sourceFilename": "campaign-links.csv",
  "totalCount": 50,
  "createdCount": 50,
  "renderedCount": 50,
  "percent": 100,
  "stalled": false,
  "error": null,
  "downloadUrl": "/api/v1/bulk-qr-creations/cm2k7x9qb0003ax8eb4f6gh2t/download",
  "createdAt": "2026-08-14T08:00:00.000Z",
  "updatedAt": "2026-08-14T08:02:10.000Z",
  "completedAt": "2026-08-14T08:02:10.000Z",
  "failedAt": null
}
```

## Abort a bulk creation

`DELETE /api/v1/bulk-qr-creations/{id}`

Stops a running bulk creation and deletes the QR codes it already created. The rollback runs during the request, so the response normally has `status` `failed` and `error.code` `cancelled_by_user`. Aborting a bulk creation that has already failed returns it unchanged.

> [!WARNING]
>
> A bulk creation that has completed can't be aborted: the request returns `400`. Delete its QR codes one by one with [QR codes](https://docs.getqr.com/api/endpoints/qr-codes.md) instead.

**id** `string` *required*

ID of the bulk creation.

```bash cURL theme={null}
curl -X DELETE "https://getqr.com/api/v1/bulk-qr-creations/cm2k7x9qb0003ax8eb4f6gh2t" \
  -H "Authorization: Bearer $GETQR_API_KEY"
```

```json 200 theme={null}
{
  "id": "cm2k7x9qb0003ax8eb4f6gh2t",
  "status": "failed",
  "sourceFilename": "campaign-links.csv",
  "totalCount": 50,
  "createdCount": 18,
  "renderedCount": 12,
  "percent": 26,
  "stalled": false,
  "error": {
    "code": "cancelled_by_user",
    "message": "Bulk QR creation was cancelled."
  },
  "downloadUrl": null,
  "createdAt": "2026-08-14T08:00:00.000Z",
  "updatedAt": "2026-08-14T08:00:40.000Z",
  "completedAt": null,
  "failedAt": "2026-08-14T08:00:40.000Z"
}
```

## Download the archive

`GET /api/v1/bulk-qr-creations/{id}/download`

Returns a ZIP archive with one 1024 px JPEG per QR code, named after its row: `001-Menu.jpg`, `002-Wine list.jpg` and so on. The archive is named after `sourceFilename`. Available once `status` is `completed`. Downloading dismisses the bulk creation, so [Get the current bulk creation](#get-the-current-bulk-creation) stops returning it.

**id** `string` *required*

ID of the bulk creation.

```bash cURL theme={null}
curl "https://getqr.com/api/v1/bulk-qr-creations/cm2k7x9qb0003ax8eb4f6gh2t/download" \
  -H "Authorization: Bearer $GETQR_API_KEY" \
  --output qr-codes.zip
```

```http 200 theme={null}
HTTP/1.1 200 OK
Content-Type: application/zip
Content-Disposition: attachment; filename="campaign-links-qr-codes.zip"

<binary archive data>
```

## Common issues

> [accordion] 422 with "contains an invalid HTTP(S) URL"
>
> The message names the row. Each URL must be an `http` or `https` address with a real domain, such as `example.com/menu`. Fix the row and send the request again.

> [accordion] 422 "CSV headers must be exactly: name,url."
>
> The first row of the CSV must be `name,url`, in that order and in lowercase, with no other columns.

> [accordion] 422 "This idempotencyKey was already used with a different request body."
>
> Keys can't be reused for a different batch. Send a new `idempotencyKey`.

> [accordion] 409 "The bulk QR archive is not ready for download."
>
> The bulk creation hasn't completed yet. Keep polling until `status` is `completed`.

> [accordion] 400 "Cannot cancel a finished bulk QR creation."
>
> The bulk creation has already completed, so it can't be rolled back.

> [accordion] 403 exceeded\_limit
>
> Your workspace is on the Single QR plan, which doesn't include bulk creation. Upgrade the plan.

> [accordion] I got back an older bulk creation instead of a new one
>
> Another bulk creation is still running in your workspace, or you reused an `idempotencyKey`. Wait for it to finish, or use a new key.

## What's next

> [card] [Customization](https://docs.getqr.com/api/guides/customization.md)

> [card] [QR codes](https://docs.getqr.com/api/endpoints/qr-codes.md)

> [card] [Bulk create in the app](https://docs.getqr.com/bulk/overview.md)

> [card] [CSV format](https://docs.getqr.com/bulk/csv-format.md)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.