Evaluate a domain
Read a listing properly — banded metrics, missing data, marquee linkers, and the availability stamp that is not a check.
A page of listings gives you rows. Turning rows into a judgement means handling three things the API is deliberate about: the metrics are bands, a missing metric is not a zero, and the availability timestamp is not a live check.
Read the whole row
curl -s https://getrevised.com/api/v1/domains/LxRrd32h \
-H "Authorization: Bearer $REVISED_KEY"{
"id": "LxRrd32h",
"status": "active",
"domain": "septictankcleaningsydney.com.au",
"tier": "open",
"name_disclosed": true,
"tld": "com.au",
"category": "other",
"referring_domains": "100-250",
"backlinks": "100-500",
"domain_authority": "10-19",
"revised_score": { "low": 85, "high": 95 },
"agent_citability": null,
"spam": "low",
"age_years": 4,
"snapshots": "<10",
"linked_by": ["FCC"],
"metrics_missing": ["agent_citability"],
"hold_state": "none",
"estimated_value_cents": 100,
"availability_checked_at": "2026-09-19T21:04:06.589Z",
"updated_at": "2026-09-19T21:04:06.589Z"
}| Field | What it tells you |
|---|---|
referring_domains | How many distinct sites link here. The most durable signal on the row — far harder to manufacture than raw backlink count. |
backlinks | Total links. A large number over a small referring-domain count usually means one site linking many times. |
domain_authority | An aggregate of the link graph, 0–100. Correlates with referring domains, but not perfectly. |
revised_score | Revised’s own published range. rsMin filters its lower bound. |
agent_citability | How often the domain is cited by the sources AI answer engines draw on. A different question from link authority. Methodology. |
spam | low or moderate. Filters the obvious cases, not all of them. |
snapshots | How much of the site the public archive holds — a proxy for whether it was a real site rather than a parked shell. |
linked_by | Named, recognisable domains that link here. Read this column. |
age_years | Age in whole years, where known. |
metrics_missing | Which of the optional metrics on this row are null. |
The metrics are bands
referring_domains is "100-250", not 137. domain_authority is "10-19". revised_score is a {low, high} range.
That is deliberate: a point estimate implies a precision the underlying link data does not have. A domain measured at 137 referring domains today might measure 119 next week with nothing about the domain having changed, and sorting on a fake third significant figure produces a fake ranking.
So flatten bands to a midpoint for sorting, keep the band string for display, and never show a midpoint to a reader as if it were a measurement. Four shapes to handle: "10-50", "<100", "250+" and "1K-5K".
_SUFFIX = {"K": 1_000, "M": 1_000_000}
def _num(token):
"""'1K' -> 1000.0, '250' -> 250.0"""
token = token.strip().upper()
if token and token[-1] in _SUFFIX:
return float(token[:-1]) * _SUFFIX[token[-1]]
return float(token)
def band_midpoint(band):
"""Flatten a published band to a single sortable number.
'10-50' -> 30.0 midpoint
'<100' -> 50.0 half the ceiling
'250+' -> 375.0 the floor plus 50%, arbitrary but consistent
None -> None genuinely unknown; not zero
"""
if band is None or band == "":
return None
band = str(band).strip()
if band.startswith("<"):
return _num(band[1:]) / 2
if band.endswith("+"):
return _num(band[:-1]) * 1.5
if "-" in band:
low, high = band.split("-", 1)
return (_num(low) + _num(high)) / 2
return _num(band)
for probe in ["10-50", "<100", "250+", "1K-5K", "0-9", None]:
print(f"{str(probe):>8} -> {band_midpoint(probe)}") 10-50 -> 30.0
<100 -> 50.0
250+ -> 375.0
1K-5K -> 3000.0
0-9 -> 4.5
None -> NoneMissing is not zero
agent_citability: null means the domain has no row in the current index — not a score of zero. The same goes for domain_authority, backlinks, age_years and category. Every row carries metrics_missing naming exactly which of its optional metrics are absent, so you never have to guess whether a blank means “we looked and found nothing” or “we have not looked”.
Filling a missing metric with 0 silently pushes those rows to the bottom of your ranking. That is a judgement, and not one you meant to make.
The honest alternative: drop the missing component and re-normalise the remaining weights.
import math
WEIGHTS = {"rd_mid": 0.35, "da_mid": 0.25, "citability": 0.25, "snap_mid": 0.15}
# Ceilings past which more stops meaning better, for the log-scaled components.
CEILINGS = {"rd_mid": 500.0, "snap_mid": 200.0}
def _component(column, value):
if value is None:
return None
if column in CEILINGS:
# log1p so 0 maps to 0, then clamp at the ceiling.
return min(math.log1p(value) / math.log1p(CEILINGS[column]), 1.0)
return min(max(value / 100.0, 0.0), 1.0) # DA and citability are already 0-100
def rank_score(row):
"""Weighted 0-100 score. Missing components are dropped, not zeroed."""
total, used = 0.0, 0.0
for column, weight in WEIGHTS.items():
value = _component(column, row.get(column))
if value is not None:
total += value * weight
used += weight
if used == 0:
return None
return round(100 * total / used, 1)
def components_used(row):
"""How many of the four components this score is actually built on."""
return sum(_component(c, row.get(c)) is not None for c in WEIGHTS)
candidate = {"rd_mid": 175.0, "da_mid": 15.0, "citability": None, "snap_mid": 5.0}
print(rank_score(candidate), "from", components_used(candidate), "of", len(WEIGHTS), "components")50.6 from 3 of 4 componentsTwo rules the weights do not express, and both matter:
- Log-scaling on the count-like components. The gap between 10 and 100 referring domains matters much more than the gap between 1,000 and 1,090.
- Report how many components were used. A row scored on two of four is a weaker claim than one scored on all four, even when the number is higher. Re-normalising keeps the arithmetic honest; it cannot invent the missing evidence.
Weights are a judgement call, not a fact. Keep them where whoever reads the ranking can argue with them.
Scan linked_by before you trust the score
linked_by names recognisable domains that link to this one. A listing with a small referring-domain count and a university, a government body or Wikipedia in linked_by is often more interesting than one with hundreds of forgettable links — and no single aggregate captures that. It is the column most worth reading with your own eyes.
Availability is a stamp, not a check
Measure the age of the stamp and act on it:
from datetime import datetime, timezone
checked = datetime.fromisoformat(row["availability_checked_at"].replace("Z", "+00:00"))
age_hours = (datetime.now(timezone.utc) - checked).total_seconds() / 3600
if age_hours > 48:
print(f"{row['id']}: availability last checked {age_hours:.0f}h ago — re-check before acting")LxRrd32h: availability last checked 62h ago — re-check before actingcheckedDays filters the directory on stamp freshness, so you can ask for only recently-checked rows at the source.
Before you register anything
Read the archive
Pull up what the site actually was. A high referring-domain count on a domain whose archived pages are pharmacy spam is a liability, not an asset —
spam: "low"filters the obvious cases, not all of them.Confirm the links still exist
A link recorded in a crawl is not necessarily a link on the live page today.
Confirm availability at a registrar
The stamp is not a check, and a Revised hold reserves the listing here and nothing else.
Have a plan for the content
Inheriting links to a site about Queensland fishing and pointing them at something unrelated wastes most of what you just picked up.