Free tool · no account · runs in your browser

Which of your pages are quietly decaying?

Content decay is slow enough that nobody notices it in a chart. Upload your Search Console export and every page gets judged against the rest of your own site over the same calendar days — so a January dip that hit everything reads as healthy, and a page that fell while the rest of the site held reads as what it is.

Your file never leaves your browser. It is read by JavaScript in this tab: there is no upload, no signup, no email, no model calls, and no limit on how often you run it. This page's security policy blocks fetch, XHR, WebSocket, EventSource and beacon calls and form posts from this window.

This needs a day-by-day table with a Date column and a Page column — one row per page per day — and Search Console’s Export → Download CSV button does not produce one. Its ZIP puts pages in one file and days in another, and which page earned which day’s clicks is in neither of them. Filtering to a single page and taking the dates file does not work either: that file has no page column at all. You can drop files here.

Three places give you the right shape. In Data Studio (formerly Looker Studio), add the Search Console connector, pick your property with the URL Impression table, put Date and Landing Page in as the dimensions with Clicks and Impressions as the metrics, and export the table as CSV. In the Search Analytics for Sheets add-on, set Group By to Page and Date, request the rows, then download the sheet as CSV. From the Search Analytics API, query with dimensions: ["date", "page"].

However you get it, the first line should look like Date,Landing Page,Url Clicks,Impressions. Column names in any language are recognised and extra columns are ignored. Two things matter: one row is one page on one day, and the dates are written year first — 2026-06-05, which is also what 2026/06/05, 2026.06.05 and the compact 20260605 read as. A date format this check does not recognise is refused with the first value it could not read, never guessed at, and the fix is to export again with ISO dates or to format the Date column as yyyy-mm-dd before saving the CSV. Ask for at least 112 days, because a verdict is the last 28 days against the 84 before them; a year gives every page a settled baseline.

How big a file it takes. This page opens up to 64 MB of files at once and reads up to 12 MB of table text, which is about 135,000 rows of a day-by-day table. A ZIP counts at its compressed size against the first limit and at its unpacked size against the second. A file over either is refused by name and never read in part, and the refusal says what to trim: a shorter date range, or only the section you changed.

This tool needs JavaScript. If you can read this line, its script has not started: wait a moment, then reload; if it is still here, JavaScript is off or a script file failed to load.

What this actually measures

Decay, with the seasonality taken out

Judged against your own site

The last 28 days against the 84 before them, then the rest of the site's move over the same days subtracted — with the page left out of its own baseline, so a big page's crash cannot soften the very number it is being judged against.

Dates, not rows

Search Console leaves out a page's row on days it got nothing. Counting "the last 28 rows" therefore spans four weeks on a busy page and four months on a quiet one. Every page here is summed over identical calendar days and a missing day counts as zero.

Refusals are kept

Under 3,000 impressions in the trailing 90 days, or an archive too short to cover the window, and the answer is "can't tell" with the reason rather than a guess.

Every verdict is read against the last day in your export, not against today. If that file is three weeks old the report says so at the top — the pages are judged as they stood when you took it, which is the only thing the file can honestly support.

What happens to your file

Nothing leaves your browser. The file is read by JavaScript running in this tab and analysed in memory; it is not uploaded, there is no copy on a server, and nothing from it is written to a cookie or to local storage (the theme switch remembers light or dark and nothing else). The page loads no analytics and no third-party scripts. Because nothing is sent, there is nothing for us to count or to limit: run it as often as you like.

You do not have to take our word for it. Open your browser's developer tools, choose the Network tab, run a check, and watch it make no request. Two things back that up. This page carries a content security policy, readable in its source, that blocks fetch, XHR, WebSocket, EventSource and beacon calls (connect-src 'none') and form posts from this window (form-action 'none'), and allows images only as inline data: URIs. A content security policy does not cover every browser feature. WebRTC is not covered, nor is navigating the tab to another address, and in one browser we tested a form that opens in a new window got past form-action. Nor does it stop this page asking penlid.com itself for a file (a stylesheet, a font, a script), because the page needs those: a request like that, with your data in its address, would reach our server's log. So the second check matters: the project's tests read this page's own script and every module it imports, and fail on any of the browser's network APIs, on navigation, and on any code that would build something for the browser to load — a form, an image, a link, a stylesheet — or set an address from script. A test that reads source text cannot catch code written to hide what it does, which is why the policy is the first check and these are the second.

The site-wide script penlid.js, which every page loads, contains network calls for the signed-in app's account switcher. This page never calls it, and a test checks that those calls sit only inside the switcher.

The two things that do leave this page's memory are the two you ask for: the text you click to copy, and the page you click to print or save as PDF.

One copy of the classifier. Each page's verdict is worked out by the same stats.js function that the paid dashboard runs, so the rules you see here are the rules it applies, not a second copy that could drift from them. Reading your file and adding up the whole site's days are this tool's own code (gscsv.js and diagnose.js), and the paid product has its own version of those two steps.

The other question, same file

If you already know which page you changed and want to know whether the change worked, the did-it-work check reads the same export, plus a list of what you changed and when.