Recipe
Track backlinks with a weekly diff you can read in a minute
Pull, store, compare, post. The whole recipe is one Python file, a folder of dated JSON and a cron line.
The recipe, start to finish
- 1
Create the key
Sign in with Google, make a key on the account page and export it as
RANKIFT_KEY. Never commit it to the repo. - 2
Choose the action
refdomainsfor the domain-level diff, which is what most alerts need. Usebacklinksonly when you need URLs and anchors, at the same 2 credits. - 3
Store dated snapshots
One file per domain per run, named with the date. Disk is cheap, and old files let you answer questions nobody has asked yet.
- 4
Diff and post
Compare the two newest files, print what left and what arrived, and exit quietly if both lists are empty.
- 5
Schedule it
Cron on Monday morning. Add a failure notification, because a silent monitor is worse than none.
diff.py
Checked against the documented parameters: action, q, per, page and key. The response shape differs per action, so print one real response and set ROWS and ID to match before you trust it.
import json, os, sys, glob, urllib.parse, urllib.request
KEY, SITE = os.environ['RANKIFT_KEY'], 'https://rankift.com/api.php'
ROWS, ID = 'rows', 'domain' # adjust after inspecting one response
def pull(domain):
out, page = [], 1
while True:
qs = urllib.parse.urlencode({'action': 'refdomains', 'q': domain,
'per': 1000, 'page': page, 'key': KEY})
with urllib.request.urlopen(f'{SITE}?{qs}') as r:
batch = json.load(r)[ROWS]
out += batch
if len(batch) < 1000 or page >= 5: # five pages = 5,000 rows
return out
page += 1
domain, date = sys.argv[1], sys.argv[2]
json.dump(pull(domain), open(f'snap/{domain}-{date}.json', 'w'))
files = sorted(glob.glob(f'snap/{domain}-*.json'))
if len(files) > 1:
old = {r[ID] for r in json.load(open(files[-2]))}
new = {r[ID] for r in json.load(open(files[-1]))}
lost, gained = sorted(old - new), sorted(new - old)
if lost or gained:
print(f'{domain}: -{len(lost)} +{len(gained)}')
print('lost:', lost[:25])Why a diff beats a dashboard
A chart tells you the line moved. A diff tells you which domains moved, which is the thing you can act on.
Tools like Ahrefs and Semrush show new and lost links in their own interface, and for ad hoc work that is fine. The trouble starts at fifty sites and one analyst, when the Monday ritual of opening each project eats the morning.
A script turns that ritual into a file. It also leaves a dated archive, so in March you can answer what the profile looked like in October without begging a vendor for an export.
The cron line
Runs Monday at 07:00 server time and mails any output to you, which is why the script prints nothing when nothing changed.
0 7 * * 1 cd /srv/linkwatch && for d in example.com rival-one.com rival-two.com; do python3 diff.py $d $(date +%F); done | mail -s 'link diff' your-team-listWhat it costs
| Setup | Calls per week | Credits per week | Per year |
|---|---|---|---|
| 1 domain, under 1,000 referring domains | 1 | 2 | about $0.01 |
| 1 domain, 4,000 referring domains | 4 | 8 | about $0.04 |
| Your site and 5 rivals, 1 page each | 6 | 12 | about $0.06 |
| 50 client sites, 1 page each | 50 | 100 | about $0.52 |
Gotchas we hit, so you do not
Snapshots only change when the index does. We refresh about monthly, so most weekly runs return an identical list and print nothing. That is deliberate.
Deep pagination is capped at 50,000 rows, and the five-page limit in the script keeps you far below it. Huge domains need sampling, such as sorting by Domain Rating and stopping early.
Run the first pull by hand and read the JSON. Check the field names against the constants at the top of the script, then count the rows against the total shown by the free overview action, which gives the number of referring domains for comparison.
Handle HTTP 401 and 402 explicitly, because 401 means a bad key and 402 means the balance reached zero. Both belong in your failure notification. When a diff does fire, follow the lost links runbook for the first hour.
Extending it
Swap refdomains for backlinks when the alert needs the donor URL and anchor. Keep the same two-credit cost in mind.
Add bulk_dr to score every new domain, at one credit per call of up to 2,000 domains. Then your message can say "3 new, best DR 72" instead of a bare list.
For Google Sheets, the same query string works in Apps Script with UrlFetchApp. And for rivals, use the thresholds from competitor backlink alerts.