Docs
Set up TrustTraffic
Install the crawler tracker, check it works, read the dashboard, run citation checks, and pull all of it into your own tools through the API.
On this page
Quick start
- Create an account. Crawler tracking is free, no card.
- Add your domain. You get a site token starting with
ttfc_. - In the dashboard, open the setup panel (the status button, top right), pick your stack and copy the code. Your token is already filled in. The same code is below with a placeholder token.
- Deploy it. The status button turns green at the first AI-crawler visit.
- Don’t want to wait for a real crawler? Send a test hit.
How tracking works
AI crawlers such as GPTBot, ClaudeBot and PerplexityBot don’t run JavaScript, so an analytics script in your pages never sees them. TrustTraffic works a layer lower: a few lines in your server, middleware or CDN look at each request’s User-Agent. When it matches one of the 53 AI crawlers we know (see the crawler directory), they send one small JSON record to TrustTraffic, after the response has gone, so your pages are never slower.
On our side the User-Agent is checked again, the IP is checked against the address ranges the crawler’s vendor publishes, and the hit is stored. Nothing is sent for ordinary visitors unless you switch on human page views, and even then it is only a count per day.
The code has no dependencies: nothing to install from npm, and no script on your pages.
Install the tracker
Pick the one that matches where your site runs. Replace ttfc_YOUR_SITE_TOKEN with your site token, or copy the code from the dashboard, where it’s already filled in.
Next.js
Two files. middleware.ts (in the project root, or in src/ if you use it) records every AI-crawler request. Middleware runs before the page, so it can’t know the status code and records 200.
// middleware.ts — TrustTraffic AI-crawler tracker (no dependencies)
// Next.js 16+: name this file proxy.ts and export `proxy` instead of `middleware`.
import { NextResponse, type NextFetchEvent, type NextRequest } from 'next/server';
const AI_BOTS = /OAI-SearchBot|ChatGPT-User|GPTBot|Claude-SearchBot|Claude-User|ClaudeBot|anthropic-ai|claude-web|Perplexity-User|PerplexityBot|Grok-DeepSearch|xAI-Grok|GrokBot|Google-Extended|Google-CloudVertexBot|Google-NotebookLM|GoogleOther|Applebot-Extended|Applebot|bingbot|meta-externalagent|meta-externalfetcher|FacebookBot|Bytespider|TikTokSpider|Amazonbot|bedrockbot|CCBot|cohere-ai|cohere-training-data-crawler|YouBot|DuckAssistBot|MistralAI-User|Diffbot|AI2Bot|Ai2Bot-Dolma|ImagesiftBot|Omgilibot|Omgili|Webzio-Extended|Timpibot|PetalBot|PanguBot|Kagibot|VelenPublicWebCrawler|iaskspider|FirecrawlAgent|SemrushBot-OCOB|SBIntuitionsBot|panscient|PoseidonResearchCrawler|aiHitBot|Andibot|QualifiedBot|YandexAdditional|FriendlyCrawler|img2dataset|Cotoyogi|Crawlspace/i;
const COLLECT = 'https://trusttraffic.net/api/collect';
const TOKEN = 'ttfc_YOUR_SITE_TOKEN';
export function middleware(req: NextRequest, event: NextFetchEvent) {
const ua = req.headers.get('user-agent') ?? '';
if (AI_BOTS.test(ua)) {
event.waitUntil(
fetch(COLLECT, {
method: 'POST',
headers: { 'content-type': 'application/json' },
body: JSON.stringify({
token: TOKEN,
path: req.nextUrl.pathname,
userAgent: ua,
ip: req.headers.get('x-forwarded-for')?.split(',')[0]?.trim() ?? null,
}),
}).catch(() => {}),
);
// The exact path for app/[...missing]/page.tsx, so its 404 report matches the hit logged here.
const headers = new Headers(req.headers);
headers.set('x-trusttraffic-path', req.nextUrl.pathname);
return NextResponse.next({ request: { headers } });
}
return NextResponse.next();
}
export const config = { matcher: '/((?!_next/|favicon.ico).*)' };The second file is what turns a hit into a 404. It is a catch-all route, so it only runs for URLs that no other route matches. It reports the 404, then shows your normal not-found page.
// app/[...missing]/page.tsx — tells TrustTraffic an AI crawler hit a 404.
// Only renders when no other route matched, so a report from here is always a real 404.
// (Already have a root catch-all route? Put the fetch below in it, just before its notFound().)
import { headers } from 'next/headers';
import { notFound } from 'next/navigation';
const AI_BOTS = /OAI-SearchBot|ChatGPT-User|GPTBot|Claude-SearchBot|Claude-User|ClaudeBot|anthropic-ai|claude-web|Perplexity-User|PerplexityBot|Grok-DeepSearch|xAI-Grok|GrokBot|Google-Extended|Google-CloudVertexBot|Google-NotebookLM|GoogleOther|Applebot-Extended|Applebot|bingbot|meta-externalagent|meta-externalfetcher|FacebookBot|Bytespider|TikTokSpider|Amazonbot|bedrockbot|CCBot|cohere-ai|cohere-training-data-crawler|YouBot|DuckAssistBot|MistralAI-User|Diffbot|AI2Bot|Ai2Bot-Dolma|ImagesiftBot|Omgilibot|Omgili|Webzio-Extended|Timpibot|PetalBot|PanguBot|Kagibot|VelenPublicWebCrawler|iaskspider|FirecrawlAgent|SemrushBot-OCOB|SBIntuitionsBot|panscient|PoseidonResearchCrawler|aiHitBot|Andibot|QualifiedBot|YandexAdditional|FriendlyCrawler|img2dataset|Cotoyogi|Crawlspace/i;
const COLLECT = 'https://trusttraffic.net/api/collect';
const TOKEN = 'ttfc_YOUR_SITE_TOKEN';
export default async function Missing({ params }: { params: Promise<{ missing: string[] }> }) {
const h = await headers();
const ua = h.get('user-agent') ?? '';
if (AI_BOTS.test(ua)) {
await fetch(COLLECT, {
method: 'POST',
headers: { 'content-type': 'application/json' },
body: JSON.stringify({
token: TOKEN,
kind: 'notfound',
v: 2,
// The middleware's exact path when it's there, so the hit it logged matches.
path: h.get('x-trusttraffic-path') ?? '/' + (await params).missing.map(encodeURIComponent).join('/'),
userAgent: ua,
}),
}).catch(() => {});
}
notFound(); // renders your app/not-found.tsx with a 404 status
}- Already have middleware? Keep yours and add the
AI_BOTSblock to it. For crawler requests, keep thex-trusttraffic-pathheader line: the 404 route uses it to match the exact path. - Already have a top-level
[slug]or[...slug]route? Next.js won’t build two, so skip the second file and put itsfetchinto your route, just before itsnotFound(). - Pages Router: the middleware works as it is. The catch-all needs the App Router, so without it 404s are recorded as 200.
- Not in
app/not-found.tsx: Next.js renders that component on every page, so a report from there marks real pages as 404. - Using a coding agent? The dashboard’s setup panel has a ready-made prompt for Claude Code, Cursor or Copilot under “Installing with a coding agent?”.
Express
One middleware, added before your routes. It waits until the response is sent, so it reports the real status code, 404s included, with no second file. It needs Node 18 or newer (for the built-in fetch).
// TrustTraffic AI-crawler tracker (no dependencies). Reports the real status code, 404s included.
const AI_BOTS = /OAI-SearchBot|ChatGPT-User|GPTBot|Claude-SearchBot|Claude-User|ClaudeBot|anthropic-ai|claude-web|Perplexity-User|PerplexityBot|Grok-DeepSearch|xAI-Grok|GrokBot|Google-Extended|Google-CloudVertexBot|Google-NotebookLM|GoogleOther|Applebot-Extended|Applebot|bingbot|meta-externalagent|meta-externalfetcher|FacebookBot|Bytespider|TikTokSpider|Amazonbot|bedrockbot|CCBot|cohere-ai|cohere-training-data-crawler|YouBot|DuckAssistBot|MistralAI-User|Diffbot|AI2Bot|Ai2Bot-Dolma|ImagesiftBot|Omgilibot|Omgili|Webzio-Extended|Timpibot|PetalBot|PanguBot|Kagibot|VelenPublicWebCrawler|iaskspider|FirecrawlAgent|SemrushBot-OCOB|SBIntuitionsBot|panscient|PoseidonResearchCrawler|aiHitBot|Andibot|QualifiedBot|YandexAdditional|FriendlyCrawler|img2dataset|Cotoyogi|Crawlspace/i;
const COLLECT = 'https://trusttraffic.net/api/collect';
const TOKEN = 'ttfc_YOUR_SITE_TOKEN';
app.use((req, res, next) => {
const ua = req.headers['user-agent'] ?? '';
if (AI_BOTS.test(ua)) {
res.on('finish', () => {
fetch(COLLECT, {
method: 'POST',
headers: { 'content-type': 'application/json' },
body: JSON.stringify({
token: TOKEN,
path: req.originalUrl.split('?')[0],
userAgent: ua,
statusCode: res.statusCode,
ip: req.ip ?? null, // behind a proxy/CDN, set app.set('trust proxy', …) so this is the crawler's IP — it's what verifies the bot
}),
}).catch(() => {});
});
}
next();
});Behind a proxy or CDN, set Express’s trust proxy so req.ip is the crawler’s IP and not your load balancer’s. Without the real IP a hit can’t be verified.
Cloudflare Worker
Wraps your origin. Use this for any site behind Cloudflare, and for static hosts such as GitHub Pages, Netlify or S3, which have no server of their own to hook. Deploy the worker and add a route for your domain (Workers Routes → example.com/*). Already have a worker? Call your existing handler where the comment says, instead of fetch(request).
// TrustTraffic AI-crawler tracker (no dependencies). Wraps your existing handler.
const AI_BOTS = /OAI-SearchBot|ChatGPT-User|GPTBot|Claude-SearchBot|Claude-User|ClaudeBot|anthropic-ai|claude-web|Perplexity-User|PerplexityBot|Grok-DeepSearch|xAI-Grok|GrokBot|Google-Extended|Google-CloudVertexBot|Google-NotebookLM|GoogleOther|Applebot-Extended|Applebot|bingbot|meta-externalagent|meta-externalfetcher|FacebookBot|Bytespider|TikTokSpider|Amazonbot|bedrockbot|CCBot|cohere-ai|cohere-training-data-crawler|YouBot|DuckAssistBot|MistralAI-User|Diffbot|AI2Bot|Ai2Bot-Dolma|ImagesiftBot|Omgilibot|Omgili|Webzio-Extended|Timpibot|PetalBot|PanguBot|Kagibot|VelenPublicWebCrawler|iaskspider|FirecrawlAgent|SemrushBot-OCOB|SBIntuitionsBot|panscient|PoseidonResearchCrawler|aiHitBot|Andibot|QualifiedBot|YandexAdditional|FriendlyCrawler|img2dataset|Cotoyogi|Crawlspace/i;
const COLLECT = 'https://trusttraffic.net/api/collect';
const TOKEN = 'ttfc_YOUR_SITE_TOKEN';
export default {
async fetch(request, env, ctx) {
const response = await fetch(request); // ← your existing handler / origin
const ua = request.headers.get('user-agent') ?? '';
if (AI_BOTS.test(ua)) {
ctx.waitUntil(
fetch(COLLECT, {
method: 'POST',
headers: { 'content-type': 'application/json' },
body: JSON.stringify({
token: TOKEN,
path: new URL(request.url).pathname,
userAgent: ua,
statusCode: response.status,
ip: request.headers.get('cf-connecting-ip'),
}),
}).catch(() => {}),
);
}
return response;
},
};Any other stack
Anything that can see the request and make an HTTP call can report hits: PHP, Rails, Django, Go, nginx with a log shipper, and so on. When the User-Agent matches the bot regex from the snippets above, send one POST after the response:
curl -X POST https://trusttraffic.net/api/collect \
-H 'content-type: application/json' \
-d '{"token":"ttfc_YOUR_SITE_TOKEN","path":"/pricing","userAgent":"Mozilla/5.0 (compatible; GPTBot/1.2; +https://openai.com/gptbot)","statusCode":200,"ip":"203.0.113.7"}'| Field | What to send |
|---|---|
token | Your site token. Required. It goes in the body; there’s no auth header. |
userAgent | The request’s User-Agent, unchanged. Required. Checked again on our side; anything that isn’t an AI crawler is ignored. |
path | The path without the query string, e.g. /pricing. Defaults to /. |
statusCode | Your response status, 100–599. Defaults to 200. |
ip | The client’s IP, i.e. the crawler’s. Used for the IP check; without it the hit is “unknown”. |
ts | ISO time of the request. Defaults to now. |
kind | Leave it out for a crawler hit. "human" counts one human page view (only when human tracking is on for the site). "notfound" with "v": 2 marks the hit you just sent for that exact path as a 404. Only needed when you can’t send the real status in the first place. |
Answers: 200 with {"recorded":true,"bot":"GPTBot","ip":"verified"}, or {"recorded":false} when the User-Agent isn’t an AI crawler. 401 unknown_token means the token is wrong. 429 means over 300 hits a minute for one site, or 600 a minute from one IP. Send it without waiting for the answer, so tracking can never slow a page down.
Human page views (Pro and up)
For the bot-vs-human ratio, switch on Track human page views in the setup panel. Your code then gains one extra branch that sends {"kind":"human"} for real page loads (the browser’s Sec-Fetch-Dest: document header), and not for images, API calls or link prefetches. We store one number per site per day: no path, no User-Agent, no IP. Switching it off stops the counting straight away, even if the old code is still deployed.
Check it’s working
Pretend to be GPTBot against your live site:
curl -A "Mozilla/5.0 (compatible; GPTBot/1.2; +https://openai.com/gptbot)" https://example.com/
Within a few seconds the dashboard shows a GPTBot hit and the status button turns green. Its IP check says spoofed, which is correct: the request came from your computer, not from OpenAI. Real GPTBot visits show verified. To test the 404 signal, request a page that doesn’t exist, e.g. https://example.com/this-page-does-not-exist, and it appears as a 404.
Troubleshooting
Nothing shows up after the test
- Is the new code deployed to the site you tested? Local changes don’t count.
- Is the token right? A wrong one gets
401 unknown_token. Log the collect response once to see it. - Next.js:
middleware.tsmust sit next toapp/(root orsrc/), and itsmatchermust include the path you tested. - If a CDN serves the page from its cache, your server never sees the request. Cloudflare Workers run before the cache; with other CDNs, keep bots out of the cache or use the Worker.
- Static hosts have nothing to hook without Cloudflare in front. Use the Worker.
404s show as 200 (Next.js)
- The
app/[...missing]/page.tsxfile is missing, or another dynamic route answers that URL first and callsnotFound()itself. Add the report to that route.
Real crawlers show as “unknown”
- Some vendors publish no IP list (see IP checks), so their hits can only ever be unknown.
- No IP was sent. Behind a proxy, use the forwarded client IP (Express:
trust proxy).
The human count stays at 0
- The switch is off, the code wasn’t redeployed after switching it on, or the account is on Free.
Reading the dashboard
- Crawler hits: every AI-crawler visit by bot, page and day, in four groups. Answer fetches are a person asking an AI about your page right now; search indexing feeds AI search results; model training collects text to train models; other crawlers covers the rest.
- 404 signal (Pro and up): pages crawlers asked for that don’t exist. That’s demand for content you haven’t written, or links you broke. You also get a daily email digest when it happens.
- Bot vs human (Pro and up): AI-crawler hits against human page views, once you switch human tracking on.
- IP checks: verified means the IP is in the vendor’s published ranges or passes their reverse-DNS check. Spoofed means the vendor publishes a check and this IP fails it: someone using the bot’s name. Unknown means no IP, a vendor with nothing published, or a lookup that failed. A failed lookup is never counted as spoofed. Bots we can verify: OAI-SearchBot, ChatGPT-User, GPTBot, Perplexity-User, PerplexityBot, Google-Extended, Google-CloudVertexBot, Google-NotebookLM, GoogleOther, Applebot-Extended, Applebot, Bingbot, Amazonbot, CCBot, DuckAssistBot, MistralAI-User, PetalBot, YandexAdditional.
Citation checks (Pro and up)
Crawler hits tell you an AI read your page. Citation checks tell you whether its answers name you. Add the searches you care about (up to 200, each up to 100 characters) and pick engines. A check asks each engine your search and records the sites its answer recommends, then the pages it read, ranked. Your site counts as cited when its domain or a subdomain is in the results, or when the answer names it without a link (then there’s no rank).
| Engine | Tokens per search |
|---|---|
| Perplexity | 1 |
| Google AI Overviews | 1 |
| ChatGPT | 2 |
- Tokens: one search on one engine. Pro 100, Super 300, Supreme 800 a month, topping up on the day of the month you subscribed. Unused tokens don’t roll over.
- Only answers are charged. A check that fails, times out or comes back unreadable costs nothing. “Not cited” is an answer, so it is charged.
- Schedule: never, weekly or daily (UTC), plus “Run check now” whenever you like.
- Competitors (Super and up): up to 20 domains, called out whenever they show up in your results. No extra searches or tokens.
API
A read-only JSON API for everything the dashboard shows: crawler hits, 404s, human counts and citation results. Use it for reports, spreadsheets, BI tools or your own alerts. No key can change anything.
Create a key
- Open Dashboard → API Keys (Pro and up).
- Name it after what will use it, e.g. “Looker Studio” or “weekly report script”.
- Tick the data it may read, and choose all sites or only some.
- Copy the key (
ttk_…). It’s shown once. We keep only a hash, so we can’t show it again; if you lose it, revoke it and create a new one.
Keys per plan: Pro 1, Super 2, Supreme 5. After a downgrade the oldest keys keep working and the rest stop until you revoke some or upgrade. Revoking a key takes effect immediately.
| Access | What it can read |
|---|---|
| Sites | Your tracked domains and their ids. Never the tracker token. |
| Crawler hits | Every AI-crawler visit: bot, vendor, page, status, time, IP check result — plus the per-bot and per-day totals. |
| Crawler IP addresses | The raw IP on each crawler hit. Needs “Crawler hits” too. |
| 404 signal | Pages AI crawlers asked for that returned 404, with counts and which bots. |
| Human page views | The per-day human count behind the bot-vs-human ratio (if you track humans). |
| Citation checks | Your queries, engines, schedule and every check result (competitors included on Super+). |
Authentication
Send the key as a bearer token over HTTPS. Every endpoint is GET and returns JSON. Keep the key on a server or in a script, never in browser code or a public repo.
curl -H "Authorization: Bearer ttk_YOUR_API_KEY" https://trusttraffic.net/api/v1/me
/api/v1/me works with any key, so it’s the quickest way to test one.
Endpoints
| Endpoint | Needs | Returns |
|---|---|---|
/api/v1/me | any key | This key: name, scopes, sites. Use it to test a key. |
/api/v1/sites | Sites | Sites this key can see, with their ids. |
/api/v1/sites/{siteId}/crawlers?days=30&limit=1000 | Crawler hits | Crawler hits + per-bot, per-category, per-day totals. Each hit’s raw IP needs “Crawler IP addresses”. |
/api/v1/sites/{siteId}/not-found?days=30 | 404 signal | 404s AI crawlers hit, by page and bot. |
/api/v1/sites/{siteId}/humans?days=30 | Human page views | Human page views vs crawler hits. |
/api/v1/sites/{siteId}/citations?days=30 | Citation checks | Citation-check setup and results. |
days: how far back to look, 1 to 90. Defaults to 30.limit(crawlers only): how many individual hits to return, newest first, 1 to 5000. Defaults to 1000. Thesummaryalways covers every hit in the window;truncated: truemeans there were more hits thanlimit.- Get site ids from
/api/v1/sites.
Responses
Real shapes, shortened. Times are ISO 8601 in UTC. ipVerdict is verified, spoofed or unknown. A hit carries ip only when the key has “Crawler IP addresses”. In citation results, source: true marks a page the answer read rather than a site it recommends. Competitor fields appear on Super and up.
{
"name": "weekly report script",
"prefix": "ttk_Ab12Cd",
"plan": "super",
"scopes": [
"sites",
"crawlers",
"notFound"
],
"sites": "all"
}{
"sites": [
{
"id": "5f0c2a1e-8b4d-4c3a-9e2f-7a1b3c4d5e6f",
"domain": "example.com",
"createdAt": "2026-09-21T15:11:28.299Z",
"trackHumans": true
}
]
}{
"site": {
"id": "5f0c2a1e-8b4d-4c3a-9e2f-7a1b3c4d5e6f",
"domain": "example.com"
},
"days": 7,
"summary": {
"total": 142,
"uniqueBots": 9,
"uniquePages": 23,
"ipChecks": {
"verified": 88,
"spoofed": 3
},
"byBot": [
{
"name": "GPTBot",
"vendor": "OpenAI",
"category": "training",
"count": 51
},
{
"name": "ClaudeBot",
"vendor": "Anthropic",
"category": "training",
"count": 34
}
],
"byCategory": [
{
"key": "user",
"label": "Answer fetches",
"count": 12
},
{
"key": "search",
"label": "Search indexing",
"count": 40
},
{
"key": "training",
"label": "Model training",
"count": 85
},
{
"key": "crawler",
"label": "Other crawlers",
"count": 5
}
],
"byDay": [
{
"date": "2026-09-18",
"count": 17
},
{
"date": "2026-09-19",
"count": 22
}
],
"topPages": [
{
"path": "/pricing",
"count": 31,
"last": "2026-09-24T06:12:32.088Z"
}
]
},
"hits": [
{
"bot": "GPTBot",
"vendor": "OpenAI",
"category": "training",
"path": "/pricing",
"status": 200,
"ipVerdict": "verified",
"ts": "2026-09-24T06:12:32.088Z"
},
{
"bot": "ClaudeBot",
"vendor": "Anthropic",
"category": "training",
"path": "/old-page",
"status": 404,
"ipVerdict": "unknown",
"ts": "2026-09-24T05:52:11.207Z"
}
],
"truncated": true
}{
"site": {
"id": "5f0c2a1e-8b4d-4c3a-9e2f-7a1b3c4d5e6f",
"domain": "example.com"
},
"days": 30,
"total": 6,
"pages": [
{
"path": "/old-page",
"count": 4,
"last": "2026-09-24T05:52:11.207Z",
"bots": [
{
"name": "ClaudeBot",
"count": 3
},
{
"name": "GPTBot",
"count": 1
}
]
}
]
}{
"site": {
"id": "5f0c2a1e-8b4d-4c3a-9e2f-7a1b3c4d5e6f",
"domain": "example.com"
},
"days": 30,
"trackHumans": true,
"humans": 1240,
"bots": 142,
"botShare": 0.103
}{
"site": {
"id": "5f0c2a1e-8b4d-4c3a-9e2f-7a1b3c4d5e6f",
"domain": "example.com"
},
"days": 30,
"config": {
"queries": [
"best ai crawler tracker"
],
"engines": [
"perplexity",
"google"
],
"schedule": "weekly",
"lastRunAt": "2026-09-21T19:17:40.964Z",
"competitors": [
"rival.com"
]
},
"runs": [
{
"query": "best ai crawler tracker",
"engine": "perplexity",
"ts": "2026-09-21T19:17:40.281Z",
"cited": true,
"rank": 3,
"results": [
{
"rank": 1,
"domain": "rival.com",
"url": "https://rival.com/"
},
{
"rank": 3,
"domain": "example.com",
"url": "https://example.com/"
},
{
"rank": 4,
"domain": "blog.somewhere.io",
"url": "https://blog.somewhere.io/ai-crawlers",
"source": true
}
],
"mentions": [],
"competitorsCited": [
"rival.com"
]
}
]
}Errors and limits
Errors are JSON too: {"error":"scope","message":"…"}. The message is written for people.
| Status | error | Meaning |
|---|---|---|
| 401 | missing_key | No "Authorization: Bearer ttk_…" header, or it isn’t a TrustTraffic key. |
| 401 | invalid_key | The key doesn’t exist or was revoked. |
| 403 | plan | The account’s plan doesn’t include the API, or that data (404s, humans, citations need Pro). |
| 403 | plan_limit | The account holds more keys than its plan allows; the oldest keep working, this one doesn’t. |
| 403 | scope | The key wasn’t given access to this data. Edit what it can read in API Keys. |
| 404 | site | No such site, or not one this key may see. |
| 429 | rate_limited | More than 60 requests a minute on this key. |
Each key may make 60 requests a minute, and each IP 600 a minute across all keys. Data only changes as crawls arrive, so polling every few minutes is plenty. After a 429, wait a minute.
Code examples
// Node 18+ (or any runtime with fetch). Keep the key on the server, never in browser code.
const KEY = process.env.TRUSTTRAFFIC_KEY;
const api = async (path) => {
const res = await fetch('https://trusttraffic.net' + path, { headers: { Authorization: `Bearer ${KEY}` } });
const body = await res.json();
if (!res.ok) throw new Error(`${res.status} ${body.error}: ${body.message}`);
return body;
};
const { sites } = await api('/api/v1/sites');
for (const site of sites) {
const { summary } = await api(`/api/v1/sites/${site.id}/crawlers?days=7&limit=1`);
console.log(site.domain, summary.total, 'AI crawler hits this week');
}import os, requests
KEY = os.environ["TRUSTTRAFFIC_KEY"]
BASE = "https://trusttraffic.net"
def api(path):
r = requests.get(BASE + path, headers={"Authorization": f"Bearer {KEY}"}, timeout=30)
r.raise_for_status()
return r.json()
for site in api("/api/v1/sites")["sites"]:
missing = api(f"/api/v1/sites/{site['id']}/not-found?days=30")
for page in missing["pages"]:
print(site["domain"], page["path"], page["count"])Plans and limits
| Free | Pro | Super | Supreme | |
|---|---|---|---|---|
| Price / month | $0 | $9 | $19 | $39 |
| Unlimited AI crawler tracking | ✓ | ✓ | ✓ | ✓ |
| Bot-vs-human traffic ratio (opt-in human tracking) | — | ✓ | ✓ | ✓ |
| 404 signal + daily notifications | — | ✓ | ✓ | ✓ |
| Citation checks: ChatGPT, Perplexity, Google AI | — | ✓ | ✓ | ✓ |
| Competitor citations + notifications | — | — | ✓ | ✓ |
| Citation tokens / month | — | 100 | 300 | 800 |
| API keys | — | 1 | 2 | 5 |
Pricing has yearly prices and how to switch plans.
Your data
For each crawler hit we store the path, the crawler’s name, vendor and category, the status code, its User-Agent and IP, the IP check result, and the time. That’s data about bots, not about your visitors. Human tracking stores one count per day and nothing else. Deleting a site deletes its hits, human counts and citation results immediately. The full detail is in the privacy policy.
Stuck, or missing something here? Email hello@trusttraffic.net.