I'm always excited to take on new SEO and paid media projects across ecommerce, finance, and service brands.

Phone Number

+91 91881 75037

LinkedIn

in/sooryadasps

Address

Kochi, Kerala, India

Social

Home / Blog / Technical SEO Automation
Technical SEO Automation

How I Built an Automated SERP Drop Detector

Sooryadas PS Sooryadas PS
Jun 2026 9 min read
SERP Drop Detector automation pipeline: track, detect, diagnose, verdict, alert

Managing SEO for six concurrent clients means six different Search Consoles, six different sets of tracked keywords, and a question that comes up constantly: why did this keyword just drop?

Answering it properly meant opening GSC, checking if impressions fell or just clicks, checking Google's update history for that date, and running a manual SERP check to see who's now ranking above the page, taking 10 to 15 minutes per keyword when done right. Multiply that across six clients and a dozen-plus drops in a busy week, and diagnostic work was quietly consuming the time I should have spent on fixes.

So I built the SERP Drop Detector: a Python pipeline that pulls ranking data from Google Search Console every morning, flags any keyword that's fallen more than 3 positions against its 14-day baseline, runs it through a layered diagnostic check, and emails me, and my clients if they want visibility, a plain-English verdict with a recommended next action. It runs on GitHub Actions at 9:30 AM UTC, before I've opened my laptop.

This post is a walkthrough of how it actually works, the diagnostic logic behind it, and why I built it the way I did.

The problem with "the keyword dropped"

A ranking drop on its own tells you almost nothing. The same -6 position move could mean four completely different things.

Google rolled out a core update and your page no longer satisfies the new ranking signals. A competitor published a stronger page and displaced you organically. Google partially de-indexed or stopped crawling the page as often. Or the position barely moved but a new SERP feature (a featured snippet, an AI Overview, or a "People also ask" block) pushed your listing below the fold, so even though you're still "ranking," nobody's clicking.

Each of those has a different fix. Algorithm-driven drops often resolve on their own or need a content quality pass. Competitor displacement needs a content gap analysis against the page that beat you. Indexing problems need a crawl audit. CTR or layout problems need title and meta rework, not a content rewrite. Treating all four the same way, usually by re-optimizing content that didn't need it, wastes time and sometimes makes things worse.

The diagnostic problem is that Google Search Console gives you the symptom (position dropped) but not the cause. You have to cross-reference it against other data sources to get the cause. That cross-referencing is exactly what I automated.

How the pipeline works

The system is built as a sequential pipeline, each stage handing off to the next.

Track. Every morning, gsc_puller.py pulls the last three days of ranking data (impressions, clicks, position, CTR) for every property I manage, one API call per property. In parallel, algo_update_fetcher.py syncs the latest confirmed Google algorithm updates from a third-party tracker, so the system always has a current reference list without me updating it manually.

Detect. anomaly_detector.py compares every tracked keyword's current position against its own 14-day rolling average. Anything that's fallen more than 3 positions gets flagged. The 14-day window matters: a single day's fluctuation is noise; a sustained move against a two-week baseline is a real signal worth investigating.

Diagnose. This is the part that actually answers "why," and it runs three checks that aren't mutually exclusive: a keyword can trigger more than one signal at once, and the verdict accounts for that.

The first check, handled by gsc_classifier.py, looks at impressions and CTR in the seven days before the drop. If impressions fell more than 20% against their pre-drop average and the keyword normally gets at least 15 impressions a day, that's classified as an algo_or_indexing signal, meaning Google is showing the page less often, which points to an algorithm change, a manual action, or a crawling or indexing issue rather than a ranking displacement. The 15-impression floor matters: below that volume, a swing of two or three impressions produces a percentage change that looks dramatic but is just daily noise.

The second check looks for the opposite pattern: impressions held steady (no more than a 10% dip) but actual CTR is more than 15% below what's expected at the new position. Position 3 and position 7 don't have the same expected click-through rate, so the check uses a position-adjusted CTR table rather than comparing raw numbers; it only flags an issue when the CTR drop is bigger than the position change alone would explain. That pattern, ctr_or_layout, usually means the page is still being shown but something about the listing (the title, the meta description, or a new SERP feature crowding it out) is suppressing clicks.

In parallel, algo_matcher.py checks whether a confirmed Google algorithm update rolled out in a window from 7 days before the drop to 2 days after. That window is intentionally asymmetric: the 7-day lookback catches updates that started affecting rankings before the drop was detectable in GSC's own reporting lag, and the 2-day forward window catches updates that began rolling out right around when the drop was first measured.

The third piece, competitor_checker.py, is the one I'm most deliberate about, because live SERP checks cost API credits and I didn't want the tool burning through a monthly quota on drops that don't need it. It only fires a live SERP lookup when three conditions are all true: the signal isn't purely an impression-suppression case (if Google's simply showing the page less, a competitor check is irrelevant, as there's no displacement to diagnose), the drop is at least 5 positions, and the monthly SerpAPI usage is still under a 200-call soft cap, leaving a 50-call buffer against the free tier's 250-per-month limit. When it does fire, it returns the top 4 sites currently outranking the tracked page, included directly in the report.

Verdict. verdict_generator.py takes whichever signals fired, sometimes more than one at once, and combines them into a single plain-English diagnosis with a recommended action, rather than disconnected data points I'd have to interpret myself.

Alert. alert_dispatcher.py sends the result as an HTML email digest every morning via Brevo, and a separate monthly_reporter.py runs a month-end rollup that surfaces keywords that dropped more than once, drops that never recovered, and pages where multiple keywords failed at the same time, a different and more useful signal than any single keyword drop, since it usually points to a page-level problem rather than keyword-level noise.

Here's the full flow as a diagram:

Five stage pipeline: track, detect, diagnose, verdict, alert

What the output actually looks like

Below is the monthly health summary (numbers and domains anonymized, but the structure and logic match real client output). It groups keywords that dropped more than once into an "unstable keywords" table, lists unrecovered drops sorted by how long they've been open, and, the part I find most useful, flags "chronic pages," where the same URL had multiple keyword drops on separate occasions. That's the signal that something is structurally wrong with a specific page, not just keyword-level variance.

Monthly SEO health summary showing unstable keywords, unrecovered drops, and chronic pages

In the example above, "keyword 5" dropped 7.1 positions following a logged core update, with a competitor also moving into the picture; both an algo_or_indexing and a ctr_or_layout signal fired on the same drop, exactly the kind of compound case the multi-signal design is meant to catch. It's been open 25 days, longer than the other two unresolved drops, which is also visible at a glance because the report sorts by days unresolved.

The daily digest takes a different shape: a full table of every tracked keyword's current rank, day-over-day change, clicks, and impressions, color-coded by status, plus a callout for anything that recovered in the past week:

Daily SEO performance digest showing all tracked keywords, rank changes, and recovered keywords

This version is what I send to clients who want day-to-day visibility without asking me for an update. It replaces a manual status check-in with something they can glance at over coffee.

"A ranking drop is a symptom. The diagnosis is what actually tells you what to do about it, and that's the part most tools skip."

– Sooryadas PS

Why I built it this way

A few design decisions came from getting burned by the obvious version of this tool.

The first version I sketched flagged every position change, and the result was useless: daily rank fluctuation of one or two positions is normal SERP volatility, not a signal. The 3-position threshold against a 14-day baseline, rather than yesterday's position, was the fix; it filters out noise while still catching real movement quickly.

I also originally let the competitor check fire on every drop, and burned through almost half a month's SerpAPI quota in nine days across just two client properties. The three-condition gate (magnitude, signal type, and a soft cap with a reserve) exists because that happened.

The GSC 2-day reporting lag is also easy to miss if you haven't worked with the API directly: data for the most recent two days is usually incomplete, so the pipeline always pulls data ending two days before "today" to avoid drawing conclusions from a partial dataset that will look different once it finishes populating.

The actual time saved

It's worth being concrete about what this replaced, because "automation saves time" is a claim that's easy to make and hard to verify.

Manually diagnosing a single dropped keyword (opening GSC, checking the impressions and CTR trend, cross-referencing Google’s update history for that date, and running a SERP check to see who’s now outranking the page) took roughly 10 to 15 minutes per keyword when done properly. Across six client accounts with active SEO work, a typical week could easily produce a dozen or more flagged drops worth investigating, especially during periods of algorithm volatility. At the lower end of that range, that's two hours a week spent purely on diagnosis, before any actual fix work begins. During a turbulent month with a confirmed core update, that number goes up, not down, exactly when there's the least spare time to do it manually.

The pipeline doesn't eliminate that work entirely: a no_gsc_signal verdict still means manual investigation, since GSC data alone couldn't explain the drop. But it does the first pass automatically: by the time the digest email opens, it's already clear which drops are likely algorithmic and probably don't need immediate action, which ones have a named competitor to investigate, and which ones are genuinely ambiguous and need attention first. That triage used to take the most time, since it meant repeating the same diagnostic steps on every drop regardless of how it would turn out. Now that two hours goes to the drops that actually need a human judgment call, not the ones the data already answers.

Key takeaways
  • A position drop is a symptom, not a diagnosis; the cause requires cross-referencing impressions, CTR, algorithm history, and live SERP data.
  • Thresholds and noise floors matter as much as the detection logic itself; without them, the tool flags noise instead of signal.
  • Automating the diagnostic triage, not the fix, is what actually saves time, since it reserves human judgment for the genuinely ambiguous cases.

What's next

The tool currently treats GSC data and a live SERP snapshot as its only two data sources. The next addition is pulling in Core Web Vitals field data per page, since a CWV regression on a high-traffic page is itself a plausible cause of a ranking drop that none of the current three signals would catch; it would show up looking like an unexplained no_gsc_signal case today.

If you're managing rankings across more than two or three properties and you're still doing this diagnosis manually, it's worth the few hours it takes to build something like this. The code for the public version is on GitHub if you want to see the implementation directly: serp-drop-detector-public.

Sooryadas PS

Sooryadas PS

SEO & Google Ads Specialist in Kochi, writing about technical SEO, GEO/AEO, and paid media.

Connect on LinkedIn
Keep reading

Related articles

How I Took a DA 23 Site to 1.03M Impressions in 7 Days
Jun 2026 • 8 min read

How I Took a DA 23 Site to 1.03M Impressions in 7 Days

How timing and topical consistency let a DA 23 site hold top-3 positions against established financial publishers, without a legacy backlink profile to compete on.

How I Got YouTube Subscribers for ₹2.43 Each
Jun 2026 • 7 min read

How I Got YouTube Subscribers for ₹2.43 Each

The targeting logic and sequential creative testing behind a YouTube subscriber-growth campaign that converted at 26.53%, including why the first week's cost spike is misleading.