---
title: "Reporting"
url: "/docs/reporting"
canonical_url: "https://crunchjunkie.io/docs/reporting"
markdown_url: "https://crunchjunkie.io/docs/reporting.md"
language: "en"
type: "doc"
summary: "The report builder, widgets, AI summaries, scheduling, white-label, and export."
site: "CrunchJunkie"
llms_txt: "https://crunchjunkie.io/llms.txt"
---

# Reporting
Source: https://crunchjunkie.io/docs/reporting

The report builder, widgets, AI summaries, scheduling, white-label, and export.

## Draft with AI
Draft with AI lets Crunch assemble a full, first-draft report for you instead of starting from a blank page. In the report builder, click Draft with AI, pick the client, and — optionally — type a short focus (for example, "explain the ROAS dip in June") and/or choose an objective: Lead gen, E-commerce ROAS, Brand awareness, Traffic, or App installs. Then hit Generate.

Crunch builds the report from that client's connected data sources — Google Ads, GA4, Meta, Microsoft Ads, Amazon Ads, Reddit Ads, Search Console, Bing Webmaster Tools, LinkedIn, Matomo, Piwik PRO, Brevo, Mailchimp and Google Business Profile. When the client has an active AI Visibility project, Crunch now brings that in too: it automatically adds an AI Visibility section (visibility, share of voice, competitors) and a GEO Audit section (AI-readiness), so a client's AI presence lands in the same drafted report as their paid and analytics channels. It lays out sections, KPIs, charts, tables and a plain-language executive summary, and adds a cross-channel comparison automatically when more than one paid channel is connected: it opens the report, right after the summary, with the blended totals (spend, conversions, leads and cost per lead across channels), then each channel follows, and the recommendations close it. The focus and objective you provide steer which metrics and sections Crunch leads with.

## Every figure comes from your connected data
Every metric in a Draft with AI report comes from a vetted, per-source catalog and the client's actual data — never from the model's imagination. Crunch chooses which widgets and metrics to include and writes the narrative, but the numbers are pulled straight from your connected sources, the same way a widget you drag in by hand would be. That's the key difference from tools that let an AI free-type figures into a report: there are no invented KPIs and no fabricated trends.

If a source isn't connected for that client, Crunch simply leaves it out rather than guessing. The result is a draft you can trust to send after a quick review, not one you have to fact-check line by line.

## How Crunch AI uses your data
Crunch AI writes from your real data. Every metric that appears in a report's widgets is pulled straight from your connected data sources and validated against a vetted, per-source metric catalog. The summary itself is written by a language model, so it is given only the report's figures — each change stated the way the KPI cards state it (points for a rate, % for a count or an amount, "~ within margin" where the card says so), with the period it covers and the answers behind it — and told to add no figure of its own and to state no cause the figures don't show. In the editor, percentages and amounts in a summary that can't be found in the report's data are flagged. That check catches some slips, not all, so read the summary before it goes out.

Only the report's aggregated metrics — the numbers already shown in the report, plus their comparison-period deltas — are sent to the AI model to generate a summary, together with, on a report with Google Ads figures, the period's Google Ads account changes (who made a change is sent as a role such as "a person" or "Script", never an email address). Crunch doesn't send raw exports, and the metrics it does send are used solely to write that summary: on the API tiers CrunchJunkie uses, your data is not used to train the providers' models. On Managed AI, summaries are written by Claude on CrunchJunkie's Anthropic key. If you'd rather use your own account, add your own key (Anthropic, OpenAI or Gemini) under Settings → AI & API Keys and toggle on "Power CrunchJunkie AI" — Crunch will run summaries through it instead. Without that, an OpenAI, Claude or Gemini key you added for AI-visibility scans is used. Either way, AI output is informational, streamed back for you to edit, and never sent to a client until you approve it.

## Editing an AI-drafted report
A drafted report opens straight in the drag-and-drop builder, fully editable — it is a normal report in every way. If a widget doesn't look right, click it to select it, then change its metric, breakdown, source or date range in the side panel — or just tell Crunch what you want in plain language ("make this a bar chart", "show top 5 by ROAS", "break down by device"). A widget that comes up empty usually means its data source needs connecting on the Data page. Rearrange or resize widgets, swap metrics and dimensions, change the date range or comparison period, edit or regenerate the executive summary, and add your own text blocks and commentary. Nothing is locked.

Because Draft with AI produces the same kind of report you'd build by hand, you can save it as a template, white-label it, schedule it, and export it to PDF like any other. Use it as a fast starting point for a new client, or run it whenever you want a fresh, data-grounded first pass to edit down.

## The report builder
The builder is a drag-and-drop canvas that always edits against live data. When you create a report, CrunchJunkie picks a smart default layout for the channel you're reporting on — paid search, social, or SEO — so the most useful widgets are already in place. Drag any widget to reorder it, resize it across the grid, or remove it entirely.

Each report is tied to a client and a date range. Change the range at the top and every widget updates at once. Add a comparison period (previous month, previous year, or a custom window) and your metrics gain change indicators automatically. Because there's no separate render step, the canvas you build on is identical to what your client sees — no surprises after you hit send.

## Widgets and data modules
Widgets are the building blocks of a report. Scorecards show a single headline metric with its change versus the comparison period. Time-series charts plot a metric across the date range (several counts from one source of very different size, such as LinkedIn impressions beside clicks and leads, get a second axis on the right, so the small ones do not sit flat at zero). Tables break performance down by campaign, ad group, landing page, or any other dimension the source exposes. There are also channel-split donuts, goal-progress bars, and text blocks for your own commentary, with a formatting toolbar: headings, lists, quotes, alignment, links, text colours and highlights that follow the report's theme (so they read in light and dark mode and in the PDF), small or large text, callout boxes (note, tip, warning, important) and divider lines.

Every widget pulls from a connected data source, and many can blend metrics across channels — for example, total spend across Google Ads and Meta in one scorecard. Click any widget to change its metric, dimension, visualisation, or date treatment. Sensible defaults mean you can often leave a smart layout almost untouched and still ship a clean, accurate report.

Two small touches keep reports tidy. The "Hide title" toggle in a widget's settings removes its heading in the report, live link and PDF — handy when a scorecard's label is already obvious or you want a cleaner cover. "Start on a new page" in the same panel makes a widget open a fresh page in the PDF and when printing (sections always do; the live link is unaffected), and the report's settings have a "Show cover page" switch for reports that should begin straight with the numbers. And undo/redo (⌘/Ctrl-Z, and ⌘/Ctrl-Shift-Z to redo, or the arrows in the toolbar) step back and forward through your edits, so experimenting with a layout is always safe.

## Ad previews, ad-level tables and audience breakdowns
Beyond the standard scorecards and charts, some widgets show the work itself. An ad-preview widget renders real ad cards — the creative's image or video thumbnail, its headline and copy, and that ad's own performance stats — so a client sees the actual ads their budget ran, not just aggregate numbers. Ad previews are available on Google Ads, Meta and LinkedIn (add one from the palette, or ask Crunch to "show the top ads with their images"). Where a platform's API doesn't expose a creative's image or text to us, the card falls back to the ad's name, type and stats rather than showing a blank — never a fabricated preview. For a plain metrics grid of individual ads instead of cards, use a table broken down by "Ad" (Meta and LinkedIn), where each row carries the ad's real name.

Like any other widget, an ad preview takes the report's date range or its own, and a limit on how many ads to show. On Google Ads it also accepts filters — by campaign name, type or status, and by ad group — so a preview can be scoped to a single campaign. Meta and LinkedIn ad previews don't filter yet; they show every ad that delivered in the period, up to the limit.

LinkedIn also reports who saw and engaged with your ads, and you can put that straight into a report. A LinkedIn table broken down by Seniority, Job function, Industry or Company size turns campaign delivery into an audience view — which career levels, departments, industries or company sizes your budget actually reached — using LinkedIn's own demographic reporting. Industries carry the names from LinkedIn's current industry list, in your report's language where LinkedIn offers it; an industry we can't name shows as "Industry 3243" ("Branche 3243" in a German report), never an invented name. Company sizes use your report's number format (1,001–5,000 in English, 1.001–5.000 in German). One honest detail worth knowing: LinkedIn suppresses any segment too small to be privacy-safe, so those rows simply don't appear in the table rather than being estimated or filled in. As with every widget, the numbers come straight from the source; nothing is modelled.

## Cross-channel "All channels" widgets
When a client has two or more paid ad accounts connected — Google Ads and Meta, any mix with Microsoft, Amazon, Reddit, ChatGPT and LinkedIn Ads, or simply two Google Ads accounts — you can roll them into one blended widget. Add a KPI, line, bar or table widget and choose "All channels" as its data source. Every connected account of every paid channel is included.

Blended metrics are Spend, Impressions, Clicks, CTR, Avg. CPC, CPM, Conversions, Conv. rate, Conv. value, Cost per conv., ROAS, Leads, Cost per lead and Lead rate. Leads are Google Ads and Microsoft Ads primary conversions plus Meta leads plus LinkedIn Lead Gen Form leads; Amazon, Reddit and ChatGPT Ads count towards spend but not towards leads. Cost per lead is the spend of the channels that report leads divided by their leads. For a lead-gen client, switch the widget's **Lead-gen blend** on (or ask the editor to "count leads as conversions"): every channel's conversions become its leads, so Conversions, Conv. rate and Cost per conv. read leads throughout and the report never shows two numbers for one thing; a channel without leads then adds no conversions.

A blended chart plots one metric by day, by calendar week (ISO weeks, Monday to Sunday, shown as KW in German reports) or by month, as a line or bar per channel, as one blended total, or both. A blended table shows one row per channel plus a Total row, with the columns you choose, including each channel's share of spend.

The maths is done properly. Sums are added up, and every rate (CTR, CPC, cost per lead, ROAS) is recalculated from those totals inside each day, week or month, never averaged across channels, so a blended cost per lead for March is March's spend divided by March's leads. Money is added only within the dominant currency and nothing is converted: a channel billed in another currency still counts towards clicks and leads, but not towards spend. A value a channel doesn't report is left out rather than counted as zero, and a week with spend but no leads shows a gap instead of a cost per lead of zero. ROAS is the conversion value divided by the spend of only the accounts that report a conversion value for the period (a reported value of zero counts). An account that reports none, such as a ChatGPT Ads account without purchase events, keeps its spend in Spend, CPC and cost per conversion but not in ROAS, which would otherwise count its spend against no value and understate the return. When that leaves an account out, the widget says so underneath, for example "ROAS from the 3 accounts (of 4) that report conversion value". Only active accounts are counted: one with no spend and no value in the period is in neither number. Which accounts drop out: a ChatGPT Ads account without purchase events, and a Reddit Ads account whose value field the API refuses. Google, Meta, Microsoft, Amazon and LinkedIn Ads report a value of zero when there is none, so their spend stays in ROAS. When no account reports a value, ROAS shows a dash and "no conversion value reported", never zero.

The same rule holds for every ratio of a result to what bought it: cost per conversion, conversion rate, cost per lead and lead rate count only the channels that report that result for the period. Each is paired from the same accounts, so spend or clicks never sit over results they did not produce. A channel that spent but reports no leads, such as Amazon, Reddit or ChatGPT Ads, keeps its spend in Spend, but not in cost per lead, and the widget says so underneath, for example "Cost per lead from Google Ads and Meta Ads: Amazon Ads (€500) reports no leads". A channel that reports zero stays in: its spend bought nothing countable, and leaving it out would flatter the figure. Charts pair each day, week or month on its own. When the channels in a ratio change between the two periods, the comparison shows no prior data, as ROAS does. One case gets its own warning: a channel that reports zero of the widget's conversions but leads in the same period, like Meta in a lead-gen account with zero purchases and 169 leads. Its spend is in cost per conversion and none of its leads are, so the widget says so and suggests the lead-gen blend, which the builder offers in one click. It never switches by itself. With the lead-gen blend on, the reverse applies: a channel with zero leads but purchases gets the warning.

One caveat is worth telling clients: each platform attributes conversions by its own rules, so someone who clicked both a Google ad and a Meta ad can be counted by both. Blended leads are the sum of what the platforms report, not a de-duplicated count.

If the client has GA4 connected, you can put the de-duplicated number right beside it. Add the metric "Leads (GA4)" (and "Cost per lead (GA4)") to the blended widget and choose which GA4 key event counts as a lead — the builder lists the key events the property actually recorded in the last 90 days. That metric counts the event once for the whole site, however many ads the person clicked first, so the gap between "Leads" and "Leads (GA4)" is the double-counting, in the client's own numbers. The two are never added together and never replace each other: they answer different questions, and both are shown. Until an event is chosen the GA4 lead metrics report nothing at all rather than a zero, and in a by-channel table they appear on the Total row only, because a site-wide count belongs to no single channel. Cost per lead (GA4) is shown only when every spending channel bills in one currency — with mixed currencies there is no honest way to divide one currency's spend by the whole site's leads, so it shows a dash.

You can build these widgets by hand, start from the Multi-channel template, ask Crunch ("leads by month across all channels", "blended cost per lead by calendar week"), or use the MCP connector: create_report with the "All channels" template, then add_widget and edit_widget. A Draft-with-AI report adds a cross-channel section automatically whenever two or more paid channels are connected.

## Budget vs actual by product line
An "All channels" table can compare your planned budget with what was really spent, line by line. In the report builder choose an All channels table, switch Rows to "Budget vs actual" and add your product lines. Each line has a name, the campaign-name terms that find its campaigns, and its budget: a default per month and/or an amount per calendar month (paste a whole year from a spreadsheet at once). Ask Crunch can set it up too ("budget vs actual: Health insurance = campaigns containing PKV or KV_DE, 1,500 a month"), and the MCP connector's add_widget and edit_widget take the lines as data in budgetLines (no AI), always as the complete list.

How campaigns are matched. A campaign belongs to the first line, in the order you list them, whose terms its name contains, ignoring upper and lower case. "PKV" finds "PKV | Spanien" on Google Ads and "Allianz PKV" on Meta alike. A campaign no line claims goes to a "Not assigned" row, so the Total row always equals what your connected accounts spent, after the report's own exclusions (Meta campaigns left out by name, a Google Ads campaign filter). Put the more specific line first when two lines could claim the same campaign.

The columns are Product line, Planned budget, the actual spend of each channel, Total actual, Difference (planned minus actual, so a positive figure is not spent yet) and % of budget used. The planned budget for the period is pro-rated by days for each calendar month it touches: 15 to 31 October takes 17/31 of October's budget, and a period over several months adds up the pro-rated months. A line without a budget for a month of the period shows "—" for Planned budget, Difference and % used, never 0, and the Total row shows no planned budget while one line has none; one sentence under the table names the month and the lines ("No budget set for September 2026 on KFZ"). When no line has a typed budget for the period at all, the Planned budget, Difference and % of budget used columns are left out rather than filled with dashes; the note says "No budgets set for September 2026", and only in the report editor a quiet line says where to add them (this widget's settings, Amounts per month). If the report compares periods, a "Spend, previous period" column shows each line's spend then; budgets are not compared. Dates in the notes are written in the report's language.

The progress bar. Beside each line's % of budget used sits a slim bar: the line's spend divided by its planned budget, the same number the cell prints. The Total row's bar is the total spend divided by the total planned budget, never an average of the lines' bars, and it appears only when the Total has a planned budget. Past 100 % the bar is full and drawn in the warning colour, and the number turns the same colour. While the period is still running inside one month, a thin mark on the bar shows Expected by today as a share of the budget, so you can see at a glance whether a line spends ahead of plan or behind it. A line without a budget has no bar. The bars are the same in the report, on the share link and in the PDF.

When the period lies within the current month, two more columns appear. Expected by today is the planned budget for the complete days up to yesterday; Projected month-end is the spend up to yesterday divided by those days and multiplied by the days to the end of the month. It is a straight-line projection at the current pace, not a forecast, and the note under the table says which days it covers.

Planned budget from your campaign settings. Instead of typing budgets, switch "Planned budget from" in the widget settings to "Campaign settings" (or "Settings, hand where typed", where an amount you typed for a line and month wins). Google Ads and Meta only show the budgets set today, never a history, so CrunchJunkie reads every connected Google Ads and Meta account once a day (shortly after 04:00 UTC, two to four calls per account) and keeps each campaign's budget per day. A line's planned budget is then the sum of its campaigns' daily budgets, day by day, counting a campaign only while it was enabled: paused, ended or not yet started campaigns count 0. Google Ads: a daily budget counts per day, capped at Google's own monthly limit of 30.4 times the daily budget (Google may spend up to twice the daily budget on one day but charges at most that in a month), so a 31-day month counts 30.4/31 per day. A Google Ads shared budget is counted once; if it serves campaigns of several lines it is not split by guessing but shown in its own "Shared budget" row. Meta: the campaign budget, or, without one, the budgets of its active ad sets added; a lifetime budget is spread evenly over its start and end dates. The report's exclusions (Meta campaigns left out by name, the Google Ads campaign filter) apply to the plan as to the spend.

"Enabled" follows the campaign's own status (Google Ads ENABLED, Meta ACTIVE), plus the end and start dates Google reports as ended or not yet started; other Google serving states such as SUSPENDED are not treated as paused, so such a campaign still counts at its budget.

What the history can and cannot say. The first reading is the first day the planned budget is known. Days of the current month before it use today's settings, and the note under the table says "Planned from today's budget settings for days before" that date; the rest of the current month is planned at the latest reading. An earlier month without readings shows "—" with "Budget settings history starts" that date; nothing is invented, and a day the daily reading missed uses the previous reading for up to three days, as the note says. Campaigns removed since are not in today's settings, so they are missing from days before the first reading. Channels other than Google Ads and Meta are not read from settings yet: while one is in the table the planned budget shows "—" and the note names it (restrict the table's channels or type the budgets). In the AI data block, get_report_data and the REST API, budget.plannedSource says which source applied. The MCP tools add_widget and edit_widget take plannedSource ("manual", "settings" or "settings+manual"), and Ask Crunch understands "planned budget from the campaign settings".

Honest edges. A channel that could not be read is named under the table and left out of the sums, never counted as 0. A channel billed in another currency than the main one is not added up (nothing is converted), and budgets are read in the main currency. Every campaign that spent in the period is read from each platform with its exact spend (to the cent; the table prints whole currency units), including Google Ads campaigns removed since, so Not assigned is only what no line claims. Beyond 5,000 campaigns on one channel the smallest count as Not assigned, and the table says so. Budgets are in one currency, shown in the column headers and kept on the widget from the first time it loads; if an account later bills in another currency it is left out and named, never relabelled. Spend is read in each ad account's own time zone, so near midnight "through yesterday" can differ by a day. In the AI data block, MCP get_report_data and the REST API the table carries rows with planned, actual and difference as numbers, where null means no budget set.

## Currencies
Every amount in a report is shown in the currency the connected account itself uses: the Google Ads account's currency, the Meta, Microsoft, Amazon, Reddit, ChatGPT and LinkedIn ad account's, the GA4 property's for revenue, the Matomo site's, the Brevo account's display currency and the Mailchimp store's currency for attributed revenue. We read it from each platform and refresh it daily, and it carries through every widget — KPI cards, tables, chart axes and tooltips, donuts, maps, funnels and ad previews — in the app, on share links and in PDFs. Nothing is ever converted: a GBP account shows pounds, an AUD account Australian dollars. When different currencies meet in one blended "All channels" widget, money is added up only within the currency carrying the most spend, as described above, and the other accounts still count towards clicks, conversions and leads.

## Targets on KPI cards
Any KPI widget can carry a target per metric. Set it in the builder (Targets, below the widget's metrics) or ask Crunch, for example "set targets of 500 leads and a cost per lead of 180 € on the totals card". A target applies to the widget's own date range, so for a quarterly goal set the report or the widget to This quarter.

The card then shows a bar and the attainment: actual divided by target, or target divided by actual for metrics where lower is better (cost per lead, CPC, a spend cap), so 100 % or more always means the target is met. While the period is still running, sums such as leads, clicks and spend also show the plan to date (target × days elapsed ÷ days in the period, marked as a tick on the bar), the pace against that plan and a straight-line projection for the end of the period. Rates are never projected this way, because a rate doesn't accumulate over time; they are compared with the target directly. Targets look the same in the app, on share links and in PDFs.

## Edit a widget with Crunch
Every data widget has an "Edit with Crunch" box in its settings, so you can change it in plain language instead of hunting through menus. Type an instruction — "make this a bar chart", "show top 5 by ROAS", "break down by device" — and click Apply. Crunch adjusts that one widget's metric, dimension, visualisation or sort to match, still against live data. The card's own toolbar has the same Chomps button — click it and the cursor lands straight in that widget's box.

It's the fastest way to reshape a widget when you know what you want but not exactly which control to reach for. As with everything in the builder, the change is undoable, so you can try an instruction and step back if it isn't quite right.

How a table is sorted depends on its source. A Search Console table or donut, the AI Visibility brand ranking and the breakdown tables of Google Ads, Google Analytics, Meta Ads, Microsoft Ads, LinkedIn Ads, Amazon Ads, Reddit Ads, ChatGPT Ads, Piwik PRO, Matomo and Merchant Center have a Sort setting (by any metric they show, highest or lowest first; for a position or a cost column, lowest is best), and the sort is applied before the row limit, so a 50-row table of queries by impressions holds the 50 queries with the most impressions, and a 10-row table of campaigns sorted by cost holds the 10 campaigns that cost the most. Without a Sort, a table keeps the order it has always had, still applied before the row limit: Google Ads campaigns and ad groups by clicks, search terms and placements by impressions and asset groups by conversions, Merchant Center by number of products, LinkedIn Ads in LinkedIn's own order, and the others by their first metric, highest first. A sort saved before a source offered one is not used until you pick one again. Rows are ordered by the number, never by the text shown, and a row with no value goes last. Sorted lowest first, a Google Ads table leaves out rows with nothing to measure (no impressions, or no clicks or conversions behind a cost or rate per click or conversion), so they never show as the lowest. Tables by day, week, month or date keep date order, and the Google Ads tables by day of the week and by age keep their natural order. Other sources set their own order (email campaigns newest first, for example); there Crunch tells you it can't re-sort the table rather than saving a sort that would be ignored.

## Crunch AI summaries
Crunch AI reads the live numbers inside a report and writes a short executive summary — typically three numbered insights that call out what changed and why it matters. It flags anomalies, such as a sharp ROAS drop or a budget-limited campaign, so your client hears about issues from you first rather than spotting them in the data.

The summary streams in within seconds and is fully editable: tweak a sentence, delete an insight, or regenerate the whole thing if the report's date range or data changes. AI output is informational and always under your control — nothing is sent to a client until you're happy with it.

On a report with Google Ads figures, the executive summary, Campaign insights, Wins, Issues, Action plan, a custom question, and "Analyse the widget above/below" on a Google Ads widget also read the period's account changes from the stored change history: the 12 most significant (budget, bidding and status changes first, then keywords and targeting, minor edits such as renames and ad text last), with the total for the period. They come from what CrunchJunkie has stored, never from Google live, so a summary is never held up by Google; for a period that runs into today, the summary is told up to when changes are recorded (the nightly copy). Crunch mentions a change only where it plausibly explains a movement in the numbers, and then says it coincides with or likely contributed to it, never that it caused it; it does not list changes for their own sake. When a summary read them, the "Based on" line under it says how many: "· 12 of 45 account changes" when it read the 12 most significant of 45, or "· 3 account changes" when it read them all. Nothing is added when no changes are stored for the period, when the report has no Google Ads source, when the summary already reads a Change history widget (its entries are then in the data), or when it reads figures from another Google Ads account than the one the changes come from (the report's default account, or the client's only one). The Significant changes and Impact of changes presets read the whole history, as before. A summary is not marked out of date because changes were added to the history later: only its figures and its period decide that.

## AI summary presets
When you add or regenerate an AI text block, you can steer what Crunch writes with a preset instead of typing a prompt. The presets are: Executive summary, Analyse the widget above, Analyse the widget below, Campaign insights, Wins, Issues, Action plan, Significant changes, Impact of changes — plus a Custom prompt option for anything else. "Wins" and "Issues" are the fastest way to split the good news from the things that need attention; "Analyse the widget above/below" ties the commentary to a specific chart or table on the page.

Whichever preset you pick, Crunch is given only the report's numbers and told to add none, and the result is fully editable before you send.

## Sample size and margin of error on AI-visibility metrics
AI answers are non-deterministic, so an AI-visibility number is a sample, not an absolute. To keep that honest, AI-visibility KPI cards — in the app and in reports and shared links alike — show how solid each figure is. Under the metric you'll see "Based on N runs", where N is the number of scan runs behind it (prompts × models × repeats in the period). For Visibility %, the card also shows a margin of error as "± X%". We compute it to be genuinely informative rather than merely reassuring: it is boundary-safe (it never implies an impossible visibility below 0% or above 100%), and it widens when your AI engines disagree with each other or when the sample is thin — so a rate built from many agreeing runs shows a tight ±, while a shaky one shows a wide one. When a figure rests on only a few scan days and engines, how much they disagree is itself uncertain, so that part of the ± uses Student's t rather than the normal curve and is a little wider. That is why two brands can share the same headline % yet carry very different margins; read the number and its ± together.

We take that one step further on changes over time. When Visibility moves versus the previous period, we compare the change with the combined margin of error of both periods. Only a move of at least twice that margin is clearly beyond the margin of error; anything smaller is not, and is flagged "within margin" next to the delta on the KPI cards, and shows as a grey "~" before the change on Home, in the Overview's competitor table and Biggest movers, and, in a report, on the change chips of the Brand table, in the Prompt table's Δ Visibility and in the Brand × Tag, Tag × Month, Brand × Month and Tag tables. It is one rule on every one of them, with the same margin (the "± X%" of the headline), so a move never reads as a gain in one place and as "~" in another. AI summaries follow it too: Crunch is told to describe such a change as unchanged, never as a rise or a fall. A count such as Mentions also follows the number of answers collected, so next to a comparison its card names the answers in both periods ("Based on 1,426 runs · 2,940 in the comparison period"). It is an honest signal that the move may well be noise rather than a real shift, so a small wiggle (say a three-mention drop when citations are highly variable) isn't over-read as a trend. A small coloured dot next to "Based on N runs" summarises how much to trust the figure overall — green for high, amber for medium, grey for low — combining the sample size with the width of the ±.

This annotation appears only on AI-visibility metrics, where sampling applies — ad-platform and analytics numbers come straight from the source and aren't sampled. It's the same principle as the free AI-visibility check: show the sample size, never over-claim a single number.

The same honesty applies while a scan is running. If individual checks fail — a provider outage, a rate limit — the scan retries them a bounded number of times, then completes without them and tells you how many failed, right in the scan status bar. A failed check is never silently retried forever and never counted as a run, so N only ever includes checks that really happened. After the scan, the Prompts tab shows exactly which prompts had failed checks — an amber badge on the prompt names the engine and the provider's own error message — so a smaller sample size is always attributable, never mysterious. The latest scan's failed checks are also grouped by cause: key rejected, quota or billing limit, rate limit, timeout, provider outage, empty or cut-off answer, each with its count and a one-line fix (a quota problem on your own key points to the provider's console, or to Managed AI). Scans from before error messages were kept say that the cause was not stored. How loudly this is shown depends on how much of the scan it is: when fewer than 10 % of the scan's checks failed and no engine failed entirely, you see a small amber "1 check failed" pill beside Last scan in the sidebar (on a phone, above the page), on every page of the project, and the causes open from it; a cause that only a new scan would fix (a rate limit, a timeout, an outage, an empty answer, an unstated reason) then says that no action is needed, because the next scan runs those checks again anyway; at 10 % or more, or when every check of one engine failed, the pill turns red and the Overview also keeps a banner, because the figures are then missing a real part of the picture. A check counts as failed only while it is still failing: one that failed once and then succeeded on a retry is not counted, nor is one of a prompt you have since paused or deleted. The pill's popover links to the Prompts tab, which opens the same causes above the prompt list, each with the engines it hit and their counts. The notification bell in the top bar also lists the latest scan's failed checks with the date, the engine, the count and the provider's own message, and a link to the Prompts tab; once a later, finished scan of the same markets runs without failed checks, the entry is marked as resolved and the pill goes away; a scan that is still running, or ended in an error, does not resolve it. Last scan counts a market's own scheduled scan as well as a project-wide one, so the sidebar and the notice always name the same date.

During a trial, a project whose setup is unlikely to work gets one gentle email. Between 2 and 7 days after its first completed scan, CrunchJunkie checks the project once for the three problems above: no tracked engine that names pages, engines that appear to read the brand as software, and a latest scan with at least 5 failed checks or at least 10 % of its checks failed. If one shows, the workspace owner receives one short email that names the problem with its numbers and window and links to the project page, where the notice shows the fix. The email changes and bills nothing, is sent once per project, and can be unsubscribed from like any product notification.

**Only whole answers count.** An engine reports why it stopped writing: it finished, it reached the length limit the scan set, its web search ran out of rounds, or it declined to answer. ChatGPT, Gemini, Claude, Grok, Perplexity, DeepSeek and Meta AI answers are stored only when they are complete. One that arrives empty or cut off is not a measurement (a brand named after the cut would be invisible), so it counts neither as a mention nor as a miss: the check fails with the reason, a later pass of the scan may ask again, and an engine that keeps answering like this is stopped for the rest of the scan, with the reason in the scan bar. An answer the engine declined to give is stored as its answer, because that is what a real user gets; one its safety filter cut off partway is a fragment and is not. A failure on the provider's side reported inside an answer is temporary and is retried. Google AI Overviews, AI Mode and Microsoft Copilot work as before: an empty result there means no AI answer was shown (except an answer that arrived without its lists, below). N is the number of answers that count. The limits leave room for the longest real answers: 8,192 tokens for Claude and Gemini and 8,000 for ChatGPT, Meta AI and Grok, and Gemini thinks at its low setting because its thinking counts against the same limit. Brand names are read from up to 40,960 characters of an answer. Answers stored before this check existed (October 2026) were not checked this way. Until then Claude answers were limited to 1,024 tokens, so most stopped before the end and some arrived empty, and Gemini answers often ended after a few hundred characters, so Claude and Gemini figures from before the change read low. A project with answers from before gets a note on its Timeline when its first complete answer is stored, and drop and overtake alerts, signals and client digests leave that engine out of any comparison that spans it (with no engine left, they say nothing). In the MCP connector, get_ai_answer shows the reason each stored answer ended (stopReason).

**Names Google linked in AI Overviews.** SerpAPI, which reads Google AI Overviews, AI Mode and Microsoft Copilot for us, sometimes returns a name Google linked without its text: the link's address comes back, the name does not. A list item then reads ": One of the leading full-service agencies …" with no name before the colon. Since 11 October 2026 a scan keeps those links with the answer. Where a list item or paragraph starts without its name (with a colon, a comma or a dash, or empty) and its first link is such a link, the link's domain stands in brackets in the name's place, such as "[example.com]: One of the leading …", so you can see that a linked name stood there; we do not guess the brand's display name. That link, and only that link, counts as a mention when its domain is the client's or a tracked competitor's own domain: the client's website, a domain tagged Owned, the GEO audit's domain or an alias written as a domain, or a competitor domain set in the AI-visibility project's settings. A subdomain or a shared platform (facebook.com, g2.com, amazon.de and the like) stands for no brand. Such a mention counts for visibility and share of voice only, without a position or sentiment, and only when the answer does not name the brand anyway. The Responses view and get_ai_answer in the MCP connector label it: a linked name SerpAPI returned without its text; counted from its link's domain. A link without text in the middle of a sentence, links with text, and the sources listed under an answer never count as mentions. Answers stored before 11 October 2026 were not read this way and can miss a brand Google linked; they are left as they were. A project with earlier answers gets an internal note on its Timeline for each engine, on the day its first answer is read this way, and alerts do not compare across it. A table row whose name SerpAPI dropped together with its link cannot be recovered.

**One scan per day counts.** If a prompt is scanned more than once on the same day — a manual re-run, a retry after a settings change — the later scan replaces the earlier one for that engine and day. It never adds to it. Pooling both would double the sample without any new evidence and make that day a bigger population than every other day. The same rule applies everywhere the runs are read: the headline KPIs, the topic and source cards, report widgets, the API and the MCP connector all measure the same runs, so their numbers agree.

**"Last 30 days" means 30 whole days.** The window is counted in calendar days in UTC and always ends with today, so "last 30 days" on 23 September is 25 August to 23 September inclusive — never 31 days, and never a part-day at either end. That matters because a window measured as "now minus 30 × 24 hours" would cut the oldest day in half and would cover a different span depending on the hour you opened the page. Every surface uses this one definition: the headline KPIs, the charts, the competitive table, the topic and source cards, the per-engine tables, report widgets, the REST API and the MCP connector. So a sample size shown on one card is the same population as the one beside it. The days are UTC days because scans are stored by UTC day. In a report, the dates themselves come from the report's range, which follows your workspace's time zone; AI visibility then counts the scans of those dates by their UTC day.

### Answers captured without their lists

In October 2026 many Google AI Overviews and Google AI Mode answers began to arrive from SerpAPI, which reads both for us, without their lists. The opening and closing paragraphs arrive and the list between them does not, so an answer reads "Leading agencies include:" followed by "If you tell me your budget …". The last complete AI Overviews answers we saw were on 12 October, the first incomplete ones on 13 October; for AI Mode, 12 and 15 October, with no scan between. For AI Overviews, SerpAPI's public issue tracker lists the same loss ([#4453](https://github.com/serpapi/public-roadmap/issues/4453), [#4457](https://github.com/serpapi/public-roadmap/issues/4457)); no public report names AI Mode, where the gaps are our own measurement. Brands in a missing list counted as not named, so AI Overviews and AI Mode figures from those days may read low.

A scan now leaves such an answer out. An answer that announces content that is not there is not stored: a sentence ending with a colon that ends the answer, that is followed only by a closing question or offer, or that starts a run of announcements with nothing listed between them; and an overview that is there but has no text. It counts neither as a mention nor as a miss, it is not asked again in the same scan, because every search is billed and comes back the same, and when at least five answers of the engine come back like this and they are more than those that did not, the scan stops asking the engine. "No AI Overview" for a query stays a real result, an inline label such as "Hourly rates: usually …" is not a gap, and neither is a short label such as "Summary:" before its own paragraph. Answers stored before this check are left out once they have been checked. An answer that lost a list without such a visible trace cannot be told apart and still counts, so the figures from these days may still read low.

Every AI-visibility widget, chart and the Overview whose figures include these engines in this period says so (a band on charts, a line under the rest; "Show measurement gaps" in the builder hides it on one widget), the AI summary, Ask Crunch, the MCP connector and the API are told the same, and drop and overtake alerts, signals and client digests leave the engine out of any comparison that touches these days for as long as this lasts. Signals raised before this check stay as they are. Each affected project gets an internal note on its Timeline. Microsoft Copilot, also read through SerpAPI, is not affected. While answers arrive this way, we also read every name a list item may carry under another key, comparison blocks and every form of a table, so nothing SerpAPI does return is lost.

### Copilot on projects with a language or market

Microsoft Copilot is read through SerpAPI, whose Copilot search takes no language or country setting. Until 21 October 2026 we therefore put an instruction in front of every prompt on a project with a set language or market (for example “You are answering for a user located in Spain. Reply in German …”). Bing searched for the meaning of that instruction as well, so many Copilot answers cited translation sites and pages that repeat the instruction instead of real sources, and the brands those answers named were chosen by that detour. On a test of three prompts on a Spanish project answered in German, 14 of 32 cited pages repeated the instruction, 32 were translation sites, and the brand’s own site was never cited.

Since 21 October 2026 Copilot is sent the prompt itself, in the project’s language, followed by a short market label in that language, for example “(Markt: Spanien)”. In the same test none of 54 cited pages repeated an instruction or was a translation site, the brand’s site was cited for all three prompts, and every answer was still in German. A project without a market, or in a language we have no label for, is sent the prompt alone. Pages that repeat the old instruction are still left out of every citation and source count, in earlier answers too.

Copilot figures for projects with a set language or market are marked as a measurement gap from the day Copilot first ran with the instruction until 21 October 2026: a line under the widget, a band on charts, a note for the AI summary, Ask Crunch, the MCP connector and the API, and the engine is left out of any signal, alert or digest comparison that touches those days. 21 October counts as inside the gap, because scans earlier that day still used the instruction; scans from 22 October on are clean. Copilot figures from before and after that date are measured on different questions, so compare them with care.

## Scheduling and delivery
Open any report and click Schedule to put it on autopilot. Pick weekly or monthly, set the send time, and add recipients. On each scheduled run CrunchJunkie sends a branded email from your domain with a live link to the report.

The PDF is attached by default and the live link always shows current numbers, so recipients can file the document and still check in later. The schedule panel records when the report was last sent, and you can pause or edit a schedule at any time — changes take effect on the next run.

The day and time are in your workspace's time zone (Settings → Preferences), daylight saving included: a report set for Mondays at 09:00 in Auckland goes out at 09:00 Auckland time in summer and in winter. Schedules saved before workspace time zones existed keep running on UTC, as they were set; the schedule panel shows what that time is in your zone, with a button that switches the schedule to it. The panel also shows the next send.

If a send cannot go out at its hour (an hour with many reports due, or an outage), it is retried every hour for up to a day on a weekly schedule and up to three days on a monthly one, so the report goes out later instead of missing the period. A retry never sends a report twice. Saving a schedule never sends a report whose time has already passed: the first send is the next one the panel shows. The attached PDF counts its period from the day the send is scheduled for, in the schedule's time zone, so a schedule still on UTC reports the same period as before, and a send that goes out later reports the same period as an on-time one. The live link in the email always counts from your workspace's today, so for a schedule still on UTC in a workspace far from UTC the PDF and the live link can show different periods for a few hours: a "1st, 03:00 UTC" schedule in a New York workspace sends its PDF of November on the evening of 30 November New York time, while the link, still on 30 November, shows October until midnight there. Switching the schedule to your workspace's time zone removes the difference. When you switch, a weekly schedule takes the day and hour it was arriving on in your zone, and a monthly one the day it was arriving on, unless that falls in another month (then it keeps its day of the month).

Before you save any change to a schedule, the panel shows the next send, the period that report will cover, and, if the change means a period will not be sent at all, a line saying so ("The report for 21–27 Sep 2026 will not be sent."). Saving never sends a report whose time has already passed. And a report never goes out twice for the same period: if a changed schedule's next send would cover the period that was just sent (a weekly report moved from Monday to Friday covers the same last week), that send is skipped. A report with fixed dates covers the same days every time and goes out on every scheduled send.

A locked workspace (a cancelled, paused or unpaid subscription) sends no scheduled report emails and no client digests. A send that falls due while it is locked is skipped for good: it is not sent later, not even once the workspace is unlocked. Sends resume on their next scheduled date after you choose a plan. The workspace owner gets one email when this starts, and the lock screen says so too.

## Monthly copies
Agencies that keep one report per client per month ("Google Ads Report - September 2026", then October, and so on) can let the report make the next one itself. Open the report, choose Monthly copy (under Share or in the ⋯ menu), turn on "Create a copy every month", pick the day of the month and check the name.

On that day, in your workspace's timezone (Settings → Preferences), the report is copied for the month that just ended. The copy keeps everything Duplicate keeps: widgets, layout, cover, branding, language and which account each source reads. Its date range is that month as fixed dates, for example 1–30 September 2026, so the copy keeps showing September however late it is opened. The comparison carries over and follows the fixed month: "vs. Previous period" becomes the whole previous month (September against August), also on a widget whose range is the whole month, and "vs. Previous year" compares with the same month a year earlier. A widget with its own range gets fixed dates too: a range that ends before today, such as a 12-month trend, as it read on the first day after the month (1 October 2025 to 30 September 2026); a range up to today, such as this month, this quarter or year to date, as it read on the month's last day (1 January to 30 September for year to date). The copy has no email schedule and no copy rule of its own, and it appears with the client's other reports.

AI summaries are not copied, since they were written for an earlier month. Instead, when the copy is made, each AI widget is written again for the copy's month from the copy's own numbers, in the report's language: with its task, as the report writes it when the copy is opened, and for a widget with a saved custom question, that question, as pressing Send would ask it. (An "Analyse widget above / below" widget whose neighbour has no numbers to read is left as it is, as it is in the report.) A shared link or PDF of the copy therefore carries the summary from the start. It is written with your workspace's AI key, or on Managed AI within your plan's monthly assistant messages (one message per summary), and the email about the copy, or the panel after "Create now", says whether it was written. If it could not be (no AI key, the month's assistant messages used up, an error from the AI provider, or no data yet), the widget stays empty and is written in the copy as before: when you open it, or for a custom question when you press Send. When many copies are due in the same hour, those that no longer fit are made, with their summaries, in the next hourly run. A summary written at the start of the month uses the numbers as they stood then; if late conversions are still coming in, the report marks the summary with the day it was written, and Regenerate rewrites it.

The name comes from a pattern. {month} is the month the copy covers, written in the report's language (Juli in a German report; Spanish and French months in lower case, as in a sentence, or with a capital first letter if you write {Month}), {year} is its year, {client} the client's name and {report} this report's name. If the report's name already contains a month and a year, in English, German, Spanish or French, the pattern is proposed from it: "unisono • Google Ads Report - Juli 2026" becomes "unisono • Google Ads Report - {month} {year}". Before you save, the panel shows the next copy's date, name and dates.

Each month of a report is copied once. If a run is repeated, or the rule is deleted and set up again, nothing new is made, and if the copy could not be made on its day (an outage), the next hourly run in the same month makes it. A rule only acts on run days from the day it was turned on (or the report was restored), so it never back-fills earlier months. Paused or cancelled workspaces get no copies. "Create now" makes the copy of the month that just ended straight away, and it counts as that month's copy. Days 29 to 31 fall on the last day in shorter months. Copies are always made from the report that holds the rule, so change that report when future months should look different.

Reports → Scheduled lists every rule beside the scheduled emails, with the next copy and the last one, and lets you pause, resume or delete it. Whoever set up the rule gets an email when the schedule makes a copy. If the report is deleted, its rule makes nothing and is marked as needing attention until the report is restored or the rule is deleted. If a copy is deleted, the schedule does not make it again; the report's panel lets you make the copy of the month that just ended again. Read-only members can see the rules but not change them.

## The report email
Whether you send a report by hand or on a schedule, the email that reaches your client is prefilled and personalised rather than a bare "here's your report" line. The subject is built from the report itself — the data source and the date range, for example "Google Ads report · June 2026" — so it reads clearly in a busy inbox. The body greets the client by their saved contact name, names the data source and the timeframe, and signs off from the team member who created the report, with their email set as the reply-to so a reply lands with the right person.

The whole email is localised — English, German, Spanish or French — following the report's language, and it's editable before you send if you want to add a note. If the client has no saved contact name, a friendly generic greeting is used instead. Recipients default to the client's contact email and can be overridden with your own list.

## White-label and custom domain
White-label settings live under Settings → Branding. Upload your logo, set your brand colour, and choose the sender name and address scheduled emails go out from. Reports — both live links and PDFs — then carry your identity instead of ours.

On eligible plans you can also connect a custom domain so live report links sit on your own URL (for example, reports.youragency.com). Add a CNAME record at your DNS provider, verify it in CrunchJunkie, and the platform handles the SSL certificate automatically. The result is reporting that looks like a product your agency built.

## PDF export and live links
Every report can be shared two ways. A live link gives your client an always-current view that reflects the latest daily sync — ideal for stakeholders who like to check in between formal updates. A PDF export produces a pixel-perfect, white-labelled document for clients who prefer an attachment they can file or forward.

Both outputs render from the same underlying report, so they never diverge. "Download PDF" in the report header (and "Export PDF" on a live link) produces a finished file in one click — no print dialog: a full-page cover, your widgets on the grid they have on screen, each section starting on a new page, a running header with the client, report and period, and page numbers in the footer. Portrait and landscape are both available; the file is named after the client, report and period so it files itself. A table wider than the page is fitted rather than cut off: its headers wrap, its cells take less padding and its text can shrink to 7 pt, and a table that is still too wide continues in a second block that repeats its first column. Each is named once, and the period in words ("September 2026", not "Last month"): a report whose name already carries the client or the month is not given them twice. The one-time and scheduled emails name the same period in their subject and message. Changes against the comparison period read the same on every surface: a rate (CTR, impression share, visibility) changes in percentage points, a count or an amount in percent, unless a widget's Comparison format says otherwise. A score out of 100 (sentiment, GEO audit scores) changes in points too. From a previous value of 0, a KPI tile and a table both show the change in points or, in the Absolute and Both formats, as an absolute change; there is no percentage change from 0, so none is shown. Numbers, dates, metric names and labels follow the report's language. The document is rendered on our servers from the live data at that moment, so a PDF from the app, one downloaded by a client from a live link and the one attached to a scheduled or one-time email are identical. Scheduled and one-time sends attach the PDF by default (untick "Attach PDF" for link-only); if a render ever fails, the email still goes out with the live link. Live links can be access-controlled so only intended recipients can open them.

## Google Ads change history
The Change history widget lists what changed in a client's Google Ads account during the report period: budgets, bid strategies and their targets, campaign and ad group status and bids, keywords, ads, assets, locations and audiences. Each entry gives the date and time (in the account's time zone, as Google Ads shows it), the campaign and ad group it belongs to, the old and the new value — "Daily budget: €50.00 → €80.00", in the account's own currency — and who made the change: a person, an automated rule, a script, or a Google recommendation that was applied. Add it from the widget palette ("Change history", Google Ads only), or ask Crunch for "the account's change history"; it is never added to a report by default, and a drafted report includes it only when you ask for it. It filters by level (campaigns, budgets, ad groups, keywords, ads and assets, other) and by who made the change, follows the report period or its own date range, and lists the newest changes up to the number you set, always with the total for the period.

How far back it goes: Google Ads keeps change history for 30 days only. CrunchJunkie copies it every night from the day a Google Ads account is connected, and the first copy includes the 30 days Google still has. From then on every report period is covered for as long as we keep the copy (25 months). For a period that starts before the copy began, the widget says from which date changes are recorded instead of claiming there were none. Changes made since last night's copy are read live from Google Ads, so a report that runs to today is complete. Google notes that its change log may not contain every change the Google Ads interface lists in its own change history; the widget shows exactly what Google's API returns. Disconnecting the account deletes its stored change history.

Who sees the email address: Google tells us the email of the Google Ads user who made each change. Inside CrunchJunkie your team sees it. Shared links, PDFs and present mode show "Your agency" instead — never an address — while automated rules, scripts and Google recommendations keep their label everywhere. The "Significant changes" and "Impact of changes" AI summaries read this stored history (any report period, not just the last 30 days), and so do Crunch AI summaries on a report with this widget. On a report without it, the other summaries read the period's most significant changes (see Crunch AI summaries). Always with the same role labels; an email address is never sent to an AI model.
