Revised

Find domains

Filter and page the directory with GET /api/v1/domains without burning a rate-limit window.

GET /api/v1/domains is the endpoint you will spend most of your requests on. It is free and unlimited inside your rate limit — a page never charges for the rows it matched, whatever tier they are on. Filter structurally on the server, page with the cursor, then narrow locally.

This guide assumes you have a key and know what a tier is. If not, start with the Quickstart.

Filter on the server first

bash
curl -s -G https://getrevised.com/api/v1/domains \
  -H "Authorization: Bearer $REVISED_KEY" \
  -d category=education \
  -d tld=com \
  -d spam=low \
  -d rsMin=70 \
  -d limit=250
ParameterAcceptsNotes
categoryA category code, comma-separated for severalSee the codes. An unknown code is refused.
tldAn exact TLD without the dot — com, com.au
spamlow, moderate
tieropenThe only value this filter accepts. There is no way to ask for regular or featured rows specifically.
featuredbooleanReads the shelf rank. Not the same thing as tier == "featured".
recommendedbooleanListings that pass the recommended heuristic: categorised, at least one marquee linker, and a Revised Score floor.
rsMin, rsMax0–100Bounds on the published Revised Score range. rsMin filters the lower bound.
citabilityMin0–100Minimum Agent Citability score.
ageMin0–100Minimum age in whole years. Rows with a null age_years never match — see below.
checkedDays1–3650Only listings whose availability was checked within that many days.
rdBand ids, comma-separated: lt10, 10-50, 50-100, 100-250, 250plusReferring-domain bands. The ids are not the values the response returns — see below.
sourceMarquee-linker display names, comma-separated, case preserved — Hacker News,Wikipedia
priceRangeunder-100, 100-249, 250-999, 1000-2499, 2500-plusBuckets the same figure the response reports as estimated_value_cents, so a band and the values it returns agree.
q1–80 charactersFree text across the masked hint, tags, category and blurb.
sortlatestOmitted: ascending listing id. latest: newest discovered_at first.
limit1–250, default 50Clamped, not rejected — see below.
cursorAn opaque cursor from a previous response
sinceISO 8601 instantBusiness plan only. See Incremental sync.

Every other endpoint and parameter on this API answers on every plan.

Page with the cursor

Paging is by opaque cursor, not offset. Stop on has_more, never on an empty page, and keep sort the same across every page of a run — a cursor belongs to the ordering it came from.

python
import time

import requests

BASE = "https://getrevised.com"


def fetch_listings(session, max_pages=10, limit=250, pause=0.6, **filters):
    """Page /api/v1/domains with a cursor. Returns (rows, watermark)."""
    rows, cursor, watermark = [], None, None

    for _page in range(max_pages):
        params = dict(filters, limit=limit)
        if cursor:
            params["cursor"] = cursor

        response = session.get(f"{BASE}/api/v1/domains", params=params, timeout=30)
        response.raise_for_status()
        payload = response.json()

        rows.extend(payload["data"])
        if watermark is None:
            watermark = payload["synced_at"]  # first page only — see Incremental sync

        if not payload["has_more"]:
            break
        cursor = payload["next_cursor"]
        time.sleep(pause)
    else:
        print(f"stopped at the {max_pages}-page cap — narrow the filters or raise it")

    return rows, watermark

Three details in that loop are load-bearing:

  • The max_pages cap. A bug in a filter cannot then run away with your whole rate-limit window.
  • time.sleep(pause). Your ceiling is per key per minute: 20 on Free, 60 on Pro, 120 on Business. At limit=250 a 0.6-second pause keeps a Free key comfortably inside its window.
  • The watermark comes from the first page only. It is read before any row is, so a listing updated mid-run is picked up next time rather than skipped.

The same thing in shell, one page at a time:

bash
curl -s -G https://getrevised.com/api/v1/domains \
  -H "Authorization: Bearer $REVISED_KEY" \
  -d tier=open -d limit=250 \
  | jq -r '.next_cursor, .has_more, (.data | length)'
Output
text
c7Kd0Pq1
true
250

Category codes

Category is the strongest filter the directory has, and the codes are Revised’s own — marketing and software are not among them. There is no categories endpoint on the REST API, so hardcode the list. (The MCP server does expose a free list_categories tool.)

Counts are as of 22 September 2026.

CodeLabelListingsOn the open shelf
otherOther1,553359
mediaMedia & Entertainment1,154260
saasSaaS599155
educationEducation512114
ecommerceE-Commerce44086
healthcareHealthcare18633
travelTravel & Hospitality17439
socialSocial Media16838
gamingGaming13329
productivityProductivity11222
real-estateReal Estate10815
fintechFintech10620
foodFood & Beverage10422
automotiveAutomotive9016
cryptoCrypto & Web38414
aiAI & Machine Learning7926
securitySecurity7918
fitnessFitness & Wellness7515
legalLegal5215
iotIoT & Hardware295

Those counts total 5,837 categorised listings out of 17,713 in the directory, so a category filter reaches about a third of it. The rest carry no category at all and no category value will return them. If breadth matters more than precision, filter on something else and sort locally.

An unknown code is refused with 400 invalid_request rather than answered with an empty page, which is what you want — a typo fails loudly instead of looking like a quiet day in the directory.

Traps

limit is clamped, but an empty limit is refused. Ask for 5,000 and you get 250; ask for 0 and you get 1. But ?limit= — which is what ?limit=${n} sends for an undefined n — is a 400, on purpose: answering it with a page size of 1 would quietly make a full sync 250 times longer.

Minimum filters exclude null rows. rsMin, citabilityMin and ageMin drop rows where the metric is missing rather than ranking them low. Since agent_citability is null whenever the domain has no row in the citability index, a mild-looking citabilityMin=20 can remove most of a page. Check metrics_missing on the rows you do get back.

ageMin is the one that catches people out, because age is unknown on a large share of the directory — 9 of the 30 open-tier rows bundled with the Python walkthrough carry a null age_years. So ageMin=1 does not mean “at least a year old”; it means “at least a year old, and we know how old it is”. If you want old domains without silently dropping every unaged row, filter on something else and sort on age_years locally, treating null as unknown rather than young.

rd takes band ids, not band values. The response reports referring_domains as "<10", "10-50", "50-100", "100-250" or "250+". The filter takes lt10, 10-50, 50-100, 100-250 or 250plus — the ids exist so you never have to percent-encode < or +. Only the middle three coincide, so copying a value straight out of a response into the filter is a 400 on exactly the two ends of the range you are most likely to want.

bash
# WRONG — these are response values, not filter ids
curl -s -G https://getrevised.com/api/v1/domains -d 'rd=<10,250+' ...

# RIGHT
curl -s -G https://getrevised.com/api/v1/domains \
  -H "Authorization: Bearer $REVISED_KEY" \
  -d rd=100-250,250plus \
  -d source='Hacker News,Wikipedia'

sort=latest cannot be combined with since. The change feed is ordered by id.

Unpublished parameters are ignored, published ones are enforced. A parameter this API documents, carrying a value it cannot honour, is a 400 naming the parameter and what it accepts — never a silently wider page. Parameters it does not document are ignored, so tracking and cache-busting keys on the query string are harmless.

Read status before you read anything else. A since page mixes full listings with tombstones. Outside the change feed every row is status: "active", but branching on it costs nothing and makes the code correct if you later add since.

Errors

Statuserror.codeWhat happened
400invalid_requestA published parameter carried a value it cannot honour, or a cursor this endpoint did not issue. The message names the parameter.
401unauthorizedNo key, or a key that no longer resolves.
402upgrade_requiredOn this endpoint it means exactly one thing: since on a plan without the change feed.
405method_not_allowedCarries Allow.
429rate_limitedCarries Retry-After alongside the X-RateLimit-* headers every response has.

Next

Type to search…

↑↓ navigate openesc close