> ## 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 page views
Get analytics for individual pages in 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 the page-level view duration data for a design.
The request body can include page filters and pagination controls.
## HTTP method and URL path
POST https\://api.canva.com/rest/v1/designs/\{designId}/analytics/page-views
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}`
Indicates the media type of the information sent in the request. This must be set to `application/json`.
For example: `Content-Type: application/json`
## Path parameters
The design ID.
## Body parameters
The design page IDs to retrieve view duration data for.
**Minimum items:** `1`
**Maximum items:** `100`
The maximum number of page results to return per page.
**Minimum:** `1`
**Maximum:** `100`
**Default value:** `50`
If the success response contains a continuation token, there are more page results you can list. You can use this token in the request body and retrieve more page results from the list.
To retrieve all page results, you might need to make multiple requests.
A filter for page view data.
**Available values:** The only valid value is `by_viewer`.
The viewer ID to filter by.
Filters to viewer traffic from a specific trackable link. Omit this field to target viewer traffic that did not come from a trackable link.
**Available values:** The only valid value is `for_editor`.
**Available values:** The only valid value is `by_link`.
The trackable link ID to filter by.
## Example request
Examples for using the `/v1/designs/{designId}/analytics/page-views` endpoint:
```sh
curl --request POST 'https://api.canva.com/rest/v1/designs/{designId}/analytics/page-views' \
--header 'Authorization: Bearer {token}' \
--header 'Content-Type: application/json' \
--data '{
"page_ids": [
"PBBKfZml7MRHRQVw",
"PBBKfZml7Mghjsdg"
],
"limit": 50,
"continuation": "RkFGMgXlsVTDbMd:MR3L0QjiaUzycIAjx0yMyuNiV0OildoiOwL0x32G4NjNu4FwtAQNxowUQNMMYN",
"filter": {
"type": "by_viewer",
"link_id": "hddb98a3716",
"viewer_id": "b2d44753-f996-46ff-8f01-d4143911550f"
}
}'
```
```js
const fetch = require("node-fetch");
fetch("https://api.canva.com/rest/v1/designs/{designId}/analytics/page-views", {
method: "POST",
headers: {
"Authorization": "Bearer {token}",
"Content-Type": "application/json",
},
body: JSON.stringify({
"page_ids": [
"PBBKfZml7MRHRQVw",
"PBBKfZml7Mghjsdg"
],
"limit": 50,
"continuation": "RkFGMgXlsVTDbMd:MR3L0QjiaUzycIAjx0yMyuNiV0OildoiOwL0x32G4NjNu4FwtAQNxowUQNMMYN",
"filter": {
"type": "by_viewer",
"link_id": "hddb98a3716",
"viewer_id": "b2d44753-f996-46ff-8f01-d4143911550f"
}
}),
})
.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/page-views"))
.header("Authorization", "Bearer {token}")
.header("Content-Type", "application/json")
.method("POST", HttpRequest.BodyPublishers.ofString("{\"page_ids\": [\"PBBKfZml7MRHRQVw\", \"PBBKfZml7Mghjsdg\"], \"limit\": 50, \"continuation\": \"RkFGMgXlsVTDbMd:MR3L0QjiaUzycIAjx0yMyuNiV0OildoiOwL0x32G4NjNu4FwtAQNxowUQNMMYN\", \"filter\": {\"type\": \"by_viewer\", \"link_id\": \"hddb98a3716\", \"viewer_id\": \"b2d44753-f996-46ff-8f01-d4143911550f\"}}"))
.build();
HttpResponse response = HttpClient.newHttpClient().send(
request,
HttpResponse.BodyHandlers.ofString()
);
System.out.println(response.body());
}
}
```
```py
import requests
headers = {
"Authorization": "Bearer {token}",
"Content-Type": "application/json"
}
data = {
"page_ids": [
"PBBKfZml7MRHRQVw",
"PBBKfZml7Mghjsdg"
],
"limit": 50,
"continuation": "RkFGMgXlsVTDbMd:MR3L0QjiaUzycIAjx0yMyuNiV0OildoiOwL0x32G4NjNu4FwtAQNxowUQNMMYN",
"filter": {
"type": "by_viewer",
"link_id": "hddb98a3716",
"viewer_id": "b2d44753-f996-46ff-8f01-d4143911550f"
}
}
response = requests.post("https://api.canva.com/rest/v1/designs/{designId}/analytics/page-views",
headers=headers,
json=data
)
print(response.json())
```
```csharp
using System.Net.Http;
var client = new HttpClient();
var request = new HttpRequestMessage
{
Method = HttpMethod.Post,
RequestUri = new Uri("https://api.canva.com/rest/v1/designs/{designId}/analytics/page-views"),
Headers =
{
{ "Authorization", "Bearer {token}" },
},
Content = new StringContent(
"{\"page_ids\": [\"PBBKfZml7MRHRQVw\", \"PBBKfZml7Mghjsdg\"], \"limit\": 50, \"continuation\": \"RkFGMgXlsVTDbMd:MR3L0QjiaUzycIAjx0yMyuNiV0OildoiOwL0x32G4NjNu4FwtAQNxowUQNMMYN\", \"filter\": {\"type\": \"by_viewer\", \"link_id\": \"hddb98a3716\", \"viewer_id\": \"b2d44753-f996-46ff-8f01-d4143911550f\"}}",
Encoding.UTF8,
"application/json"
),
};
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"
"strings"
)
func main() {
payload := strings.NewReader(`{
"page_ids": [
"PBBKfZml7MRHRQVw",
"PBBKfZml7Mghjsdg"
],
"limit": 50,
"continuation": "RkFGMgXlsVTDbMd:MR3L0QjiaUzycIAjx0yMyuNiV0OildoiOwL0x32G4NjNu4FwtAQNxowUQNMMYN",
"filter": {
"type": "by_viewer",
"link_id": "hddb98a3716",
"viewer_id": "b2d44753-f996-46ff-8f01-d4143911550f"
}
}`)
url := "https://api.canva.com/rest/v1/designs/{designId}/analytics/page-views"
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer {token}")
req.Header.Add("Content-Type", "application/json")
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/page-views",
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => array(
'Authorization: Bearer {token}',
'Content-Type: application/json',
),
CURLOPT_POSTFIELDS => json_encode([
"page_ids" => [
"PBBKfZml7MRHRQVw",
"PBBKfZml7Mghjsdg"
],
"limit" => 50,
"continuation" => "RkFGMgXlsVTDbMd:MR3L0QjiaUzycIAjx0yMyuNiV0OildoiOwL0x32G4NjNu4FwtAQNxowUQNMMYN",
"filter" => [
"type" => "by_viewer",
"link_id" => "hddb98a3716",
"viewer_id" => "b2d44753-f996-46ff-8f01-d4143911550f"
]
])
));
$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/page-views')
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request['Authorization'] = 'Bearer {token}'
request['Content-Type'] = 'application/json'
request.body = <
## Success response
If successful, the endpoint returns a `200` response with a JSON body with the following parameters:
The list of page view durations.
Properties of items>} defaultExpanded={true}>
The total time spent viewing this page, in seconds.
The page ID. This is omitted for design types that do not support page IDs.
The average time spent viewing this page, in seconds. This is omitted for `by_viewer` filters.
The number of unique viewers for this page. This is omitted for `by_viewer` filters.
If the success response contains a continuation token, there are more page results you can list. You can use this token in the request body and retrieve more page results from the list.
To retrieve all page results, you might need to make multiple requests.
## Example response
```json
{
"items": [
{
"page_id": "PBBKfZml7MRHRQVw",
"total_view_duration_seconds": 1860,
"average_duration_seconds": 21.5,
"unique_view_count": 87
}
],
"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
##### Failed to read request body
```json
{
"code": "bad_request_body",
"message": "Failed to read request body"
}
```
##### The page views filter is invalid.
```json
{
"code": "bad_request_body",
"message": "filter must include a valid type discriminator and required fields"
}
```
##### 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}"
}
```