Find expired domains with backlinks by category, in Python
Pull expired domains that still carry backlinks in one niche — education, SaaS, fintech — from the Revised API in Python, then rank and export a shortlist.
This is a runnable Python notebook for one job: finding expired domain names that still carry backlinks, inside a category you care about. It queries the Revised directory through GET /api/v1/domains, filtered by category and referring-domain band, pages through every match, turns the banded metrics into numbers you can sort on, ranks them, and writes a shortlist to CSV.
It needs an API key (free on every plan) and bundles no listing data: every row it shows comes from your own live calls.
Query expired domains by category
Everything the notebook does starts from this loop. Set REVISED_API_KEY to a key from getrevised.com/account — keys are free on every plan.
import os
import time
import requests
session = requests.Session()
session.headers["Authorization"] = f"Bearer {os.environ['REVISED_API_KEY']}"
params = {
"category": "education", # Revised's own codes, see below
"rd": "50-100,100-250,250plus", # referring-domain band ids
"tier": "open", # names you may republish
"limit": 250,
}
rows, cursor = [], None
for _ in range(10): # hard cap on pages
if cursor:
params["cursor"] = cursor
r = session.get("https://getrevised.com/api/v1/domains", params=params, timeout=30)
r.raise_for_status()
page = r.json()
rows.extend(page["data"])
if not page["has_more"]:
break
cursor = page["next_cursor"]
time.sleep(0.6)
print(len(rows), "listings")Swap education for any other code and the rest is unchanged. The endpoint has no metric sort — sort takes only latest — so ranking happens locally, which is what the rest of the notebook is for.
What comes back
The shape of the rows you get back, shown with a made-up name (example-name.com). Your own run returns live listings:
domain | referring_domains | backlinks | linked_from | agent_citability score | age_years |
|---|---|---|---|---|---|
| example-name.com | 100-250 | 100-500 | Wikipedia | 60 | 8 |
| example-name-two.com | 50-100 | <100 | Hacker News | null | 5 |
Three things to read off it:
- Metrics are bands, not points.
100-250is the claim; the notebook’sband_midpointturns it into a number for sorting only. nullmeans unknown, not zero. A row withagent_citability: nullhas no citability data, andmetrics_missingsays so. The ranking drops that component rather than scoring it as nothing.- Small link counts can still be worth reading. A named linker such as Wikipedia says more than hundreds of anonymous links. Evaluate a domain covers how to weigh them.
These rows will go stale: a name someone registers leaves the directory. Run the query for today’s list.
Pick a category
category takes Revised’s own codes, not English words: education, saas, fintech, healthcare, ecommerce, ai, security, legal, real-estate, travel and ten more. There is no marketing and no software. Comma-separate to search several at once — category=fintech,crypto.
The full list with per-category counts is in Find domains, and the notebook carries it as a table because the REST API has no categories endpoint. An unknown code is refused, not answered with an empty page, so a typo fails loudly.
Many listings carry no category, so a category filter reaches only part of the directory. If breadth matters more than niche, drop category and filter on q, which searches keyword tags and blurbs.
What “with backlinks” means in a listing
Every listing is a name that was registered, lapsed, and still has sites linking to it. These are the fields that describe those links:
| Field | What it tells you | Filter |
|---|---|---|
referring_domains | How many distinct domains link in, as a band. | rd, by band id |
backlinks | Total links, as a band. Many can come from one site. | — |
linked_from | Named, tracked sites that link in — Wikipedia, a university, Hacker News. | source, linkedFromMin |
snapshots | How much of the old site’s history is archived, as a band. | — |
agent_citability | Agent Citability (preview), a 0–100 score: how deeply sources that AI assistants cite link to the domain. null when unknown. | citabilityMin — drops every unknown row |
rd takes band ids, not the values the response reports. Filter with lt10, 10-50, 50-100, 100-250 or 250plus; the response says <10 and 250+. Copying a value straight out of a response into the filter is a 400 on exactly the two ends you are most likely to want. More in Filters.
Read linked_from with your own eyes. A small referring-domain count with Wikipedia or a university in it is often worth more than hundreds of forgettable links, and no aggregate captures that.
What the notebook covers
| Section | What you get out of it |
|---|---|
1. GET /api/v1/me | Why this is always the first call: plan, remaining reveals, rate limit, change-feed access. |
2. tier | The three shelves, and the two independent reasons domain can be null. |
| 3. Categories | All 20 codes with their listing counts, because there is no categories endpoint on the REST API. |
| 4. Paging | A cursor loop with a page cap, a pause between requests, and the watermark taken from the first page. |
| 5. Bands | A parser for "10-50", "<100", "250+" and "1K-5K", and why the API publishes ranges instead of numbers. |
| 6. Ranking | A four-component weighted score with the weights on the outside, where you can argue with them. |
| 7. Reveal | The only call that spends a reveal, why it is free here, and why availability_checked_at is not a live check. |
| 8. Export | A CSV write with a tier == "open" assertion immediately in front of it. |
Four things worth stealing
The paging loop. It stops on has_more rather than on an empty page, keeps a hard max_pages cap so a bad filter cannot run away with your rate-limit window, sleeps between requests, and keeps the synced_at watermark from the first page — which is the value you would pass as since on the next run.
band_midpoint. Four band shapes, one function, None preserved as None. The midpoint is for sorting; the band string is what you show a reader.
Missing components are dropped, not zeroed. The ranking re-normalises the remaining weights and reports a components_used column beside every score. A row scored on two of four components is a weaker claim than one scored on all four, even when its number is higher.
The export assertion.
assert (df["tier"] == "open").all(), \
"refusing to export: non-open rows are disclosed under the API terms, not for republication"It sits immediately before to_csv, not at the top of the cell, so no later edit to the filters can quietly bypass it. See Republishing rules.
What it does not do
It does not decide anything for you. A high rank score is a reason to look at a domain, not a reason to register one — the notebook’s closing section is a checklist for what to do afterwards: read the archive, confirm the links still exist on the live pages, confirm availability at a registrar, and have a plan for the content.
It also does not cover holds or the since change feed. Those are in Reveals and holds and Incremental sync.
Handling your key
The notebook reads the key from an environment variable or a getpass prompt, so it never lands in the notebook file and never ends up in saved output. Do not paste a key into a cell you intend to share.
API_KEY = os.environ.get("REVISED_API_KEY") or getpass.getpass("Revised API key (rvd_...): ").strip()Set REVISED_API_KEY in your environment, or type the key at the prompt.
Questions
Can I search for a specific expired domain by name?
No. q covers the masked name hint, keyword tags, category and blurb — never the real domain name, on any plan. If you already have a listing id, use GET /api/v1/domains/{id}.
Are these domains available to register?
Each one was available when Revised last checked, and availability_checked_at records when that was. It is a timestamp, not a live check. Confirm at a registrar before you act.
Does the API cost anything?
Listing and paging are free on every plan, inside a per-key rate limit of 20 requests a minute on Free, 60 on Starter and 120 on Business — see Rate limits. The only call that can spend is a reveal of a regular or featured name. Open-tier names, which is everything this notebook touches, cost nothing.
Can I publish the shortlist?
Only the open rows. Their names are already published on the Revised website. regular and featured names are disclosed under the API terms only, which is why the export cell asserts tier == "open" before it writes anything. See Republishing rules.