> ## 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.

# Analytics

> Scan statistics for one QR code or your whole workspace.

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

Get scan totals and breakdowns by country, device, browser and more, for a single QR code or across your workspace.

| Method | Path | Description |
| - | - | - |
| `GET` | `/api/v1/qrs/{qrId}/analytics` | [Get analytics for a QR code](#get-analytics-for-a-qr-code) |
| `GET` | `/api/v1/analytics` | [Get workspace analytics](#get-workspace-analytics) |

For a ready-made summary per QR code, see [Reports](https://docs.getqr.com/api/endpoints/reports.md).

## What the numbers mean

* **Scans.** Every count is in the `clicks` field. A scan is counted each time someone opens the QR code's short link.
* **Unique scans.** With `unique=true`, each visitor is counted once in the date range.
* **Scope.** Numbers come from the QR code's short link. A QR code without a short link has no scans to count.
* **History.** Analytics can't be reset through the API. Deleting a QR code doesn't purge its historical scan data.

## Headers

**Authorization** `string` *required*

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

## Query parameters

Both endpoints take these parameters. All of them are optional.

**groupBy** `string` (default: count)

Breakdown dimension. `count` returns the total. See [Breakdowns](#breakdowns) for every value and its response shape.

**interval** `string` (default: 30d)

Preset date range: `24h`, `7d`, `30d`, `90d`, `1y`, `mtd`, `qtd`, `ytd` or `all`. See [Date ranges](#date-ranges). Ignored when you send `start`.

**start** `string`

Start of a custom range, ISO 8601, for example `2026-08-01` or `2026-08-01T00:00:00Z`. Takes precedence over `interval`. Must be earlier than `end`.

**end** `string`

End of a custom range, ISO 8601. Defaults to the end of today. Used together with `start` or `interval=all`, and ignored with the other intervals.

**timezone** `string` (default: UTC)

IANA timezone used to align `timeseries` buckets, for example `America/New_York`. An unknown timezone returns `422`.

**unique** `boolean` (default: false)

`true` counts unique scans instead of total scans.

You can also send [dimension filters](#dimension-filters).

## Date ranges

| `interval` | Covers | `timeseries` buckets |
| - | - | - |
| `24h` | The last 24 hours | Hours |
| `7d` | The last 7 days, including today | Days |
| `30d` | The last 30 days, including today | Days |
| `90d` | The last 90 days, including today | Days |
| `1y` | The last 365 days, including today | Months |
| `mtd` | Month to date | Days |
| `qtd` | Quarter to date | Days |
| `ytd` | Year to date | Days, or months after 180 days |
| `all` | Since your workspace was created | Days, or months after 180 days |

For a custom range, send `start` and optionally `end`. Ranges of up to 2 days use hourly buckets, ranges over 180 days use monthly buckets, and everything in between uses daily buckets.

## Dimension filters

Narrow the results to scans that match a filter. Different filters are combined with AND. Where a filter takes several values, separate them with commas: the values are combined with OR.

| Filter | Value | Several values |
| - | - | - |
| `country` | ISO 3166-1 alpha-2 country code, for example `US` | Yes |
| `region` | ISO 3166-2 region code, for example `US-CA` | Yes |
| `city` | City name, for example `New York` | Yes |
| `continent` | `AF`, `AN`, `AS`, `EU`, `NA`, `OC` or `SA` | Yes |
| `device` | Device type, for example `Mobile` or `Desktop` | Yes |
| `browser` | Browser name, for example `Chrome` | Yes |
| `os` | Operating system, for example `iOS` or `Windows` | Yes |
| `url` | Destination URL | Yes |
| `referer` | Referrer, for example `google.com` | No |
| `utm_source`, `utm_medium`, `utm_campaign`, `utm_term`, `utm_content` | UTM value, up to 190 characters | No |

An unknown country or continent code returns `422`. Device, browser and OS names are matched with a capital first letter, so `mobile` works like `Mobile`.

For example, scans from the US or Canada on mobile devices: `?country=US,CA&device=Mobile`.

## Breakdowns

`count` returns an object. Every other value returns an array, sorted by `clicks` from highest to lowest (`timeseries` is sorted by time).

| `groupBy` | Each item contains |
| - | - |
| `count` | `clicks` (an object, not an array) |
| `timeseries` | `start` (start of the bucket), `clicks` |
| `continents` | `continent`, `clicks` |
| `regions` | `region`, `country`, `clicks` |
| `countries` | `country`, `city` (always `*`), `clicks` |
| `cities` | `city`, `country`, `clicks` |
| `devices` | `device`, `clicks` |
| `browsers` | `browser`, `clicks` |
| `os` | `os`, `clicks` |
| `triggers` | `trigger` (`qr` or `link`), `clicks` |
| `referers` | `referer` (`(direct)` when unknown), `clicks` |
| `referer_urls` | `refererUrl` (`(direct)` when unknown), `clicks` |
| `top_links` | `id`, `domain`, `key`, `shortLink`, `url`, `createdAt`, `qr` (`id`, `qrType`, `title`), `clicks` |
| `top_urls` | `url`, `clicks` |
| `top_types` | `qr_type`, `clicks` |
| `utm_sources`, `utm_mediums`, `utm_campaigns`, `utm_terms`, `utm_contents` | The UTM value (`utm_source`, `utm_medium`, `utm_campaign`, `utm_term` or `utm_content`), `clicks` |

An unknown `groupBy` returns `422` with the list of valid values.

Response when `groupBy=count`:

```json theme={null}
{ "clicks": 142 }
```

Response when `groupBy=countries`:

```json theme={null}
[
  { "country": "US", "city": "*", "clicks": 87 },
  { "country": "DE", "city": "*", "clicks": 31 },
  { "country": "GB", "city": "*", "clicks": 24 }
]
```

## Get analytics for a QR code

`GET /api/v1/qrs/{qrId}/analytics`

Returns scan statistics for one QR code, scoped to its short link. Takes the [query parameters](#query-parameters) and [dimension filters](#dimension-filters) above.

**qrId** `string` *required*

ID of the QR code. Take it from the list or create response of [QR codes](https://docs.getqr.com/api/endpoints/qr-codes.md).

```bash Total scans, last 30 days theme={null}
curl "https://getqr.com/api/v1/qrs/qr_1EPcVfeHZAY2iNoVLsgW2Aqf/analytics?groupBy=count&interval=30d" \
  -H "Authorization: Bearer $GETQR_API_KEY"
```

```bash Unique scans theme={null}
curl "https://getqr.com/api/v1/qrs/qr_1EPcVfeHZAY2iNoVLsgW2Aqf/analytics?groupBy=count&interval=30d&unique=true" \
  -H "Authorization: Bearer $GETQR_API_KEY"
```

```bash By country, last 7 days theme={null}
curl "https://getqr.com/api/v1/qrs/qr_1EPcVfeHZAY2iNoVLsgW2Aqf/analytics?groupBy=countries&interval=7d" \
  -H "Authorization: Bearer $GETQR_API_KEY"
```

```json 200 theme={null}
{
  "clicks": 142
}
```

## Get workspace analytics

`GET /api/v1/analytics`

Returns scan statistics across all QR codes in your workspace. Takes the [query parameters](#query-parameters) and [dimension filters](#dimension-filters) above, plus these scoping parameters.

**domain** `string`

With `key`, limits the results to the short link with this domain, for example `getqr.sh`. The domain must belong to your workspace.

**key** `string`

Short link slug, for example `x7Kd2p1`. Use it together with `domain`. Both are in the [QR code object](https://docs.getqr.com/api/endpoints/qr-codes.md).

**qrType** `string`

Comma-separated QR code types to include: `website`, `pdf`, `image`, `video`, `whatsapp`, `social`, `wifi`, `app`, `feedback`, `vcard`, `sms`, `payment`, `location`, `multilink`, `email` or `event`. See [QR types](https://docs.getqr.com/api/guides/qr-types.md).

```bash Devices, last 7 days theme={null}
curl "https://getqr.com/api/v1/analytics?groupBy=devices&interval=7d" \
  -H "Authorization: Bearer $GETQR_API_KEY"
```

```bash Devices, website QR codes only theme={null}
curl "https://getqr.com/api/v1/analytics?groupBy=devices&interval=30d&qrType=website" \
  -H "Authorization: Bearer $GETQR_API_KEY"
```

```bash One short link theme={null}
curl "https://getqr.com/api/v1/analytics?groupBy=count&domain=getqr.sh&key=x7Kd2p1" \
  -H "Authorization: Bearer $GETQR_API_KEY"
```

```json 200 theme={null}
[
  { "device": "Mobile", "clicks": 980 },
  { "device": "Desktop", "clicks": 240 }
]
```

## Common issues

> [accordion] 400 "QR has no associated link."
>
> The QR code has no short link, so there are no scans to count.

> [accordion] 404 when filtering by domain and key
>
> No short link with that domain and slug exists in your workspace. Copy `domain` and `key` from the QR code object.

> [accordion] 422 on groupBy, interval or a filter
>
> The value isn't supported. The error message names the parameter and lists the valid values.

> [accordion] Numbers don't match the dashboard
>
> Check that you use the same date range and timezone, and whether you count unique scans (`unique=true`) or total scans.

## What's next

> [card] [Reports](https://docs.getqr.com/api/endpoints/reports.md)

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

> [card] [Analytics in the app](https://docs.getqr.com/analytics/overview.md)

> [card] [Rate limits](https://docs.getqr.com/api/rate-limits.md)


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