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

# Traffic and AI crawler logs

> Capture AI crawlers, AI-cited fetches and AI referrals automatically with the @usenotra/sdk tracker or a log drain.

Traffic tracking shows the AI systems fetching your pages and the people arriving from AI answers. A small request-capture SDK on your site, or a log drain from your host, sends each request to Notra, which classifies it at ingest and shows the results on the **Agent** tab of **Analytics**. Notra only stores AI traffic from the SDK and log drains and discards ordinary human page views on arrival. To count human readers on any site, add the [notra.js script tag](https://www.usenotra.com/docs/analytics/install/script-tag.md) and see them in [Web analytics](https://www.usenotra.com/docs/analytics/web-analytics.md), while sites hosted with Notra Sites count them automatically.

![Agent tab with crawler and referral activity](https://mintcdn.com/notra/HaO4sYeStX3BaIEr/images/ai-traffic/traffic-light.webp?fit=max\&auto=format\&n=HaO4sYeStX3BaIEr\&q=85\&s=eafae82a4a2ec37d61e3139fbe10febb)

The results are also available under `/geo/traffic` in the API. For a post published to a Notra Site, `GET /v1/posts/{postId}/agent-reach` returns the visits to that post's page, including its Markdown copy.

## What is captured

Notra classifies captured requests into these visitor types:

* **AI crawler**: bots fetching your pages to train models or build a search index. Each crawler carries a **Purpose**: **Model training** (collects pages for training corpora), **Search index** (builds the index an AI answer engine searches) or **Cited in answer** (fetched while an assistant was answering someone).
* **AI referral**: a person who clicked through to your site from an AI answer. Notra detects referrals by referrer host, for example chatgpt.com, perplexity.ai, gemini.google.com, claude.ai, copilot.microsoft.com, you.com, chat.deepseek.com, chat.mistral.ai, grok.com and chat.qwen.ai.
* **Human visitor**: an ordinary person opening a page. Notra discards these requests from the SDK and log drains, and counts people only through `notra.js`, which Notra Sites load automatically. See [Web analytics](https://www.usenotra.com/docs/analytics/web-analytics.md).

AI traffic collection from the SDK and log drains needs no browser script or cookies. Each AI event carries:

* the source, the agent and the purpose
* a **Confidence** of **Verified**, **Reported** or **Heuristic**, which records where the agent's signature came from
* the path, host and country
* whether the client asked for Markdown
* a journey id

On Notra Sites, each published post also shows its **Agent reach** in the post's **Overview** panel and in the **Content** list, which counts the crawls, citations and referrals for that post's page and its Markdown copy.

## Supported frameworks

The `@usenotra/sdk` tracker runs inside your app's request pipeline. Pick your framework:

> [card] [Next.js](https://www.usenotra.com/docs/analytics/install/nextjs.md)
>
> `proxy.ts`

> [card] [TanStack Start](https://www.usenotra.com/docs/analytics/install/tanstack-start.md)
>
> `src/start.ts`

> [card] [Nuxt](https://www.usenotra.com/docs/analytics/install/nuxt.md)
>
> `server/middleware/geo.ts`

> [card] [Astro](https://www.usenotra.com/docs/analytics/install/astro.md)
>
> `src/middleware.ts`

> [card] [SvelteKit](https://www.usenotra.com/docs/analytics/install/sveltekit.md)
>
> `src/hooks.server.ts`

> [card] [React Router](https://www.usenotra.com/docs/analytics/install/react-router.md)
>
> `app/root.tsx`

> [card] [Netlify Edge Functions](https://www.usenotra.com/docs/analytics/install/netlify.md)
>
> `netlify/edge-functions/geo.ts`

## Log drains

If you'd rather not change your code, Notra can read your host's request logs instead. A log drain sees the method, URL, user agent, referer and IP of every request, which is enough to track declared AI crawlers and AI referrals. It can't tag Markdown links for journeys, and without extra request headers it misses agents that ask for Markdown or pose as a browser.

> [card] [Vercel log drain](https://www.usenotra.com/docs/analytics/install/vercel-log-drain.md)
>
> Vercel Pro and Enterprise teams

> [card] [Cloudflare Logpush](https://www.usenotra.com/docs/analytics/install/cloudflare-logpush.md)
>
> Any site proxied through Cloudflare

## Recognized AI agents

Notra recognizes more than 60 AI crawlers and agents from OpenAI, Anthropic, Google, Perplexity, Meta, Apple, Amazon and others. See the full [AI crawler directory](https://www.usenotra.com/docs/analytics/crawlers.md).

## The Agent tab

Open **Analytics** in the GEO sidebar and stay on the **Agent** tab. Once events arrive, the page shows the sections below, and each one follows the range picker. The **Journeys** tab on the GEO **Overview** page shows how AI agents move through your site, as described in [Journeys](https://www.usenotra.com/docs/analytics/journeys.md), and the **Web** tab counts the people reading your site, as described in [Web analytics](https://www.usenotra.com/docs/analytics/web-analytics.md).

* A hero row with **Crawlers**, **Referrals** and **Total** visit counts, each compared "vs. previous period", and a daily trend chart.
* **Sources** has three groups: **Crawlers** (training and search-index bots), **Cited** (assistant-browse fetches while an engine answered someone) and **Referrals**. Columns are **Source**, **Purpose**, **Visits**, **Pages** and **Last seen**. Open a source to see the share of its visits that asked for Markdown, and log rows that asked for Markdown carry a **Markdown** badge. Sources group by operator, for example OpenAI, Anthropic, Google, Perplexity, Microsoft, Meta, Instagram, Amazon, Apple, ByteDance and Common Crawl, with Instagram kept as its own source rather than folded into Meta.
* **Top pages by AI source** has the columns **Page** (host and path), **Sources** and **Visits**. When a project tracks more than one domain, a domain filter narrows the table.
* **Recent citations** is the live event log, with **All visitors** (AI crawler, AI referral) and **All purposes** (Model training, Search index, Cited in answer) filters. The domain filter on top pages also narrows this log, and live updates refresh every few seconds until you pause them.

## Reading traffic from the API

All paths below are prefixed with `/v1/projects/{projectId}` and need `traffic.read`. Windowed endpoints accept `days` (1 to 365) or `from`/`to` (`YYYY-MM-DD`). On the overview, journey statistics and website analytics endpoints an explicit range can span at most 366 days, and a `from` without `to` runs to today.

| Endpoint | Query | Returns |
| - | - | - |
| `GET /geo/traffic/overview` | `days`, `from`, `to`, `host`, `path` (exact page path), `sites=exclude` (leave out Notra Sites) | `totals` (`crawler`, `cited`, `aiReferral`, `conversions`), `sources[]` with `source`, `visitorType`, `agent`, `category`, `confidence`, `visits`, `previousVisits`, `markdownVisits`, `previousMarkdownVisits`, `paths`, `previousPaths`, `lastSeenAt` and daily `points[]` |
| `GET /geo/traffic/journey-stats` | window | `sources[]` and `pages[]` with exact journey counts against the previous window, `singleFetch`, `deepCrawls`, `entries` and daily counts, plus `totalPages` |
| `GET /geo/traffic/log` | `limit` (1 to 200), `visitorTypes`, `categories`, `host` | `log[]` with `capturedAt`, `visitorType`, `source`, `agent`, `category`, `confidence`, `path`, `host`, `country`, `ua`, `journeyId`, `wantsMarkdown`, plus `total`. It takes no window, so bound it with `limit`, and `host` keeps the newest matching events for that hostname and its subdomains |
| `GET /geo/traffic/journeys` | window plus `limit` (1 to 100) | `journeys[]` with `journeyId`, `source`, `visitorType`, `pages`, `distinctPaths`, `firstSeenAt`, `lastSeenAt`, `entryPath`, `samplePaths[]` |
| `GET /geo/traffic/journeys/{journeyId}` | window | `events[]` with `capturedAt`, `path`, `host`, `method`, `referer`, `country`, `agent`, `category` |
| `GET /geo/traffic/pages` | window plus `limit` (1 to 500), `visitorType` (`crawler` or `ai_referral`), `host` | `pages[]` with `host`, `path`, `source`, `visitorType`, `visits`, `previousVisits`, `lastSeenAt`. `host` ranks pages for that hostname and its subdomains before applying `limit` |

`visitorTypes` and `categories` on the log endpoint are comma-separated lists. `categories` accepts `training-crawler`, `search-index` and `assistant-browse`.

```typescript theme={"system"}
const response = await fetch(
  `https://api.usenotra.com/v1/projects/${projectId}/geo/traffic/log?limit=50&visitorTypes=crawler&categories=assistant-browse`,
  { headers: { Authorization: `Bearer ${process.env.NOTRA_API_KEY}` } }
);
const { log } = await response.json();
```

Every traffic response includes `configured`. When it is `false`, the deployment has no traffic backend configured, and the payload is empty instead of an error.

## Reading website analytics from the API

The data on the **Web** tab ([Web analytics](https://www.usenotra.com/docs/analytics/web-analytics.md)) is available under the same prefix and scope, with the same window and `host`, `path` and `sites` filters as the overview:

| Endpoint | Returns |
| - | - |
| `GET /geo/traffic/web` | `totals` (`views`, `visitors`, `sessions`, `engagedSessions`, `aiVisitors`, each with its `previous*` value), daily `points[]`, `hosts[]` and `imported` (the latest completed history import) |
| `GET /geo/traffic/web/breakdowns` | `pages[]`, `sources[]`, `countries[]`, `devices[]`, `languages[]`, `outcomes[]` (how sessions from AI assistants behave), `outboundLinks[]` (host and path only), `vitals[]` (p75 in milliseconds, CLS x1000) and `errors[]` |
| `GET /geo/traffic/web/utm` | Takes a required `dimension`: `source`, `medium`, `campaign`, `term` or `content`. `rows[]` with the top 25 values and their `sessions`, `previousSessions` and `visitors`, counted on each session's first page. Term and content cover at most the last 90 days unless a history import adds older ones. Campaign, term and content values that look like an email address, a UUID or a token show as `:id` |
| `GET /geo/traffic/imports` | `imports[]` with each history import's provider, status and progress. Connections and credentials are never returned, so start imports in the dashboard |

Notra scrubs error messages before storing them.

```bash theme={"system"}
curl "https://api.usenotra.com/v1/projects/$PROJECT_ID/geo/traffic/web/breakdowns?from=2026-10-01&to=2026-10-31&host=www.example.com" \
  -H "Authorization: Bearer $NOTRA_API_KEY"
```

## Limits to keep in mind

> [accordion] User agent matching is spoofable
>
> Crawler classification relies on user agent signatures, and anyone can send a crawler's user agent string, so the **Confidence** value tells you where the signature came from. Where an operator publishes IP ranges or a reverse DNS method, the signature table points at it, but Notra doesn't verify IPs at ingest.

> [accordion] A fetch is not proof of a citation
>
> The **Cited in answer** purpose means an assistant fetched the page while answering someone, but it doesn't prove the answer quoted or linked the page.

> [accordion] Some agents cannot be detected
>
> The signature table leaves out agents that a user agent can't honestly identify, including Pi, ChatGPT Atlas, `Google-Extended` and `Applebot-Extended`. Notra cannot reliably attribute their traffic to a specific AI agent, so it treats those requests like human visits and doesn't store them.

> [accordion] Referrals depend on the referrer header
>
> Notra recognizes AI referrals by referrer host, so it doesn't count clicks from apps that strip the referrer or from hosts that aren't in the list.

> [accordion] Fingerprinted journeys are estimates
>
> Only journeys that followed a tagged `ntr` link are exact, while fingerprinted journeys group requests by heuristic and can merge or split real sessions.

> [accordion] Sampling reduces counts
>
> If you set `sample` below 1, every count on the **Agent** tab reflects the sampled fraction, and Notra doesn't scale the numbers back up.
>
> API counts reflect the sampled fraction too.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.