# Click analytics

Three Clikso API calls read your click data — a summary with breakdowns, clicks over time for charts, and a list of recent raw clicks.

> **In short:**
>
> Three calls read your click data. The summary gives totals and breakdowns. The time series gives clicks per hour, day, week or month. The raw list gives single clicks. All three take the same JSON filters and share one quota per workspace.

## You need

* **An API key:** **Read** permission or higher.
* **A role:** Any member of the workspace.
* **A plan:** Any plan, including Free. Your plan sets how far back you can read and how many calls you can make.

## Get a summary

The summary counts every click in a date range. It also breaks them down by country, city, device, browser, operating system, referrer, link and UTM value. This request counts one link's clicks for one week.

```bash
curl -X POST "https://app.clik.so/api/analytics/summary" \
  -H "Authorization: Bearer $CLIKSO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "from": "2026-10-01T00:00:00Z",
    "to": "2026-10-08T00:00:00Z",
    "slugs": [
      "clik.is/summer-sale"
    ]
  }'
```

Response 200:

```json
{
  "status": 200,
  "success": true,
  "message": "Success",
  "data": {
    "total_clicks": 412,
    "referrers": [
      {
        "label": "https://www.google.com/",
        "count": 120
      },
      {
        "label": "https://t.co/",
        "count": 64
      }
    ],
    "devices": [
      {
        "label": "mobile",
        "count": 268
      },
      {
        "label": "desktop",
        "count": 131
      },
      {
        "label": "tablet",
        "count": 13
      }
    ],
    "os": [
      {
        "label": "iOS",
        "count": 160
      },
      {
        "label": "Android",
        "count": 108
      },
      {
        "label": "Windows",
        "count": 90
      }
    ],
    "browsers": [
      {
        "label": "Chrome",
        "count": 221
      },
      {
        "label": "Safari",
        "count": 150
      }
    ],
    "short_links": [
      {
        "label": "https://clik.is/summer-sale",
        "count": 412
      }
    ],
    "destinations": [
      {
        "label": "https://example.com/summer-sale",
        "count": 412
      }
    ],
    "countries": [
      {
        "label": "US",
        "count": 201
      },
      {
        "label": "IN",
        "count": 97
      }
    ],
    "cities": [
      {
        "label": "New York",
        "count": 48
      },
      {
        "label": "Bengaluru",
        "count": 41
      }
    ],
    "continents": [
      {
        "label": "NA",
        "count": 230
      },
      {
        "label": "AS",
        "count": 120
      }
    ],
    "timeseries": [
      {
        "timestamp": "2026-10-01T00:00:00Z",
        "clicks": 52
      },
      {
        "timestamp": "2026-10-02T00:00:00Z",
        "clicks": 61
      }
    ],
    "map_data": {
      "US": 201,
      "IN": 97
    },
    "utm": {
      "sources": [
        {
          "label": "newsletter",
          "count": 88
        }
      ],
      "mediums": [
        {
          "label": "email",
          "count": 88
        }
      ],
      "campaigns": [
        {
          "label": "summer",
          "count": 88
        }
      ],
      "terms": [],
      "contents": []
    },
    "link_performance": [
      {
        "link": "https://clik.is/summer-sale",
        "breakdown": [
          {
            "label": "https://example.com/summer-sale",
            "count": 412
          }
        ]
      }
    ],
    "retention_clamped": false,
    "retention_days": 90,
    "effective_from": "2026-10-01T00:00:00.000Z",
    "link_filter_truncated": false,
    "link_filter_limit": 250
  }
}
```

One summary call answers many questions at once. Use it before you reach for the other two calls.

## Get clicks over time

The time series gives click counts in buckets, ready for a chart. Set `interval` to `hour`, `day`, `week` or `month`. If you leave it out, you get hourly buckets.

```bash
curl -X POST "https://app.clik.so/api/analytics/timeseries" \
  -H "Authorization: Bearer $CLIKSO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "from": "2026-10-01T00:00:00Z",
    "to": "2026-10-08T00:00:00Z",
    "interval": "day"
  }'
```

Response 200:

```json
{
  "status": 200,
  "success": true,
  "message": "Success",
  "data": [
    {
      "timestamp": "2026-10-01T00:00:00Z",
      "clicks": 52
    },
    {
      "timestamp": "2026-10-02T00:00:00Z",
      "clicks": 61
    },
    {
      "timestamp": "2026-10-04T00:00:00Z",
      "clicks": 47
    }
  ]
}
```

Buckets with no clicks are left out. Fill those gaps with zero when you draw the chart.

## List raw clicks

The raw list gives single clicks, newest first. Each click has its time, link, country, device, browser, operating system and referrer.

```bash
curl -X POST "https://app.clik.so/api/analytics/filter" \
  -H "Authorization: Bearer $CLIKSO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "from": "2026-10-07T00:00:00Z",
    "to": "2026-10-08T00:00:00Z",
    "country": [
      "US"
    ]
  }'
```

Response 200:

```json
{
  "status": 200,
  "success": true,
  "message": "Success",
  "data": [
    {
      "timestamp": "2026-10-07T18:42:10Z",
      "workspace_id": "8a1f2c3d-4b5e-4f60-9a7b-1c2d3e4f5a6b",
      "url_id": "3f6c1a2e-9b4d-4e8f-a1c2-7d9e0f1a2b3c",
      "short_url": "https://clik.is/summer-sale",
      "destination_url": "https://example.com/summer-sale",
      "country": "US",
      "device": "mobile",
      "browser": "Safari",
      "os": "iOS",
      "referrer": "https://t.co/"
    }
  ]
}
```

You get at most the 1,000 most recent clicks that match. There is no next page, so use a shorter date range to see older clicks.

## Choose which clicks to count

All three calls take the same JSON body. Every field is optional.

* `from` and `to` set the time window, as ISO 8601 dates in UTC. If either one is missing, you get the last 7 days. A value that is not a date, or a `from` after `to`, gets a `422` before the call counts toward your quota.
* `url_ids`, `slugs` and `domains` pick links. A slug is written as `domain/short_code`, like `clik.is/summer-sale`.
* `country`, `city`, `region`, `continent`, `device`, `os`, `browser` and `referrer` filter by where and how people clicked.
* `short_links` and `destination_urls` filter by the exact URL. Short links include `https://`.
* `utm_source`, `utm_medium`, `utm_campaign`, `utm_term` and `utm_content` filter by the UTM value recorded for the click.

Different filters combine with AND. Values inside one list combine with OR. So the body above means: this link, and the United States or India, and phones.

Each list can hold up to 100 values. Country codes have two letters, like `US`. Device values are `mobile`, `tablet`, `desktop` and `tv`.

## How far back you can read

Your plan keeps clicks for a set number of days.

| Limit                                                                | Free    | Starter | Pro      | Business |
| -------------------------------------------------------------------- | ------- | ------- | -------- | -------- |
| Click history you can read (Older clicks are left out, not an error) | 30 days | 90 days | 180 days | 365 days |

If `from` is older than that, Clikso moves it forward to the oldest day your plan keeps. This is not an error. The response tells you it happened:

* The summary has `retention_clamped`, `retention_days` and `effective_from` in `data`.
* All three calls send the headers `X-Analytics-Retention-Clamped`, `X-Analytics-Retention-Days` and `X-Analytics-Effective-From`.

## Quota and caching

Analytics calls have their own quota. It is counted per workspace, so all your keys share it.

| Limit                                                             | Free | Starter | Pro   | Business |
| ----------------------------------------------------------------- | ---- | ------- | ----- | -------- |
| Analytics requests per minute (Per workspace, shared by all keys) | 5    | 10      | 30    | 60       |
| Analytics requests per day (Per workspace, resets at 00:00 UTC)   | 100  | 1,000   | 5,000 | 20,000   |

Every response carries headers that show where you stand:

* `X-RateLimit-Limit`, `X-RateLimit-Remaining` and `X-RateLimit-Reset` for the current minute.
* `X-Analytics-Quota-Limit` and `X-Analytics-Quota-Remaining` for the day. The day resets at 00:00 UTC.

The same request within 60 seconds is often answered from a cache. It is faster, but it still counts toward your quota. The `X-Analytics-Cache` header says `HIT` or `MISS`.

> **Planning a big job?**
>
> Read `X-Analytics-Quota-Remaining` before you start, and spread the calls out. One summary call per link covers every breakdown at once.

## What you get back

* **Summary:** `data` holds `total_clicks`, then one list per breakdown. Each list item is a `label` and a `count`. You also get `timeseries` (clicks per day), `map_data` (country code to clicks), `utm` and `link_performance`.
* **Time series:** `data` is a list of `timestamp` and `clicks` pairs, oldest first. Each timestamp is the start of its bucket, in UTC.
* **Raw clicks:** `data` is a list of clicks, newest first. Fields with no value are empty strings.

## Common errors

| Status | Code                  | What it means                                                                                                                                                         | What to do                                                                                               |
| ------ | --------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------- |
| 401    | `UNAUTHORIZED`        | The key is missing or wrong.                                                                                                                                          | Send `Authorization: Bearer clik_live_…` with a key from Settings → API.                                 |
| 403    | `FORBIDDEN`           | The `Workspace-Id` header names a different workspace than the key.                                                                                                   | Check the key’s permission and your role. If you send `Workspace-Id`, it must match the key’s workspace. |
| 422    | `VALIDATION_ERROR`    | `from` or `to` is not an ISO 8601 date, `from` is after `to`, `interval` is not `hour`, `day`, `week` or `month`, a filter list is too long, or the body is not JSON. | Fix the field and send the request again.                                                                |
| 429    | `RATE_LIMITED`        | Too many analytics calls this minute.                                                                                                                                 | Wait the number of seconds in `Retry-After`.                                                             |
| 429    | `USAGE_LIMIT_REACHED` | Your workspace used today's analytics quota.                                                                                                                          | Wait until `error.details.resets_at` (00:00 UTC), or upgrade.                                            |
| 500    | `INTERNAL_ERROR`      | The analytics store did not answer.                                                                                                                                   | Try again in a moment.                                                                                   |

## Good to know

* New clicks show up within about a minute.
* Each click is counted once, even when its delivery was retried.
* There is no unique-visitor count and no session count. Every visit is a click.
* Bots are not filtered out.
* Country and city come from the visitor's IP address. A VPN or a work network can change them.
* Each summary breakdown lists the top 250 values. `link_performance` lists the top 500.
* A `domains` filter covers at most 250 links. When it hits that, `link_filter_truncated` is `true` and the numbers cover only those links.
* The summary's own `timeseries` is always per day. For other buckets, use the time series call.
* A request with a bad body (a `422`) does not use your quota. Neither does a call refused by the per-minute limit.
* The AI analytics chat is only in the dashboard. It is not part of the API.

---

Source: https://clik.so/docs/guides/analytics
