# Analytics · Management API

> Management API reference for analytics: GET /sites/{id}/analytics/overview, GET /sites/{id}/analytics/pages, GET /sites/{id}/analytics/bots, GET /sites/{id}/analytics/edge.

> Markdown version of https://kanorio.com/en/developers/management-api/analytics. Append `.md` to any developer docs URL for this format. Index: https://kanorio.com/llms.txt · MCP: https://app.kanorio.com/api/mcp

Base URL: `https://app.kanorio.com/api/v1` · [Errors](https://kanorio.com/en/developers/basics/errors) · [Pagination](https://kanorio.com/en/developers/basics/pagination)

### Analytics overview

`GET /sites/{id}/analytics/overview`

- Stability: beta
- Permission: `analytics:view`
- Rate limit: heavy (20/minute per token, plus 20/minute per site)
- MCP tool: `analytics_overview`

Get website traffic overview for the selected period (default 30 days, or a custom from/to date range in the website's timezone): totals, previous-period comparison, daily series, devices, top traffic sources, UTM campaigns, buttons, and countries. When the range starts before traffic source data became complete, dataCoverage explains how topSources and topUtm are affected. Errors: FORBIDDEN, VALIDATION, RATE_LIMITED (per-site analytics limit), ANALYTICS_BUSY.

**Parameters**

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `id` | path | string | yes | Website id. Get it from sites.list or from sites.create. |
| `days` | query | integer | no | Number of completed calendar days ending yesterday in the website's timezone (1 to 3650, defaults to 30; days=1 means yesterday; ignored when from and to are provided). |
| `from` | query | string | no | Start calendar date (YYYY-MM-DD) in the website's timezone, inclusive. Must be paired with to. |
| `to` | query | string | no | End calendar date (YYYY-MM-DD) in the website's timezone, inclusive (up to yesterday in site timezone). Must be paired with from. |

**Response** (200)

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `days` | integer | yes | Number of days in the selected range. |
| `from` | string | yes | Start date (YYYY-MM-DD) in the website's timezone. |
| `to` | string | yes | End date (YYYY-MM-DD) in the website's timezone. |
| `timezone` | string | yes | IANA timezone used for day boundaries. |
| `dataStatus` | "ok" \| "no_traffic" \| "pending_aggregation" | yes | ok when traffic data exists; no_traffic when the site had 0 visits in this range; pending_aggregation when filled from raw events before the nightly rollup. |
| `totals` | object | yes | — |
| `totals.pageviews` | integer | yes | Total pageviews in the period. |
| `totals.uniqueVisitors` | integer | yes | Sum of daily unique visitors in the period. |
| `totals.sessions` | integer | yes | Total visitor sessions (30-minute idle boundary). |
| `totals.clicks` | integer | yes | Total custom button clicks. |
| `totals.formSubmits` | integer | yes | Total non-spam form submissions. |
| `totals.socialClicks` | integer | yes | Total external brand/social link clicks. |
| `previousPeriod` | object | yes | Totals from the immediately preceding period of equal length. |
| `previousPeriod.from` | string | yes | Previous period start date (YYYY-MM-DD). |
| `previousPeriod.to` | string | yes | Previous period end date (YYYY-MM-DD). |
| `previousPeriod.totals` | object | yes | — |
| `previousPeriod.totals.pageviews` | integer | yes | Total pageviews in the period. |
| `previousPeriod.totals.uniqueVisitors` | integer | yes | Sum of daily unique visitors in the period. |
| `previousPeriod.totals.sessions` | integer | yes | Total visitor sessions (30-minute idle boundary). |
| `previousPeriod.totals.clicks` | integer | yes | Total custom button clicks. |
| `previousPeriod.totals.formSubmits` | integer | yes | Total non-spam form submissions. |
| `previousPeriod.totals.socialClicks` | integer | yes | Total external brand/social link clicks. |
| `trends` | object | yes | — |
| `trends.pageviews` | integer \| null | yes | Percentage change vs. the previous period of equal length (null when previous period was 0). |
| `trends.uniqueVisitors` | integer \| null | yes | Percentage change vs. the previous period of equal length. |
| `trends.sessions` | integer \| null | yes | Percentage change vs. the previous period of equal length. |
| `trends.clicks` | integer \| null | yes | Percentage change vs. the previous period of equal length. |
| `trends.formSubmits` | integer \| null | yes | Percentage change vs. the previous period of equal length. |
| `trends.socialClicks` | integer \| null | yes | Percentage change vs. the previous period of equal length. |
| `daily` | object[] | yes | Day-by-day series covering every day in [from, to]. |
| `daily[].date` | string | yes | Calendar date (YYYY-MM-DD) in the website's timezone. |
| `daily[].pageviews` | integer | yes | Pageviews on this date. |
| `daily[].uniqueVisitors` | integer | yes | Unique visitors on this date. |
| `daily[].sessions` | integer | yes | Visitor sessions on this date. |
| `daily[].clicks` | integer | yes | Button clicks on this date. |
| `daily[].formSubmits` | integer | yes | Form submissions on this date. |
| `daily[].socialClicks` | integer | yes | External link clicks on this date. |
| `devices` | object | yes | — |
| `devices.mobile` | integer | yes | Unique visitors on mobile devices. |
| `devices.desktop` | integer | yes | Unique visitors on desktop devices. |
| `devices.tablet` | integer | yes | Unique visitors on tablet devices. |
| `topSources` | object[] | yes | Top entry traffic sources (google, direct, chatgpt, etc.). |
| `topSources[].name` | string | yes | Channel, button, link, or country identifier. |
| `topSources[].value` | integer | yes | Count in the selected period. |
| `topUtm` | object[] | yes | Top UTM campaigns or source/medium pairs. |
| `topUtm[].name` | string | yes | Channel, button, link, or country identifier. |
| `topUtm[].value` | integer | yes | Count in the selected period. |
| `topButtons` | object[] | yes | Top clicked custom CTA buttons. |
| `topButtons[].name` | string | yes | Channel, button, link, or country identifier. |
| `topButtons[].value` | integer | yes | Count in the selected period. |
| `topCountries` | object[] | yes | Top visitor countries (ISO 3166-1 alpha-2). |
| `topCountries[].name` | string | yes | Channel, button, link, or country identifier. |
| `topCountries[].value` | integer | yes | Count in the selected period. |
| `dataCoverage` | object | no | Present only when the selected range starts before trafficSourcesCompleteFrom; do not read the direct share of that earlier part as real traffic distribution. |
| `dataCoverage.trafficSourcesCompleteFrom` | string | yes | Local date (YYYY-MM-DD, website timezone) from which topSources and topUtm are fully recorded. |
| `dataCoverage.note` | string | yes | How data before trafficSourcesCompleteFrom affects topSources and topUtm. |

**Errors**

| Code | Status | When |
| --- | --- | --- |
| `FORBIDDEN` | 403 | — |
| `VALIDATION` | 400 | — |
| `RATE_LIMITED` | 429 | per-site analytics limit |
| `ANALYTICS_BUSY` | 429 | — |

```bash
curl -X GET "https://app.kanorio.com/api/v1/sites/{id}/analytics/overview?days=30" \
  -H "Authorization: Bearer $KANORIO_TOKEN"
```

```json
{
  "days": 30,
  "from": "2026-09-23",
  "to": "2026-10-22",
  "timezone": "Asia/Taipei",
  "dataStatus": "ok",
  "totals": {
    "pageviews": 4820,
    "uniqueVisitors": 1940,
    "sessions": 2310,
    "clicks": 412,
    "formSubmits": 38,
    "socialClicks": 185
  },
  "previousPeriod": {
    "from": "2026-08-24",
    "to": "2026-09-22",
    "totals": {
      "pageviews": 4200,
      "uniqueVisitors": 1750,
      "sessions": 2050,
      "clicks": 360,
      "formSubmits": 32,
      "socialClicks": 160
    }
  },
  "trends": {
    "pageviews": 15,
    "uniqueVisitors": 11,
    "sessions": 13,
    "clicks": 14,
    "formSubmits": 19,
    "socialClicks": 16
  },
  "daily": [
    {
      "date": "2026-10-22",
      "pageviews": 168,
      "uniqueVisitors": 72,
      "sessions": 84,
      "clicks": 15,
      "formSubmits": 2,
      "socialClicks": 6
    }
  ],
  "devices": {
    "mobile": 1320,
    "desktop": 540,
    "tablet": 80
  },
  "topSources": [
    {
      "name": "google",
      "value": 980
    },
    {
      "name": "instagram",
      "value": 540
    },
    {
      "name": "chatgpt",
      "value": 112
    }
  ],
  "topUtm": [
    {
      "name": "october_special",
      "value": 210
    }
  ],
  "topButtons": [
    {
      "name": "Order Now",
      "value": 196
    }
  ],
  "topCountries": [
    {
      "name": "TW",
      "value": 1620
    },
    {
      "name": "US",
      "value": 210
    }
  ]
}
```

### Analytics pages

`GET /sites/{id}/analytics/pages`

- Stability: beta
- Permission: `analytics:view`
- Rate limit: heavy (20/minute per token, plus 20/minute per site)
- MCP tool: `analytics_pages`

Get per-page traffic, entry sessions, bounce rate, engagement duration, button clicks, form submissions, AI assistant referral entries, and cross-tool ids (websitePageId for pages.*, productId for products.*, postId for posts.*) for the selected period. Errors: FORBIDDEN, VALIDATION, RATE_LIMITED (per-site analytics limit), ANALYTICS_BUSY.

**Parameters**

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `id` | path | string | yes | Website id. Get it from sites.list or from sites.create. |
| `days` | query | integer | no | Number of completed calendar days ending yesterday in the website's timezone (1 to 3650, defaults to 30; days=1 means yesterday; ignored when from and to are provided). |
| `from` | query | string | no | Start calendar date (YYYY-MM-DD) in the website's timezone, inclusive. Must be paired with to. |
| `to` | query | string | no | End calendar date (YYYY-MM-DD) in the website's timezone, inclusive (up to yesterday in site timezone). Must be paired with from. |

**Response** (200)

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `days` | integer | yes | Number of days in the selected range. |
| `from` | string | yes | Start date (YYYY-MM-DD) in the website's timezone. |
| `to` | string | yes | End date (YYYY-MM-DD) in the website's timezone. |
| `timezone` | string | yes | IANA timezone used for day boundaries. |
| `pages` | object[] | yes | — |
| `pages[].pageId` | string | yes | Analytics PageRef identifier (e.g. WebsitePage id, 'product:<productId>', 'blog-post:<postId>', or 'blog-index'). |
| `pages[].kind` | string | yes | Content kind (page, blog-post, product, shop-index, etc.). |
| `pages[].websitePageId` | string \| null | yes | Corresponding WebsitePage id for use with pages.get / pages.update / pages.replace_sections (null for dynamic blog posts or taxonomy pages). |
| `pages[].productId` | string \| null | yes | Corresponding Product id for use with products.get / products.update when kind is 'product' (null otherwise). |
| `pages[].postId` | string \| null | yes | Corresponding BlogPost id for use with posts.get / posts.update when kind is 'blog-post' (null otherwise). |
| `pages[].path` | string \| null | yes | URL path snapshot. |
| `pages[].title` | string \| null | yes | Page title snapshot. |
| `pages[].pageviews` | integer | yes | Total pageviews on this page. |
| `pages[].uniqueVisitors` | integer | yes | Sum of daily unique visitors on this page. |
| `pages[].entrySessions` | integer | yes | Sessions that started on this page. |
| `pages[].bounceRate` | integer | yes | Bounce percentage (0 to 100). |
| `pages[].avgEngagementSec` | integer | yes | Average foreground engagement time in seconds, weighted by daily pageviews. |
| `pages[].ctaClicks` | integer | yes | Custom CTA button clicks on this page. |
| `pages[].formSubmits` | integer | yes | Non-spam form submissions on this page. |
| `pages[].aiReferralEntries` | integer | yes | Entry sessions on this page attributed to AI assistants (ChatGPT, Perplexity, Claude, Gemini, Copilot). |

**Errors**

| Code | Status | When |
| --- | --- | --- |
| `FORBIDDEN` | 403 | — |
| `VALIDATION` | 400 | — |
| `RATE_LIMITED` | 429 | per-site analytics limit |
| `ANALYTICS_BUSY` | 429 | — |

```bash
curl -X GET "https://app.kanorio.com/api/v1/sites/{id}/analytics/pages?days=30" \
  -H "Authorization: Bearer $KANORIO_TOKEN"
```

```json
{
  "days": 30,
  "from": "2026-09-23",
  "to": "2026-10-22",
  "timezone": "Asia/Taipei",
  "pages": [
    {
      "pageId": "pg_example456",
      "kind": "page",
      "websitePageId": "pg_example456",
      "productId": null,
      "postId": null,
      "path": "/",
      "title": "Home",
      "pageviews": 2840,
      "uniqueVisitors": 1420,
      "entrySessions": 1280,
      "bounceRate": 34,
      "avgEngagementSec": 52,
      "ctaClicks": 245,
      "formSubmits": 22,
      "aiReferralEntries": 36
    }
  ]
}
```

### Analytics bots

`GET /sites/{id}/analytics/bots`

- Stability: beta
- Permission: `analytics:view`
- Rate limit: heavy (20/minute per token, plus 20/minute per site)
- MCP tool: `analytics_bots`

Get AI crawler and search bot activity summary (GPTBot, ClaudeBot, PerplexityBot, Googlebot, etc.) for the selected period, with optional per-page intent breakdown (breakdown='page', up to 3650 days) or per-page × per-bot breakdown (breakdown='page_bot', retained for 90 days). Breakdown rows are paged: pass limit and cursor (nextCursor) while hasMore is true. Errors: FORBIDDEN, VALIDATION (details.reason: UNKNOWN_BOT or RANGE_BEFORE_RETENTION), RATE_LIMITED (per-site analytics limit), ANALYTICS_BUSY, INVALID_CURSOR.

**Parameters**

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `id` | path | string | yes | Website id. Get it from sites.list or from sites.create. |
| `days` | query | integer | no | Number of completed calendar days ending yesterday in the website's timezone (1 to 3650, defaults to 30; days=1 means yesterday; ignored when from and to are provided). |
| `from` | query | string | no | Start calendar date (YYYY-MM-DD) in the website's timezone, inclusive. Must be paired with to. |
| `to` | query | string | no | End calendar date (YYYY-MM-DD) in the website's timezone, inclusive (up to yesterday in site timezone). Must be paired with from. |
| `breakdown` | query | "none" \| "page" \| "page_bot" | no | Optional breakdown mode: 'none' (default, top bots only), 'page' (per-page intent rollup, up to 3650 days), or 'page_bot' (per-page × per-bot rollup, retained for 90 days). |
| `pageRef` | query | string | no | Optional PageRef filter when breakdown is 'page' or 'page_bot' (e.g. a page id, 'blog-post:<id>', 'product:<id>', or 'other'). |
| `botToken` | query | string | no | Optional bot token filter (must be one of the 35 registered tokens, e.g. GPTBot, ClaudeBot, PerplexityBot). Applies to totals, bots, and breakdown='page_bot'; breakdown='page' rows always sum all bots by intent. |
| `limit` | query | integer | no | Maximum number of items to return, 1–100. Defaults to 100. |
| `cursor` | query | string | no | Opaque cursor from the previous page's nextCursor. Omit for the first page. |

**Response** (200)

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `days` | integer | yes | Number of days in the selected range. |
| `from` | string | yes | Start date (YYYY-MM-DD) in the website's timezone. |
| `to` | string | yes | End date (YYYY-MM-DD) in the website's timezone. |
| `timezone` | string | yes | IANA timezone used for day boundaries. |
| `totals` | object | yes | — |
| `totals.totalHits` | integer | yes | Total crawler hits in the period. |
| `totals.blockedHits` | integer | yes | Crawler hits blocked by site rules. |
| `totals.verifiedHits` | integer | yes | Hits verified against official bot CIDR/ASN IP ranges. |
| `totals.spoofedHits` | integer | yes | Hits claiming a bot User-Agent that failed official CIDR/ASN IP verification. |
| `bots` | object[] | yes | — |
| `bots[].botToken` | string | yes | Bot User-Agent token (e.g. GPTBot, ClaudeBot). |
| `bots[].operator` | string | yes | Organization operating the bot (e.g. OpenAI, Anthropic, Google). |
| `bots[].intent` | "training" \| "retrieval" \| "search" | yes | Declared purpose of the crawler. |
| `bots[].blockable` | boolean | yes | Whether this crawler can be blocked via site rules. |
| `bots[].totalHits` | integer | yes | Total hits from this bot. |
| `bots[].blockedHits` | integer | yes | Blocked hits from this bot. |
| `bots[].verifiedHits` | integer | yes | Verified hits from this bot. |
| `bots[].spoofedHits` | integer | yes | Spoofed hits claiming this bot token. |
| `pageBreakdown` | object[] | no | — |
| `pageBreakdown[].pageRef` | string | yes | Canonical PageRef id (e.g. WebsitePage id, blog-post:<id>, product:<id>, or 'other'). |
| `pageBreakdown[].path` | string \| null | yes | Canonical page path (null when pageRef is 'other'). |
| `pageBreakdown[].title` | string \| null | yes | Canonical page title (null when pageRef is 'other'). |
| `pageBreakdown[].retrievalHits` | integer | yes | Hits from AI search/retrieval crawlers. |
| `pageBreakdown[].trainingHits` | integer | yes | Hits from AI model training crawlers. |
| `pageBreakdown[].searchHits` | integer | yes | Hits from search engine indexing crawlers. |
| `pageBreakdown[].blockedHits` | integer | yes | Hits blocked with HTTP 403. |
| `pageBreakdown[].spoofedHits` | integer | yes | Hits that failed official CIDR/ASN verification. |
| `pageBreakdown[].lastSeenAt` | string \| null | yes | ISO-8601 timestamp of the latest crawler hit on this page. |
| `pageBotBreakdown` | object[] | no | — |
| `pageBotBreakdown[].pageRef` | string | yes | Canonical PageRef id (e.g. WebsitePage id, blog-post:<id>, product:<id>, or 'other'). |
| `pageBotBreakdown[].path` | string \| null | yes | Canonical page path (null when pageRef is 'other'). |
| `pageBotBreakdown[].title` | string \| null | yes | Canonical page title (null when pageRef is 'other'). |
| `pageBotBreakdown[].botToken` | string | yes | Bot User-Agent token (e.g. GPTBot, ClaudeBot). |
| `pageBotBreakdown[].operator` | string | yes | Organization operating the bot. |
| `pageBotBreakdown[].intent` | "training" \| "retrieval" \| "search" | yes | Declared purpose of the crawler. |
| `pageBotBreakdown[].hits` | integer | yes | Total hits from this bot on this page. |
| `pageBotBreakdown[].blockedHits` | integer | yes | Blocked hits from this bot on this page. |
| `pageBotBreakdown[].verifiedHits` | integer | yes | Verified hits from this bot on this page. |
| `pageBotBreakdown[].spoofedHits` | integer | yes | Spoofed hits claiming this bot token on this page. |
| `pageBotBreakdown[].lastSeenAt` | string \| null | yes | ISO-8601 timestamp of the latest hit from this bot on this page. |
| `hasMore` | boolean | no | With a breakdown: true when more breakdown rows exist. |
| `nextCursor` | string \| null | no | With a breakdown: cursor for the next breakdown page, or null on the last page. |

**Errors**

| Code | Status | When |
| --- | --- | --- |
| `FORBIDDEN` | 403 | — |
| `VALIDATION` | 400 | details.reason: UNKNOWN_BOT or RANGE_BEFORE_RETENTION |
| `RATE_LIMITED` | 429 | per-site analytics limit |
| `ANALYTICS_BUSY` | 429 | — |
| `INVALID_CURSOR` | 400 | — |

```bash
curl -X GET "https://app.kanorio.com/api/v1/sites/{id}/analytics/bots?days=30" \
  -H "Authorization: Bearer $KANORIO_TOKEN"
```

```json
{
  "days": 30,
  "from": "2026-09-23",
  "to": "2026-10-22",
  "timezone": "Asia/Taipei",
  "totals": {
    "totalHits": 640,
    "blockedHits": 0,
    "verifiedHits": 590,
    "spoofedHits": 12
  },
  "bots": [
    {
      "botToken": "GPTBot",
      "operator": "OpenAI",
      "intent": "training",
      "blockable": true,
      "totalHits": 310,
      "blockedHits": 0,
      "verifiedHits": 302,
      "spoofedHits": 2
    }
  ]
}
```

### Analytics edge

`GET /sites/{id}/analytics/edge`

- Stability: beta
- Permission: `analytics:view`
- Rate limit: heavy (20/minute per token, plus 20/minute per site)
- MCP tool: `analytics_edge`

Get Cloudflare CDN & WAF edge traffic and security summary for the selected period: total requests, mitigated requests, static CDN cache hit rate (Worker SSR/ISR HTML and API routes are recorded as 'dynamic' at the outer CDN layer), saved bytes, edge 4xx/5xx responses (errors4xx includes HTTP 403 WAF/bot blocks and 404 scanner probes), cache status breakdown, top countries, and security actions. Errors: FORBIDDEN, HOSTED_ONLY, VALIDATION, RATE_LIMITED (per-site analytics limit), ANALYTICS_BUSY.

**Parameters**

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `id` | path | string | yes | Website id. Get it from sites.list or from sites.create. |
| `days` | query | integer | no | Number of completed calendar days ending yesterday in the website's timezone (1 to 3650, defaults to 30; days=1 means yesterday; ignored when from and to are provided). |
| `from` | query | string | no | Start calendar date (YYYY-MM-DD) in the website's timezone, inclusive. Must be paired with to. |
| `to` | query | string | no | End calendar date (YYYY-MM-DD) in the website's timezone, inclusive (up to yesterday in site timezone). Must be paired with from. |

**Response** (200)

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `days` | integer | yes | Number of days in the selected range. |
| `from` | string | yes | Start date (YYYY-MM-DD) in the website's timezone. |
| `to` | string | yes | End date (YYYY-MM-DD) in the website's timezone. |
| `timezone` | string | yes | IANA timezone used for day boundaries. |
| `summary` | object | yes | — |
| `summary.totalRequests` | integer | yes | Total edge requests received by Cloudflare. |
| `summary.mitigatedRequests` | integer | yes | Suspicious or attack requests mitigated at the edge (WAF / bot rules). |
| `summary.cachedRequests` | integer | yes | Requests served from outer Cloudflare CDN cache (hit, stale, revalidated). |
| `summary.cachedBytes` | number | yes | Bytes served from outer Cloudflare CDN cache. |
| `summary.cacheHitRate` | integer | yes | Outer Cloudflare CDN static cache hit percentage (0 to 100; Worker-rendered SSR/ISR HTML and API routes are recorded as 'dynamic' in cacheStatusBreakdown). |
| `summary.errors4xx` | integer | yes | Edge 4xx responses (includes HTTP 403 blocks/challenges from WAF & bot rules and 404 responses to scanner probes). |
| `summary.errors5xx` | integer | yes | Edge 5xx responses. |
| `cacheStatusBreakdown` | object[] | no | Breakdown of Cloudflare edge cache statuses (dynamic, hit, miss, etc.). |
| `cacheStatusBreakdown[].name` | string | yes | Channel, button, link, or country identifier. |
| `cacheStatusBreakdown[].value` | integer | yes | Count in the selected period. |
| `topCountries` | object[] | yes | Top request origin countries (ISO 3166-1 alpha-2). |
| `topCountries[].name` | string | yes | Channel, button, link, or country identifier. |
| `topCountries[].value` | integer | yes | Count in the selected period. |
| `securityActions` | object[] | yes | Mitigation actions taken by Cloudflare WAF (block, managed_challenge, etc.). |
| `securityActions[].name` | string | yes | Channel, button, link, or country identifier. |
| `securityActions[].value` | integer | yes | Count in the selected period. |

**Errors**

| Code | Status | When |
| --- | --- | --- |
| `FORBIDDEN` | 403 | — |
| `HOSTED_ONLY` | 409 | — |
| `VALIDATION` | 400 | — |
| `RATE_LIMITED` | 429 | per-site analytics limit |
| `ANALYTICS_BUSY` | 429 | — |

```bash
curl -X GET "https://app.kanorio.com/api/v1/sites/{id}/analytics/edge?days=30" \
  -H "Authorization: Bearer $KANORIO_TOKEN"
```

```json
{
  "days": 30,
  "from": "2026-09-23",
  "to": "2026-10-22",
  "timezone": "Asia/Taipei",
  "summary": {
    "totalRequests": 28400,
    "mitigatedRequests": 142,
    "cachedRequests": 24100,
    "cachedBytes": 512000000,
    "cacheHitRate": 85,
    "errors4xx": 34,
    "errors5xx": 0
  },
  "cacheStatusBreakdown": [
    {
      "name": "hit",
      "value": 24100
    },
    {
      "name": "dynamic",
      "value": 4300
    }
  ],
  "topCountries": [
    {
      "name": "TW",
      "value": 22100
    },
    {
      "name": "US",
      "value": 3200
    }
  ],
  "securityActions": [
    {
      "name": "managed_challenge",
      "value": 98
    },
    {
      "name": "block",
      "value": 44
    }
  ]
}
```
