Reveal a masked domain name, then hold it
Turn a masked listing into a registrable name, reserve it inside the directory, and handle every refusal the two calls can return.
Two calls act on a listing. POST /api/v1/domains/{id}/reveal turns a masked domain into a real name. POST /api/v1/domains/{id}/hold reserves the listing inside Revised while you make up your mind. They run in that order — a hold is refused until the same account has revealed the same listing.
Reveal
curl -s -X POST https://getrevised.com/api/v1/domains/00aLaXEG/reveal \
-H "Authorization: Bearer $REVISED_API_KEY"It returns the listing in the same shape GET /api/v1/domains/{id} does, plus revealed_at, credits_charged and credits_remaining. The name below is a fabricated illustration — regular names are never published, here or anywhere else.
{
"id": "00aLaXEG",
"status": "active",
"domain": "greenvalleyfarms.com",
"tier": "regular",
"name_disclosed": true,
"hold_state": "none",
"revealed_at": "2026-09-22T03:15:02.117Z",
"availability_checked_at": "2026-09-21T18:40:11.002Z",
"credits_charged": 1,
"credits_remaining": 29
}What it costs
Where a ceiling applies, reveals are counted in credits. A regular reveal costs 1 credit and a featured reveal costs reveals.featured_credits (1 on Starter and Business); both are refused on Free, which has no reveals, and free of charge on Starter and Business, which have no ceiling. reveals.featured_credits on GET /api/v1/me gives the rate for your plan. credits_charged on the response says what this call actually spent, and credits_remaining what is left (null with no ceiling).
When it is free
These still write a ledger entry, so the reveal is recorded either way. That matters, because that record is what a hold checks:
- The listing is on the
opentier. Its name is published on the Revised site anyway. - Your plan has no reveal ceiling.
- You already revealed it. See below.
Refund if just registered
Every first reveal re-checks availability, on every plan. If the re-check finds the name was registered since our last check, the reveal costs 0 credits. The response carries credits_charged: 0, refunded: true and a note:
{
"credits_charged": 0,
"refunded": true,
"note": "Registered since our last check — your credit was refunded."
}It is idempotent
A repeat call returns the original revealed_at, costs nothing, and is the safe way to re-read a name you have already revealed. You do not need to cache names defensively to avoid spending twice.
def reveal(session, listing_id):
response = session.post(
f"https://getrevised.com/api/v1/domains/{listing_id}/reveal", timeout=30
)
if response.status_code >= 400:
error = response.json()["error"]
raise RuntimeError(f"{response.status_code} {error.get('reveal_code')}: {error['message']}")
return response.json()Reveal refusals
Branch on error.reveal_code, not on the HTTP status — several codes share a status.
reveal_code | Status | What to do |
|---|---|---|
QUOTA_EXHAUSTED | 402 | Not enough credits for this reveal. The error carries credits_needed, credits_remaining and resets_at. A featured reveal can be refused with credits left; an ordinary listing may still fit. Otherwise stop until resets_at, or upgrade. |
FEATURED_REQUIRES_PRO | 402 | The Free plan cannot reveal a featured listing. Any paid plan can. Skip the row or upgrade. |
HELD_BY_OTHER | 403 | Another account holds it. The error carries expires_at — never who holds it. Retry after that, or move on. |
NOT_FOUND | 404 | The listing has left the directory. Drop it from your working set. |
REVEAL_FAILED | 500 | Transient. Retry once with backoff, then treat as a skip. |
SIGN_IN_REQUIRED | 401 | The key did not resolve to an account. |
A QUOTA_EXHAUSTED response is worth handling properly rather than retrying:
import requests
try:
row = reveal(session, listing_id)
except RuntimeError as exc:
if "QUOTA_EXHAUSTED" in str(exc):
me = session.get("https://getrevised.com/api/v1/me", timeout=30).json()
print(me["reveals"]["remaining"], "credits left until", me["reveals"]["resets_at"])
raiseHold
A hold takes the listing off the shelf for every other account for a plan-dependent window, and masks the name for them while it runs.
curl -s -X POST https://getrevised.com/api/v1/domains/00aLaXEG/hold \
-H "Authorization: Bearer $REVISED_API_KEY"{
"hold": {
"id": "3f8c2a1e-7b4d-4e9a-9c6f-2d1b5e8a7c30",
"listing_id": "00aLaXEG",
"held_at": "2026-09-22T03:16:40.000Z",
"expires_at": "2026-09-24T03:16:40.000Z",
"can_extend": true,
"extend_hours": 24
},
"holds": { "plan": "starter", "active": 1, "limit": 5, "hold_hours": 48 }
}Taking a hold you already have is not a second hold — the existing one comes back unchanged.
The holds block reports this account’s quota: how many holds are running, the plan’s ceiling, and how long a new hold runs for. The example is a Starter account — five holds of 48 hours, each extendable once by 24. Business allows 20 holds of 168 hours, and Free allows none; see Plans and limits. Read the numbers from the response rather than hard-coding them.
Holds are account-scoped, not key-scoped. Every key on the account sees, extends and can end the same holds. Separate keys for separate systems separate the rate-limit bookkeeping, not the holds.
Hold refusals
Branch on error.hold_code. Three of these are completely different next actions and they are easy to conflate:
hold_code | What happened | What to do |
|---|---|---|
REVEAL_REQUIRED | This account has not revealed this listing | Call reveal first, then retry the hold. |
HELD_BY_OTHER | Somebody else holds it | The error carries expires_at. The listing is free the moment their window closes. |
HOLD_COOLDOWN | Your own hold on this listing ended within the last 30 days | The error carries available_at. Another account’s ended hold never blocks you — only yours does. |
HOLD_CAP_REACHED | You are at the plan’s ceiling | The error carries limit. End a hold you no longer need. |
HOLDS_REQUIRE_PRO | The plan does not include holds | Upgrade, or work without them. |
ALREADY_EXTENDED | The one permitted extension has been taken | Nothing. Let it run out or release it. |
HOLD_NOT_FOUND, HOLD_OVER | The hold id is unknown, or has already ended | Re-read GET /api/v1/holds. |
HOLD_FAILED | An unexpected server-side failure (500) | Retry once with backoff. Taking a hold you already have is safe to repeat. |
NOT_FOUND | The listing has left the directory | Drop it. |
Extend, once
curl -s -X POST https://getrevised.com/api/v1/holds/3f8c2a1e-7b4d-4e9a-9c6f-2d1b5e8a7c30/extend \
-H "Authorization: Bearer $REVISED_API_KEY"The window extends from where it was going to end, not from now, so extending early loses you nothing. A second attempt is refused with ALREADY_EXTENDED.
End a hold
DELETE /api/v1/holds/{id} takes an outcome, defaulting to released:
curl -s -X DELETE https://getrevised.com/api/v1/holds/3f8c2a1e-7b4d-4e9a-9c6f-2d1b5e8a7c30 \
-H "Authorization: Bearer $REVISED_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "outcome": "registered" }'{
"ended_at": "2026-09-22T09:02:11.000Z",
"outcome": "registered",
"registration": "taken",
"holds": { "plan": "starter", "active": 0, "limit": 5, "hold_hours": 48 }
}releasedgives the listing straight back to everyone else.registeredrecords that you registered the name yourself. That triggers an availability re-check and retires the listing if the name now resolves. The response’sregistrationfield reports what the re-check found:takenmeans the name now resolves;unknownmeans no answer, which is not the same as available.expiredis not accepted. That outcome belongs to the clock.
Either way the listing is available to other accounts immediately, and you cannot hold that same listing again for 30 days.
Review your holds
curl -s https://getrevised.com/api/v1/holds \
-H "Authorization: Bearer $REVISED_API_KEY" | jq{
"active": [
{
"id": "3f8c2a1e-7b4d-4e9a-9c6f-2d1b5e8a7c30",
"listing_id": "00aLaXEG",
"domain": "greenvalleyfarms.com",
"held_at": "2026-09-22T03:16:40.000Z",
"expires_at": "2026-09-24T03:16:40.000Z",
"can_extend": true,
"extend_hours": 24
}
],
"ended": [
{
"id": "9a41d7c2-0e5b-4f3a-8d2c-6b7e1f0a4c95",
"listing_id": "X00w46xl",
"ended_at": "2026-09-14T22:05:00.000Z",
"outcome": "expired",
"expires_at": "2026-09-14T22:05:00.000Z",
"held_at": "2026-09-12T22:05:00.000Z",
"can_extend": false
}
],
"holds": { "plan": "starter", "active": 1, "limit": 5, "hold_hours": 48 }
}Active holds carry domain, because a hold requires a reveal of the same listing by the same account. Ended holds carry their outcome — expired, released or registered.
The full flow
Filter and rank
Page
GET /api/v1/domains. Free, and it never charges for the rows it matched. See Find domains.Reveal the shortlist, not the page
One reveal per listing you are seriously considering. Repeat calls are free, so you never need to cache the name to protect your quota.
Hold what you want to research
Refused with
REVEAL_REQUIREDuntil step 2 has run for that listing on this account.Confirm availability at a registrar
availability_checked_atis a stamp, not a live check, and a hold does not reserve the name outside Revised.End the hold with the truth
registeredif you took the name,releasedif you did not. The 30-day cooldown on your own re-hold starts either way.