Tutorial
Python Backlink Checker: Save a Reviewable Backlink Sample
Build a Python backlink checker with the RankParse SDK, preserve source data and dates, and prepare a bounded CSV sample for analysis in Claude or ChatGPT.
By Abhi Bavishi, Updated , 7 min read
A Python backlink checker should leave you with evidence you can review: source pages, target URLs, observation dates, and a record of what you requested. Printing a link count is not enough to decide which links matter or which publications deserve further research.
This tutorial creates a bounded backlink sample with the RankParse Python SDK, saves the original response, and writes a CSV plus a provenance file. You can inspect the CSV yourself or give it to Claude or ChatGPT for a first-pass review. The sample does not represent every backlink on the web, and an assistant's interpretation still needs page-level checks.
Decide what you will do with the sample
Choose one target domain and one question. “Which source pages in this sample might be relevant to our migration guide?” is a useful first task. “Tell me whether our backlink profile is healthy” asks for a conclusion that a limited sample cannot establish.
The example requests URL-level rows sorted by recency within the stored dataset. That makes it suitable for inspecting individual linking pages. It is not a live monitor: RankParse's public backlink data comes from a periodic snapshot; check the active dataset date, and requesting recent rows does not mean the links were discovered today.
Use Python 3.9 or later, install the rankparse package with pip install rankparse, and set RANKPARSE_API_KEY securely in your environment. The Python SDK documentation covers installation and client configuration. Keep the key out of source control and files you upload to an assistant.
Save the response, a CSV, and the request context
Replace example.com with a bare domain you intend to research. Use a new output directory for each run so earlier evidence stays intact.
This example is checked against the SDK method signature and documented response fields. It is not a reported live API run; no sample counts or findings below are measured results.
import csv
import json
import os
from datetime import datetime, timezone
from pathlib import Path
from rankparse import RankParseClient
DOMAIN = "example.com" # Replace with your target; no scheme or path.
LIMIT = 100
started_at = datetime.now(timezone.utc)
with RankParseClient(api_key=os.environ["RANKPARSE_API_KEY"]) as client:
result = client.backlinks(DOMAIN, limit=LIMIT, sort="recent")
retrieved_at = datetime.now(timezone.utc).isoformat()
rows = result["data"]
if not isinstance(rows, list) or any(not isinstance(row, dict) for row in rows):
raise ValueError("Expected a list of backlink objects")
output = Path("backlink-runs") / started_at.strftime("%Y%m%dT%H%M%S%fZ")
output.mkdir(parents=True, exist_ok=False)
(output / "response.json").write_text(
json.dumps(result, ensure_ascii=False, indent=2), encoding="utf-8"
)
fields = [
"from_domain", "from_url", "to_url", "anchor_text", "rel",
"link_type", "crawled_at", "domain_host_count",
]
# Escape text that spreadsheet software could interpret as a formula.
# The JSON response retains the original values and nulls.
def csv_value(value):
if value is None:
return ""
if isinstance(value, str) and value.lstrip().startswith(("=", "+", "-", "@")):
return "'" + value
return value
with (output / "backlinks.csv").open("w", newline="", encoding="utf-8") as file:
writer = csv.DictWriter(file, fieldnames=fields)
writer.writeheader()
for row in rows:
writer.writerow({field: csv_value(row.get(field)) for field in fields})
provenance = {
"source": "RankParse",
"domain_requested": DOMAIN,
"request_started_at_utc": started_at.isoformat(),
"retrieved_at_utc": retrieved_at,
"limit_requested": LIMIT,
"sort": "recent",
"rows_returned": len(rows),
"response_total": result.get("total"),
"crawl_release": result.get("crawl_release"),
"aggregate_rows": sum(row.get("link_type") == "aggregate" for row in rows),
"coverage_note": "Bounded sample; absence is not proof of no link.",
}
(output / "provenance.json").write_text(
json.dumps(provenance, ensure_ascii=False, indent=2), encoding="utf-8"
)
print(f"Saved {len(rows)} rows to {output}")The script writes headers even when data is empty. It does not index the first row to guess the schema, so an empty successful response still produces a reviewable artifact. Unexpected fields remain in response.json; the CSV is a selected view of that original response.
Blank CSV cells can mean null or missing values. Use the JSON when that distinction matters. The original response also contains account-related fields, so review it before sharing; the CSV and provenance file are usually the more appropriate analysis inputs.
If the API request raises an exception, the script stops before writing a successful result. Do not catch every exception and substitute an empty list: that would make a failed request look like a domain with no returned data. Production retry and authentication behavior belongs in the SDK error-handling documentation.
Read the fields without overstating them
| Field | Useful interpretation | What it does not establish |
|---|---|---|
from_url | Candidate linking page to inspect | That the page is accessible or still links today |
to_url | Destination recorded in the dataset | The destination's current status or canonical URL |
anchor_text | Recorded link text when available | A universal measure of link quality |
rel | Stored link attribute when supplied | Missing values do not prove a specific attribute |
crawled_at | Recorded observation date | Original publication date or current live status |
link_type | Recorded kind of link or aggregate row | All rows are not necessarily editorial hyperlinks |
For this endpoint, the backlinks documentation defines total as the number of returned rows, not the site's complete link count. Use len(rows) for the saved sample size. The endpoint returns up to 1,000 rows and does not provide offset pagination to enumerate the entire index.
The default importance sort can return aggregate referring-domain rows on some serving paths. The example explicitly uses sort="recent" for documented URL-level results, and records any aggregate rows defensively. If you encounter aggregates, do not treat a domain homepage in from_url as the specific page containing the link.
crawl_release is not normally included in a backlinks response. The provenance file leaves it null when absent. Do not fill that field with today's date or copy a release identifier from an old article. Retrieval time and observation time answer different questions.
Validate a small selection before expanding the task
First confirm that the CSV's data-row count matches rows_returned and the JSON list length. Then inspect a small, varied selection: several referring domains, different target paths, missing anchors, and older observations. A spot check can reveal interpretation problems; it cannot prove full dataset accuracy.
In an illustrative review, you might find these three cases:
| Observation | Review decision |
|---|---|
| A topical tutorial still links to the intended guide | Keep as a relevant example with the inspected URL and date |
| A source page now redirects and no link is visible | Mark current link status unconfirmed; inspect the destination |
| Several URLs repeat the same publisher's footer link | Group for publisher review; retain all raw rows |
Do not turn a failed page load into a “lost link” claim. A transient error, access restriction, or redirect can prevent verification. Similarly, an empty sample means the request returned no rows for that domain and scope; it does not prove the site has no backlinks.
When comparing domains, use the same request settings and record each sample's size. Equal limits do not create equal coverage. A recent sample of 100 rows is not a random sample, so percentages calculated within it should be labeled sample percentages.
Give the assistant evidence and a stopping rule
Upload the CSV and provenance file to a Claude or ChatGPT workspace that supports file analysis and is approved for your data. Remove sensitive URLs or unnecessary identifiers first. This prompt turns the file into a review queue:
Try this prompt
Give the assistant evidence and a stopping rule
Replace placeholders and attach the inputs described above. Paste the prompt if your assistant opens an empty chat.
Need the tools? Add RankParse to Claude · ChatGPT setup
Check the cited rows against the original response and visit the promising source pages. An assistant can organize evidence, but a list of links does not reveal the publisher's editorial intent or guarantee a viable outreach opportunity.
What to build next
Use the backlink quality workflow to assess the pages you inspect. For an agent-centered process that turns a sample into a reviewable audit, see auditing backlinks with Claude.
If your next question concerns distinct linking sites, review the referring-domains endpoint, keeping its domain-level rows separate from this URL-level sample. Extend the script only after the output helps you make a concrete decision; collecting more rows alone does not complete an audit.