> ## Documentation Index
> Fetch the complete documentation index at: https://www.canva.dev/docs/apps/llms.txt
> Use this file to discover all available pages before exploring further.
# Get design analytics viewers
Get information about users who have viewed a design.
This API is currently provided as a preview. Be aware of the following:
* There might be unannounced breaking changes.
* Any breaking changes to preview APIs won't produce a new [API version](https://www.canva.dev/docs/apps/rest-apis/versions/).
* Public apps that use preview APIs will not pass the review process, and can't be made available to all Canva users.
To use this API, your app must act on behalf of a user who is a member of a [Canva Enterprise](https://www.canva.com/enterprise/) organization.
Lists viewers for a design, ordered by most recent view first.
## HTTP method and URL path
GET https\://api.canva.com/rest/v1/designs/\{designId}/analytics/viewers
This operation is rate limited to 100 requests per minute for each user of your app.
## Authentication and authorization
This endpoint requires a valid access token that acts on behalf of a user. For more information, see [Generate an access token](/docs/apps/rest-apis/reference/authentication/generate-access-token).
### Scopes
The access token must have all the following [scopes](/docs/apps/rest-apis/scopes) (permissions):
* `design:content:read`
### Capabilities
The user must have at least one of the following [capabilities](/docs/apps/rest-apis/capabilities/):
* `analytics`
## Header parameters
Provides credentials to authenticate the request, in the form of a `Bearer` token.
For example: `Authorization: Bearer {token}`
## Path parameters
The design ID.
## Query parameters
Filters viewers by a specific trackable link. If omitted, only viewers who visited the design without using a trackable link are returned.
Only returns viewers whose first view happened after this time, as a Unix timestamp (in seconds since the Unix Epoch).
The maximum number of viewers to return per page.
**Minimum:** `1`
**Maximum:** `100`
**Default value:** `50`
If the success response contains a continuation token, there are more viewers you can list. You can use this token as a query parameter and retrieve more viewers from the list, for example `/v1/designs/{designId}/analytics/viewers?continuation={continuation}`.
To retrieve all viewers, you might need to make multiple requests.
## Example request
Examples for using the `/v1/designs/{designId}/analytics/viewers` endpoint:
```sh
curl --request GET 'https://api.canva.com/rest/v1/designs/{designId}/analytics/viewers' \
--header 'Authorization: Bearer {token}'
```
```js
const fetch = require("node-fetch");
fetch("https://api.canva.com/rest/v1/designs/{designId}/analytics/viewers", {
method: "GET",
headers: {
"Authorization": "Bearer {token}",
},
})
.then(async (response) => {
const data = await response.json();
console.log(data);
})
.catch(err => console.error(err));
```
```java
import java.io.IOException;
import java.net.URI;
import java.net.http.*;
public class ApiExample {
public static void main(String[] args) throws IOException, InterruptedException {
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.canva.com/rest/v1/designs/{designId}/analytics/viewers"))
.header("Authorization", "Bearer {token}")
.method("GET", HttpRequest.BodyPublishers.noBody())
.build();
HttpResponse response = HttpClient.newHttpClient().send(
request,
HttpResponse.BodyHandlers.ofString()
);
System.out.println(response.body());
}
}
```
```py
import requests
headers = {
"Authorization": "Bearer {token}"
}
response = requests.get("https://api.canva.com/rest/v1/designs/{designId}/analytics/viewers",
headers=headers
)
print(response.json())
```
```csharp
using System.Net.Http;
var client = new HttpClient();
var request = new HttpRequestMessage
{
Method = HttpMethod.Get,
RequestUri = new Uri("https://api.canva.com/rest/v1/designs/{designId}/analytics/viewers"),
Headers =
{
{ "Authorization", "Bearer {token}" },
},
};
using (var response = await client.SendAsync(request))
{
response.EnsureSuccessStatusCode();
var body = await response.Content.ReadAsStringAsync();
Console.WriteLine(body);
};
```
```go
package main
import (
"fmt"
"io"
"net/http"
)
func main() {
url := "https://api.canva.com/rest/v1/designs/{designId}/analytics/viewers"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("Authorization", "Bearer {token}")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}
```
```php
$curl = curl_init();
curl_setopt_array($curl, array(
CURLOPT_URL => "https://api.canva.com/rest/v1/designs/{designId}/analytics/viewers",
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => array(
'Authorization: Bearer {token}',
),
));
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if (empty($err)) {
echo $response;
} else {
echo "Error: " . $err;
}
```
```ruby
require 'net/http'
require 'uri'
url = URI('https://api.canva.com/rest/v1/designs/{designId}/analytics/viewers')
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request['Authorization'] = 'Bearer {token}'
response = http.request(request)
puts response.read_body
```
## Success response
If successful, the endpoint returns a `200` response with a JSON body with the following parameters:
The list of viewers.
Properties of items>} defaultExpanded={true}>
An opaque identifier for the viewer. Use this value when filtering page view results by viewer.
When the viewer most recently opened the design, as a Unix timestamp (in seconds since the Unix Epoch).
How the viewer accessed the design.
**Available values:**
* `editor`: The viewer accessed the design with edit access.
* `commenter`: The viewer accessed the design with comment access.
* `viewer`: The viewer accessed the design with view-only access.
How this viewer should be presented to the caller.
**Available values:**
* `shown`: The viewer's identity is visible, and their user details are included.
* `anonymous`: The viewer is anonymous, so their identity is not available.
* `opt_out`: The viewer has opted out of design analytics, but some data was already collected for this viewer previosly.
* `another_team`: The viewer belongs to a different team, so their identity is not shared.
* `deleted`: The viewer's account has been deleted.
The user details of the viewer. This is omitted for viewers whose identity is not visible.
Properties of user>}>
The ID of the user.
The ID of the user's Canva Team.
The name of the user as shown in the Canva UI.
The total time this viewer has spent viewing the design, in seconds.
When the viewer first viewed the design, as a Unix timestamp (in seconds since the Unix Epoch).
The number of pages this viewer viewed for more than one second.
The device form factor used to view the design.
**Available values:**
* `small`: A small device, such as a phone.
* `large`: A large device, such as a laptop, desktop, or large tablet.
The simplified operating system name for the viewer's device.
The viewer location as city and country. This is only returned for anonymous viewers.
The number of times this viewer opened the design.
If the success response contains a continuation token, there are more viewers you can list. You can use this token as a query parameter and retrieve more viewers from the list, for example `/v1/designs/{designId}/analytics/viewers?continuation={continuation}`.
To retrieve all viewers, you might need to make multiple requests.
## Example response
```json
{
"items": [
{
"viewer_id": "b2d44753-f996-46ff-8f01-d4143911550f",
"user": {
"user_id": "auDAbliZ2rQNNOsUl5OLu",
"team_id": "Oi2RJILTrKk0KRhRUZozX",
"display_name": "Jane Doe"
},
"last_viewed_at": 1748044800,
"viewer_type": "editor",
"visibility": "shown",
"total_view_duration_seconds": 312,
"first_viewed_at": 1745452800,
"pages_viewed": 8,
"form_factor": "small",
"device_operating_system": "macOS",
"city_country": "Sydney, Australia",
"total_view_count": 14
}
],
"continuation": "RkFGMgXlsVTDbMd:MR3L0QjiaUzycIAjx0yMyuNiV0OildoiOwL0x32G4NjNu4FwtAQNxowUQNMMYN"
}
```
## Error responses
### 400 Bad Request
A short string indicating what failed. This field can be used to handle errors programmatically. For a complete list of error codes, see [Error responses](/docs/apps/rest-apis/error-responses/).
A human-readable description of what went wrong.
#### Example error responses
##### Expected long for query parameter
```json
{
"code": "bad_query_params",
"message": "Expected long for query parameter `{key}` but found {value}"
}
```
##### The continuation token is invalid or expired.
```json
{
"code": "invalid_field",
"message": "Invalid continuation token"
}
```
### 401 Unauthorized
A short string indicating what failed. This field can be used to handle errors programmatically. For a complete list of error codes, see [Error responses](/docs/apps/rest-apis/error-responses/).
A human-readable description of what went wrong.
#### Example error response
##### Access token could not be decoded or signature could not be verified.
```json
{
"code": "invalid_access_token",
"message": "Access token is invalid"
}
```
### 403 Forbidden
A short string indicating what failed. This field can be used to handle errors programmatically. For a complete list of error codes, see [Error responses](/docs/apps/rest-apis/error-responses/).
A human-readable description of what went wrong.
#### Example error response
##### The caller is not allowed to access design analytics.
```json
{
"code": "permission_denied",
"message": "Not allowed to access analytics for this design"
}
```
### 404 Not Found
A short string indicating what failed. This field can be used to handle errors programmatically. For a complete list of error codes, see [Error responses](/docs/apps/rest-apis/error-responses/).
A human-readable description of what went wrong.
#### Example error response
##### The design was not found, or analytics are unavailable.
```json
{
"code": "design_not_found",
"message": "Design not found or analytics are not available for this design"
}
```
### 429 Too Many Requests
A short string indicating what failed. This field can be used to handle errors programmatically. For a complete list of error codes, see [Error responses](/docs/apps/rest-apis/error-responses/).
A human-readable description of what went wrong.
#### Example error response
##### Rate limit exceeded
```json
{
"code": "too_many_requests",
"message": "Too many requests to {operation}"
}
```