Severity table for new referring domains
Our own rules of thumb for triage, not a Google definition| Level | Signal | Action |
|---|
| Ignore | New domain, DR above 30, anchor is your brand or a bare URL | Log it, move on |
|---|
| Look | 50 or more new domains in a week from one TLD you never targeted | Open ten of them |
|---|
| Look hard | Same exact-match anchor on 20 or more new domains | Check the anchor cloud, find the source |
|---|
| Act | Paid-looking network or hacked pages pointing at you | Document it; consider a disavow file only if you also hold a manual action |
|---|
| Wait | A domain vanished this month | Recheck after the next monthly refresh |
|---|
Why diff instead of scanning once
A single snapshot tells you what is there. A diff tells you what changed. That is where problems show up.
Google has said for years, since the Penguin 4.0 release in September 2016 and again in the December 2022 link spam update, that it mostly ignores spammy links instead of punishing them. SpamBrain does that work. So a flood of junk is more often noise than a threat, and the diff mainly saves you from reacting to noise.
Keep your own history. Suites overwrite theirs.
diff.py
Two tables would be tidier. One is enough for a hobby setup: a row per domain per snapshot date.
python
import json, sqlite3, sys, datetime
db = sqlite3.connect('links.db')
db.execute('create table if not exists snap(day text, dom text, dr int)')
rows = json.load(open(sys.argv[1]))
day = datetime.date.today().isoformat()
db.executemany('insert into snap values (?,?,?)',
[(day, r.get('domain'), r.get('dr')) for r in rows])
db.commit()
prev = db.execute('select max(day) from snap where day < ?', (day,)).fetchone()[0]
if prev:
new = db.execute('select dom from snap where day=? except select dom from snap where day=?', (day, prev)).fetchall()
gone = db.execute('select dom from snap where day=? except select dom from snap where day=?', (prev, day)).fetchall()
print(len(new), 'new,', len(gone), 'gone')
Troubleshooting the diff
| Symptom | Likely cause | Fix |
|---|
| Hundreds of 'gone' overnight | Index refresh swapped the sample | Compare monthly, not daily |
|---|
| Every domain looks new | Table was empty or day format changed | Check select distinct day |
|---|
| DR column all NULL | Row field name differs from dr | Print one raw row and adjust |
|---|
| Duplicates after a rerun | Same day inserted twice | Delete that day first, or add a unique key |
|---|
Count below total | Pagination stopped early | Fix the loop in the Actions runbook |
|---|
What this cannot tell you
It cannot prove a link hurts you. Nobody outside Google can, and Search Console's manual actions report is the only place a penalty is stated.
It also cannot see links younger than the last refresh. For fresher evidence ask the people who run the linking site, or read your server logs for the referrers. Our note on timing goes deeper.
If you want the anchors side, anchor_cloud costs 2 credits and groups text by frequency, which is the quickest way to spot an exact-match pile-up.
Wiring the diff to a weekly job
Run diff.py right after the snapshot step. Print the counts, and exit with a non-zero code when new crosses a limit you picked, say 200 in a week for a small site. The job turns red, and GitHub emails you. Simple.
Pick the limit from your own history. After two months you know whether 15 new domains a week is normal or a spike, and any fixed number copied from a blog post is a guess.
Quiet weeks are the norm.
Bad link questions, answered plainly
- Is there a reliable bad backlinks checker?
- No tool can label a link harmful with certainty, because only Google knows how it treats each one. A script can surface patterns worth reading: sudden floods, repeated exact anchors, odd TLDs. You make the call after opening the pages.
- Should I disavow what the diff flags?
- Rarely. Google has said it largely ignores spam links, and a disavow file is meant for cases such as a manual action or a paid-link mess you made yourself. Document first, and check Search Console before you submit anything.
- How often should I run it?
- Snapshot weekly, review monthly. The index refreshes about once a month, so a daily diff mostly compares a table with itself. A monthly review matches the data and keeps the noise down.