Bueno serves your blog over an API and counts every fetch, from search crawlers and AI crawlers to the humans who arrived because an AI cited you. Then it tells you what to write next.
A crawler is not a visit. Bueno keeps them apart, day by day, so the moment an AI answer starts sending you readers is a line you can actually point at.
Sample data for illustration. Not Bueno’s own measured traffic.
User-Agent first, then referrer. A hit is only ever “human” if it matches no known bot.
Googlebot, Bingbot, Applebot, YandexBot. You're indexable.
GPTBot, OAI-SearchBot, ChatGPT-User, ClaudeBot, PerplexityBot, CCBot. You're being ingested.
A human whose referrer is chatgpt.com, perplexity.ai, claude.ai, or gemini. An AI cited you and someone clicked.
Everyone else: organic search, direct, internal, or a referring site.
SEO suites (AhrefsBot, SemrushBot), link unfurlers, uptime monitors, HTTP clients.
“AhrefsBot is not a search engine.” It crawls for a rank tracker, not to index you for readers. Bueno keeps it out of your search-crawler count.
One cited analysis put the “direct” misattribution rate for AI-referred visits at 70.6%. Neither failure mode is a config problem. They’re structural to a client-side tag.
API-first: posts, categories, series, multi-locale content and assets, served over /api/v1/* to your own frontend. Replaces Sanity, and it’s free.
Every opportunity is a deterministic query over your own posts and hits, with no LLM in the loop deciding what’s wrong.
ingestion_blindTraffic is arriving but no crawler has ever been recorded.
Not how a live site behaves. Check the forwarder before trusting anything else on this page.
ai_crawlers_absentSearch engines crawl this site but no AI crawler ever has.
Usually a one-line robots.txt block on GPTBot, ClaudeBot or PerplexityBot.
not_discoveredLive for weeks and no search crawler has fetched it, while the rest of the site gets crawled fine.
Points at a per-page block. Check noindex and canonical tags first.
ingested_not_citedAI crawlers fetched this repeatedly and it has never sent back a single reader.
It's being read, but it isn't answer-shaped. The flagship rule, walked through below.
orphanNo other post links to this one.
A crawler walking your site never arrives at a page with no inbound links, however often it's submitted.
never_submittedThis post has never been accepted by an indexing provider.
Nobody told the index it exists yet. Submit it from Settings → Indexing.
decayingVisits fell hard against the prior window.
Something aged. A stale price, an old version, a dead link. Refresh, don't rewrite.
Point Claude Code or Cursor at your project’s MCP endpoint and it can read getOpportunities, pull the flagged post, rewrite it, and write it back, the same loop the dashboard runs, without you retyping anything between panels.
No 14-day trial. It would expire on the exact day the best findings start to fire. Free tier keeps 30 days of history; nothing about it is time-boxed.
A seed brief: ranked keywords, who owns the SERP, whether an AI Overview already exists. Keyed off your domain alone. Nothing to install yet.
ingestion_blind confirms the forwarder actually works, the moment traffic starts arriving.
ai_crawlers_absent fires if AI crawlers still haven't shown up, and the first real stacked chart takes shape.
ingested_not_cited, the flagship rule. Crawled enough times that citation silence stops being noise.
not_discovered. A post this old with zero search-crawler hits, while the rest of the site is crawled, means something.
decaying and the Trend column, once there's a comparable stretch behind it to measure against.
Your backend already renders the page for Googlebot, GPTBot, and the human. Forward what it saw and Bueno does the classifying.
// middleware.ts
export const config = { matcher: ["/blog/:path*"] }
export function middleware(req: NextRequest) {
const send = fetch("https://usebueno.com/api/v1/hits", {
method: "POST",
headers: {
authorization: `Bearer ${process.env.BUENO_INGEST_KEY}`,
"content-type": "application/json",
},
body: JSON.stringify({
path: req.nextUrl.pathname + req.nextUrl.search,
ua: req.headers.get("user-agent") ?? "",
referer: req.headers.get("referer") ?? undefined,
country: req.headers.get("cf-ipcountry") ?? undefined,
}),
}).catch(() => {})
ctx.waitUntil(send) // fire-and-forget, never blocks the page
return NextResponse.next()
}The free tier is the trial. No countdown, no credit card to start.
Visits count humans and AI referrals only. Crawlers are never billed. Caps are per org, and projects are unlimited.