Skip to content
SEO Tools AI
SEO Tools AISign up

Recipe

Sync link scores from a sheet or n8n

Most tool directories live in a spreadsheet, an Airtable base or a small database. This recipe turns a tool backlink check into a column fill: one request per 2,000 rows.

SEO Tools AI research team ·

The recipe in five moves

01

Export the domains

Take the website column of your listings, strip the protocol and paths, and remove duplicates. Duplicates cost nothing extra but make the mapping step messy.

02

Chunk into groups of 2,000

bulk_dr accepts up to 2,000 domains per call. A 5,000-row sheet therefore needs three calls.

03

Send, check ok

Call the API, read the JSON, and stop if ok is false. Log _credits so you see the balance after every run.

04

Write back with a date

Store the rating and today's date in two columns. Never overwrite a good value with an empty one.

05

Schedule once a month

Run it on the first of the month. The index moves about monthly, so more often gains nothing.

Before you automate anything

Run the check by hand on five listings first. Use the free overview action for each, which needs no key, and compare what you see with what you expected.

This catches the common surprises early: a listing that points to a subdomain, a product that lives on a path of a larger site, a domain that redirected last spring. A rating belongs to a domain, not to a product.

Decide how your sheet marks those cases, for example with a note column that reads shares domain. Readers forgive a footnote far more readily than a score that looks wrong.

When the five look sane, move on to the batch. Your field reference lists the columns, and the cost example shows what a run costs.

A small Python version

Needs the requests package. It is a sketch for the shape of the job: print one real response first, then map the fields you need into your own columns.

python
import csv, requests

KEY = "sk_live_..."
BASE = "https://seotoolsai.org/api.php"

rows = list(csv.DictReader(open("listings.csv")))
domains = sorted({r["domain"] for r in rows})

for i in range(0, len(domains), 2000):
    chunk = domains[i:i + 2000]
    r = requests.post(BASE, params={"action": "bulk_dr", "key": KEY},
                      data={"domains": ",".join(chunk)}, timeout=60)
    if r.status_code in (401, 402):
        raise SystemExit(f"stop: HTTP {r.status_code}")
    data = r.json()
    if not data.get("ok"):
        raise SystemExit("api said not ok")
    print(data["_credits"])   # cost and remaining balance
    open(f"raw_{i}.json", "w").write(r.text)

Doing it in Google Sheets or n8n

Sheets can call a URL from Apps Script with UrlFetchApp, and n8n has an HTTP Request node. Both do the same thing as the script above.

In n8n, put a schedule trigger first, then a node that reads your sheet or table, then an item-splitting step that builds groups of 2,000, then the HTTP Request. Add a final node that writes the rating and date back.

Keep the key in the platform's credential store, not in the workflow text. Workflows get shared, copied and pasted into forums, and a key pasted with them is spent money.

What the errors mean

Behaviour of the API and what a sync job should do.
SignalMeaningWhat the job does
HTTP 401Missing or bad keyStop, alert a human
HTTP 402Credit has run outStop, keep old scores, top up
ok is falseThe call did not succeedLog the body, retry next run
Empty result for a domainNot in the indexLeave the cell blank

Sync questions

How do I avoid paying twice if the job crashes halfway?

Write the date next to each score as you go, and have the next run skip rows already stamped this month. Chunks of 2,000 mean a crash costs at most one credit, so the risk is tiny, but the habit keeps the sheet tidy.

Can a non-programmer set this up?

Mostly yes. An HTTP Request node in n8n or Make needs only a URL, a key and a list. The fiddly part is turning the response into columns, so do one test call and look at the JSON before you build the mapping.