> ## Documentation Index
> Fetch the complete documentation index at: https://docs.hadoseo.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Export Analytics

> Export granular per-bot analytics for all your domains as JSON

```
POST /functions/v1/export-user-analytics
```

Returns a single JSON document with report-style analytics for **every domain on your account** — both domains you own and domains [shared](/dashboard/analytics) with you. Unlike the dashboard charts and PDF report, which group smaller crawlers into **"Other"** and **"Other social"**, this endpoint breaks traffic out into **each individual bot**.

<Warning>
  Always call this endpoint from a **server-side environment** (backend API, serverless function, build script, etc.). Never include your API key in client-side code — it will be visible to anyone inspecting your frontend.
</Warning>

<Info>
  Programmatic (API key) access requires the **Pro plan and above** — requests
  from Starter accounts return `403 plan_upgrade_required`. The **Export all
  (JSON)** button in the [Analytics dashboard](/dashboard/analytics) is
  available on **all plans**.
</Info>

## Request

### Headers

| Header          | Required | Description                    |
| --------------- | -------- | ------------------------------ |
| `Authorization` | Yes      | `Bearer hado_sk_your_key_here` |
| `Content-Type`  | Yes      | `application/json`             |

### Body Parameters

| Parameter | Type   | Required | Description                                                                                                        |
| --------- | ------ | -------- | ------------------------------------------------------------------------------------------------------------------ |
| `period`  | string | No       | Reporting window: `1d`, `7d`, `30d`, `90d`, or `365d`. Defaults to `30d`. Applies to every domain in the response. |

The export always covers every domain your account can access — there is no per-domain parameter.

### Example Request

```bash theme={null}
curl -X POST https://api.hadoseo.com/functions/v1/export-user-analytics \
  -H "Authorization: Bearer hado_sk_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "period": "30d"
  }'
```

## Response

### Success (200)

```json theme={null}
{
  "generatedAt": "2026-06-29T12:00:00.000Z",
  "period": "30d",
  "userId": "00000000-0000-0000-0000-000000000000",
  "domainCount": 1,
  "domains": [
    {
      "domain": "example.com",
      "domainId": "11111111-1111-1111-1111-111111111111",
      "role": "owner",
      "summary": {
        "totalCrawls": 1840,
        "prerenders": 412,
        "cachedViews": 1520,
        "cacheMisses": 320,
        "cacheHitRate": 82.6
      },
      "botTotals": {
        "googlebot": 920,
        "googleextendedbot": 14,
        "bingbot": 210,
        "openaibot": 88,
        "anthropicbot": 31,
        "perplexitybot": 12,
        "twitterbot": 40,
        "facebookbot": 35,
        "linkedinbot": 22,
        "slackbot": 9,
        "discordbot": 4,
        "whatsappbot": 6,
        "telegrambot": 3,
        "pinterestbot": 2,
        "snapchatbot": 0,
        "redditbot": 5,
        "metabot": 7,
        "quorabot": 1,
        "social_other": 8,
        "applebot": 18,
        "amazonbot": 11,
        "baidubot": 6,
        "yandexbot": 9,
        "duckduckbot": 4,
        "bytespiderbot": 25
      },
      "botSeries": [
        { "date": "2026-06-28", "googlebot": 41, "bingbot": 9, "openaibot": 3, "bytespiderbot": 1 }
      ],
      "topPages": [
        {
          "url": "https://example.com/blog/launch",
          "seoScore": 92,
          "seoIssuesCount": 1,
          "botTotal": 134
        }
      ],
      "seo": {
        "good": 120,
        "needsWork": 18,
        "poor": 4,
        "unknown": 9,
        "totalPages": 151,
        "totalIssues": 47
      }
    }
  ]
}
```

<Note>
  Each `botSeries` entry contains a count for **every** bot listed in
  `botTotals`. The example above is trimmed to a few bots for brevity; the
  actual response includes all of them, with `0` where there was no traffic.
</Note>

### Fields

| Field                 | Type   | Description                                                                                            |
| --------------------- | ------ | ------------------------------------------------------------------------------------------------------ |
| `generatedAt`         | string | ISO 8601 timestamp of when the export was produced.                                                    |
| `period`              | string | The period applied: `1d`, `7d`, `30d`, `90d`, or `365d`.                                               |
| `userId`              | string | The account that owns the API key.                                                                     |
| `domainCount`         | number | Number of domains included.                                                                            |
| `domains[].domain`    | string | The domain name.                                                                                       |
| `domains[].domainId`  | string | The domain's unique ID.                                                                                |
| `domains[].role`      | string | Access level for this domain: `owner`, `manager`, or `viewer`.                                         |
| `domains[].summary`   | object | Period totals: `totalCrawls`, `prerenders`, `cachedViews`, `cacheMisses`, `cacheHitRate`.              |
| `domains[].botTotals` | object | Total visits per individual bot for the period.                                                        |
| `domains[].botSeries` | array  | Per-day breakdown; each entry has a `date` plus a count for every bot.                                 |
| `domains[].topPages`  | array  | Top pages by bot traffic: `url`, `seoScore`, `seoIssuesCount`, `botTotal`.                             |
| `domains[].seo`       | object | SEO health bucket counts (`good`, `needsWork`, `poor`, `unknown`) plus `totalPages` and `totalIssues`. |

### Granular bot breakdown

Every bot is reported as its own field. The bots that the dashboard and PDF report fold into **"Other"** and **"Other social"** each appear individually here:

| Bucket in reports | Individual bots in `botTotals` / `botSeries`                                                                                              |
| ----------------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
| **Other social**  | `slackbot`, `discordbot`, `whatsappbot`, `telegrambot`, `pinterestbot`, `snapchatbot`, `redditbot`, `metabot`, `quorabot`, `social_other` |
| **Other**         | `applebot`, `amazonbot`, `baidubot`, `yandexbot`, `duckduckbot`, `bytespiderbot`                                                          |

The named bots already shown in reports are also present individually: `googlebot`, `googleextendedbot`, `bingbot`, `openaibot`, `anthropicbot`, `perplexitybot`, `twitterbot`, `facebookbot`, and `linkedinbot`.

<Note>
  `social_other` is a catch-all for social crawlers that can't be identified
  individually. It's reported as its own field rather than merged into another bot.
</Note>

### Error Responses

| Status | Body                                                             | Description                                        |
| ------ | ---------------------------------------------------------------- | -------------------------------------------------- |
| 400    | `{ "error": "invalid_request" }`                                 | Invalid `period` value                             |
| 401    | `{ "error": "invalid_api_key" }`                                 | API key is missing, invalid, or revoked            |
| 403    | `{ "error": "plan_upgrade_required" }`                           | Programmatic export requires the Pro plan or above |
| 429    | `{ "error": "rate_limit_exceeded", "usage": 300, "limit": 300 }` | Monthly or per-minute rate limit exceeded          |

<Info>
  When you receive a **429**, the `usage` and `limit` fields tell you where you stand against your monthly quota. Per-minute limits (60 req/min) reset automatically after 60 seconds. See [Authentication](/api-reference/authentication) for plan limits.
</Info>

## Examples

### JavaScript / Node.js

```javascript theme={null}
const response = await fetch(
  "https://api.hadoseo.com/functions/v1/export-user-analytics",
  {
    method: "POST",
    headers: {
      "Authorization": `Bearer ${process.env.HADOSEO_API_KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ period: "30d" }),
  },
);

const report = await response.json();
for (const domain of report.domains) {
  console.log(domain.domain, domain.botTotals);
}
```

<Info>
  Prefer a one-off download? The [Analytics dashboard](/dashboard/analytics) has
  an **Export all (JSON)** button that produces the same document for the
  selected period without needing an API key — and it's available on every plan.
</Info>
