{"components":{"responses":{"Error":{"content":{"application/json":{"schema":{"properties":{"error":{"properties":{"available_at":{"description":"On `HOLD_COOLDOWN`: when your own cooldown on that listing ends.","format":"date-time","type":"string"},"code":{"description":"The HTTP-level reason. `upgrade_required` (402) means exactly two things on this API and nothing else: the `since` feed on a plan without it, or a reveal refused for quota.","enum":["forbidden","invalid_request","method_not_allowed","not_found","rate_limited","server_error","unauthorized","upgrade_required"],"type":"string"},"credits_needed":{"description":"On `QUOTA_EXHAUSTED`: credits this reveal costs (1, or the plan's featured weight).","type":"integer"},"credits_remaining":{"description":"On `QUOTA_EXHAUSTED`: credits left this period. Can be above zero when a featured reveal costs more than what is left.","type":"integer"},"expires_at":{"description":"On `HELD_BY_OTHER`: when the listing frees up. Never who holds it.","format":"date-time","type":"string"},"hold_code":{"description":"The specific reason a hold was refused. This is what to branch on — `REVEAL_REQUIRED`, `HELD_BY_OTHER` and `HOLD_COOLDOWN` are three completely different next actions.","enum":["ALREADY_EXTENDED","HELD_BY_OTHER","HOLDS_REQUIRE_PRO","HOLD_CAP_REACHED","HOLD_COOLDOWN","HOLD_FAILED","HOLD_NOT_FOUND","HOLD_OVER","NOT_FOUND","REVEAL_REQUIRED","SIGN_IN_REQUIRED"],"type":"string"},"limit":{"description":"On `HOLD_CAP_REACHED`: the plan's ceiling.","type":"integer"},"message":{"type":"string"},"resets_at":{"description":"On `QUOTA_EXHAUSTED`: when the monthly reveal allowance rolls over.","format":"date-time","type":"string"},"reveal_code":{"description":"The specific reason a reveal was refused.","enum":["FEATURED_REQUIRES_PRO","HELD_BY_OTHER","NOT_FOUND","QUOTA_EXHAUSTED","REVEAL_FAILED","SIGN_IN_REQUIRED"],"type":"string"}},"required":["code","message"],"type":"object"}},"required":["error"],"type":"object"}}},"description":"Something refused the request. Every value of `error.code`, and of the `reveal_code` / `hold_code` a refused write adds, is enumerated in the schema — read them there rather than from this sentence. A 429 carries `Retry-After` alongside the same `X-RateLimit-*` headers every other response has. A 405 carries `Allow`."}},"schemas":{"EndedHold":{"properties":{"can_extend":{"description":"False once the single permitted extension has been taken.","type":"boolean"},"expires_at":{"format":"date-time","type":"string"},"extend_hours":{"description":"Hours the extension would add, on this plan.","type":"integer"},"held_at":{"format":"date-time","type":"string"},"id":{"type":"string"},"listing_id":{"type":"string"},"domain":{"description":"The registrable name. Present because a hold requires a reveal of the same listing by this account.","type":["string","null"]},"ended_at":{"format":"date-time","type":"string"},"outcome":{"enum":["expired","released","registered"],"type":"string"}},"required":["id","listing_id","expires_at","held_at","can_extend","ended_at","outcome"],"type":"object"},"Hold":{"properties":{"can_extend":{"description":"False once the single permitted extension has been taken.","type":"boolean"},"expires_at":{"format":"date-time","type":"string"},"extend_hours":{"description":"Hours the extension would add, on this plan.","type":"integer"},"held_at":{"format":"date-time","type":"string"},"id":{"type":"string"},"listing_id":{"type":"string"}},"required":["id","listing_id","expires_at","held_at","can_extend"],"type":"object"},"HoldQuota":{"properties":{"active":{"description":"Holds running right now.","type":"integer"},"hold_hours":{"description":"Hours a new hold runs for on this plan.","type":"integer"},"limit":{"description":"Holds this plan may keep at once. 0 means none.","type":"integer"},"plan":{"type":"string"}},"required":["active","limit","hold_hours","plan"],"type":"object"},"Listing":{"properties":{"age_years":{"description":"Age in whole years.","type":["integer","null"]},"agent_citability":{"description":"Agent Citability score and the AI-cited sources that link to the domain. Null when the domain has no row in the current index — not a score of zero.","properties":{"score":{"type":"integer"},"sources":{"items":{"type":"string"},"type":"array"}},"type":["object","null"]},"availability_checked_at":{"description":"When we last checked that the name is unregistered. Revealing a name in the directory re-checks it on the spot; exports and this API carry the last-checked stamp rather than triggering a check of their own, so read this timestamp before acting on the row.","format":"date-time","type":"string"},"backlinks":{"description":"Banded total backlinks.","type":["string","null"]},"category":{"type":["string","null"]},"discovered_at":{"description":"When this listing entered the directory. Written once and never rewritten, which is what `sort=latest` orders by and what makes that ordering safe to page. It is a fact about this directory, not about the domain: it is neither the registration date nor the date the name expired.","format":"date-time","type":"string"},"domain":{"description":"The registrable name, or NULL where it is withheld. TWO INDEPENDENT REASONS it can be null, and you cannot tell them apart from this field alone. (1) Another account holds the listing (`hold_state: \"other\"`) — a hold masks the name for every reader, and an unlimited reveal allowance is not an exemption from one; it comes back when their hold ends, and `hold_expires_at` says when. (2) Your plan has a monthly reveal ceiling and you have not revealed this listing: you receive names on the open tier (`tier: \"open\"`, published on the site anyway) and on anything you have already revealed, and a mask on the rest. `POST /domains/{id}/reveal` buys one. On a plan with no ceiling, only reason (1) applies. `mask_hint` is populated either way and is what to render. Read `tier` before republishing: only an `open` name is public.","type":["string","null"]},"domain_authority":{"description":"Banded domain authority, 0-100, from our link graph. Null where we have no figure — not a score of zero.","type":["string","null"]},"estimated_value_cents":{"description":"The valuation model's low bound, in cents, falling back to the reference figure on a row the model has not valued. Nothing here is for sale. This is the SAME number `priceRange` buckets, so a band and the values it returns agree.","type":"integer"},"featured":{"type":"boolean"},"hold_expires_at":{"description":"When the active hold on this listing ends. Null when nothing is holding it.","format":"date-time","type":["string","null"]},"hold_state":{"description":"`none`, `mine` (held by this key's account) or `other`. A held listing stays in the directory and stays listed here; what a hold reserves is the right to take it, inside Revised. Who holds it is never disclosed. NOTE: taking, extending or ending a hold does not move `updated_at`, so an incremental `since` poll will not re-deliver a row whose hold state changed — read the listing directly, or `GET /holds` for your own.","enum":["none","mine","other"],"type":"string"},"id":{"description":"Eight characters, CASE-SENSITIVE and mixed case in practice (`00aLaXEG`, `X00w46xl`). Store it verbatim: lower-casing one will not match on the way back.","type":"string"},"length_bucket":{"type":"string"},"linked_by":{"description":"Recognisable domains that link to this one.","items":{"type":"string"},"type":"array"},"mask_hint":{"description":"The public partial mask, e.g. `g▓▓▓▓▓▓farms` — true length, first letter, last word, hyphens. Always present, and the thing to render when `domain` is null. Null only for rows predating the mask backfill; `length_bucket` is the fallback shape.","type":["string","null"]},"metrics_missing":{"description":"Which of this row's optional metrics are NULL, by the key they are published under: `age_years`, `agent_citability`, `backlinks`, `category`, `domain_authority`. A null among those is ABSENT DATA, never a zero — an unscored domain is not a DA-0 domain — and the distinction is otherwise invisible in JSON. Empty when the row is complete. Note that the `min_*` filters exclude null rows rather than ranking them low, so a mild-looking floor can drop most of a page.","items":{"type":"string"},"type":"array"},"name_disclosed":{"description":"Whether `domain` holds a real registrable name. The same predicate `domain` itself is built from, so the two cannot disagree — false means `domain` is null and `mask_hint` is what to render. This is the discriminator; `mask_hint` is NOT, because it is populated on every row whether or not anything is masked. It is also not a reveal: on a plan with no reveal ceiling every name is disclosed without one having been bought.","type":"boolean"},"referring_domains":{"description":"Banded referring-domain count.","type":["string","null"]},"revised_score":{"description":"The Revised Score band, low to high. Kept for existing clients; `revised_score_exact` carries the score itself.","properties":{"high":{"type":"integer"},"low":{"type":"integer"}},"type":"object"},"revised_score_exact":{"description":"The Revised Score, 0 to 100: a percentile blend of link-graph centrality, referring domains and site history, relative to the whole directory, plus up to 15 points for real visitor traffic (see `visitor_traffic`). It moves as the directory and its inputs update.","type":"integer"},"snapshots":{"description":"Banded archive snapshot count, e.g. `40+`.","type":"string"},"spam":{"enum":["low","moderate"],"type":"string"},"status":{"const":"active","description":"Always `active` on a full row. Branch on this to tell a listing from a tombstone.","type":"string"},"tier":{"description":"Which shelf this listing sits on, and the only field that says whether its `domain` is PUBLIC. `open` means the name is already published on the site, in the listing page's title and in the sitemap, so you may republish it. `regular` and `featured` names are masked everywhere on the site and are disclosed to you under the API terms only — do not republish them. `featured` additionally means the curated shelf; it is not the same thing as the `featured` boolean, which reads the shelf rank and can disagree.","enum":["featured","regular","open"],"type":"string"},"tld":{"type":"string"},"updated_at":{"description":"Feed watermark. What `since` filters on, and the version of this row: with `id` it is what makes a repeated row safe to ignore.","format":"date-time","type":"string"},"visitor_traffic":{"description":"How popular the site was with real visitors while it was live, from the monthly global top-1M list of the sites browser users visit (monthly since 2021-02). `best_rank_bucket` is the best bucket it ever held (1000 = a top-1k site; 1000, 10000, 100000 or 1000000), `months_present` how many months it was on the list, and `first_seen` / `last_seen` the first and last month (`YYYY-MM`). Released under exactly the rule that releases `domain`: null whenever `name_disclosed` is false, because on a masked row these figures would identify the name. Where `name_disclosed` is true, null means the site was never on the list — not zero traffic.","properties":{"best_rank_bucket":{"enum":[1000,10000,100000,1000000],"type":"integer"},"first_seen":{"type":"string"},"label":{"description":"e.g. `Top 100k site`.","type":"string"},"last_seen":{"type":"string"},"monthly":{"description":"Month-by-month history: one character per month from `first_seen` to `last_seen`, `1` top 1k, `2` top 10k, `3` top 100k, `4` top 1M, `.` not on the list that month.","type":"string"},"months_present":{"type":"integer"}},"type":["object","null"]},"word_count":{"type":"integer"}},"required":["id","status","domain","tier","mask_hint","name_disclosed","metrics_missing","hold_state","tld","estimated_value_cents","updated_at","discovered_at"],"type":"object"},"OwnedHold":{"properties":{"can_extend":{"description":"False once the single permitted extension has been taken.","type":"boolean"},"expires_at":{"format":"date-time","type":"string"},"extend_hours":{"description":"Hours the extension would add, on this plan.","type":"integer"},"held_at":{"format":"date-time","type":"string"},"id":{"type":"string"},"listing_id":{"type":"string"},"domain":{"description":"The registrable name. Present because a hold requires a reveal of the same listing by this account.","type":["string","null"]}},"required":["id","listing_id","expires_at","held_at","can_extend"],"type":"object"},"Tombstone":{"properties":{"id":{"type":"string"},"status":{"const":"delisted","description":"The id is no longer in the directory. Reported the same way whether it was delisted, sold or blocked.","type":"string"},"updated_at":{"description":"When it left. What `since` matched on.","format":"date-time","type":"string"}},"required":["id","status","updated_at"],"type":"object"}},"securitySchemes":{"apiKey":{"description":"Your key, as `Authorization: Bearer rvd_...`. Create one at /account, on any plan. A key carries no entitlement of its own — the OWNER's current plan is resolved behind it and refreshed at least once a minute, so a subscription that lapses or upgrades moves your limits without anything being reissued. The rate limit is per key and per plan, counted in fixed one-minute windows: Free 20, Starter 40, Pro 60, Business 120 requests a minute. Live keys per account: Free 2, Starter 2, Pro 5, Business 10. The same key authenticates the MCP endpoint at /api/mcp and shares that one counter. That endpoint also accepts OAuth, which is the easier path for an interactive client and needs no key at all; this REST API is key-only.","scheme":"bearer","type":"http"}}},"info":{"description":"Read access to the expired-domain directory: every listing with its link-history metrics, Agent Citability and, where you are entitled to it, its real name.\n\nAVAILABLE ON EVERY PLAN, including free. Two things your plan decides. First, throughput: Free 20, Starter 40, Pro 60, Business 120 requests a minute per key. Second, names — a plan with a monthly reveal ceiling receives `domain` on listings it has already revealed and on the open tier, whose names are published on the site anyway, and `null` with a `mask_hint` on the rest. `POST /domains/{id}/reveal` is how you buy one, and it is the only call that ever spends: reading a page never charges for the rows it matched. Business has no ceiling and receives every name.\n\nThe one endpoint a plan can be refused is the `since` change feed (Business only).\n\nVALUES ARE REFUSED, NOT IGNORED. A parameter this document publishes, carrying a value it cannot honour, is a 400 `invalid_request` naming the parameter and what it accepts — never a silently wider page. Parameters this document does not publish are ignored, so tracking and cache-busting keys are safe to leave on the query string.\n\nMETHODS AND CORS. A method an endpoint does not implement is 405 with `Allow`, in this same JSON envelope. Cross-origin calls work: every response carries `Access-Control-Allow-Origin: *`, `OPTIONS` answers the preflight, and the rate-limit headers are exposed to script. There is no cookie and no session here, only the key you send, so a browser gains nothing it did not already have — and never send a key from a page you do not control.\n\nAGENTS: there is an MCP server on this same host at /api/mcp — Streamable HTTP, stateless, same rate limit. It exposes search, reveal and hold as tools. It authenticates either with the same bearer key or over OAuth, where a client given nothing but the URL discovers the authorization server from the challenge on a 401 and signs the person in. See /api/mcp and /docs/api.","license":{"name":"Revised API terms","url":"https://getrevised.com/terms"},"title":"Revised API","version":"1.1.0"},"openapi":"3.1.0","paths":{"/api/v1/domains":{"get":{"description":"Listings matching the filters, in ascending listing-id order, or newest `discovered_at` first with `sort=latest`. Page with `cursor` until `next_cursor` is null. Every plan may read this; a plan with a reveal ceiling receives `domain` on open-tier rows and on rows it has already revealed, and `null` with a `mask_hint` on the rest — this call never spends a reveal to fill one in. Poll with `since` for what has changed, on a plan that includes it: a `since` page mixes full listings with tombstones for rows that have left the directory, and every row carries `status`, so read that first.","operationId":"listDomains","parameters":[{"description":"Opaque cursor from a previous response's `next_cursor`. Both orderings this endpoint offers are over columns that are written once and never rewritten, so a cursor never skips or repeats a row. A cursor BELONGS TO the ordering it came from — the default's is a listing id, `sort=latest`'s is a position in that order — so pass the same `sort` on every page of a run. TREAT IT AS OPAQUE AND DO NOT CONSTRUCT ONE: the value is checked for shape AND for a position this directory actually holds, so a cursor this endpoint did not issue is a 400 `invalid_request` — never an empty page, and never a silent resume from the middle of the feed.","in":"query","name":"cursor","schema":{"maxLength":9,"type":"string"}},{"description":"Rows per page. An integer out of range is CLAMPED, not rejected — above 250 you get 250, below 1 you get 1. A `limit` that is not a whole number is refused with 400 `invalid_request`, and that INCLUDES the empty string: `?limit=` is what `?limit=${n}` sends for an undefined n, and answering it with a page size of 1 would quietly make a full sync 250 times longer.","in":"query","name":"limit","schema":{"default":50,"maximum":250,"minimum":1,"type":"integer"}},{"description":"BUSINESS PLAN ONLY — every other parameter and endpoint here answers every plan; this one is refused with `upgrade_required` (402) on Free, Starter and Pro, and paging with `cursor` reaches the same rows without it. ISO 8601 instant. Inclusive: rows whose `updated_at` is AT or after it, INCLUDING rows that have left the directory since — those come back as tombstones (`status: \"delisted\"`, no other fields). Use the `synced_at` from the FIRST page of your previous run: it is read before any row is, so a listing updated mid-run is picked up next time rather than skipped. Consecutive polls OVERLAP by design — `synced_at` trails server time by 120 seconds so that a row written just before your last poll cannot fall through the gap between being stamped and being committed. Expect to see rows again; every row is idempotent by `id` plus `updated_at`, so upsert on `id` and ignore one you already hold at that `updated_at`.","in":"query","name":"since","schema":{"format":"date-time","type":"string"}},{"description":"Ordering. Omitted: ascending listing id, the default. `latest`: newest `discovered_at` first, tie-broken by id. No other value is accepted. `latest` cannot be combined with `since` — that one is the incremental-sync feed and is ordered by id.","in":"query","name":"sort","schema":{"enum":["latest"],"type":"string"}},{"description":"Filter, passed through to the directory query.","in":"query","name":"ageMin","schema":{"type":"integer","minimum":0,"maximum":100}},{"description":"Category code, e.g. `education` or `saas`. Comma-separate for several. The codes are the `category` values listings come back with; there is no closed list, so read one off a page of results rather than guessing. An unknown code is refused, not silently ignored.","in":"query","name":"category","schema":{"type":"string","maxLength":512}},{"description":"Filter, passed through to the directory query.","in":"query","name":"checkedDays","schema":{"type":"integer","minimum":1,"maximum":3650}},{"description":"Filter, passed through to the directory query.","in":"query","name":"citabilityMin","schema":{"type":"integer","minimum":0,"maximum":100}},{"description":"Filter, passed through to the directory query.","in":"query","name":"featured","schema":{"type":"boolean"}},{"description":"Estimated-value band, bucketed on the same figure the response reports as `estimated_value_cents` — filter by a band and the values you get back are inside it.","in":"query","name":"priceRange","schema":{"type":"string","enum":["under-100","100-249","250-999","1000-2499","2500-plus"]}},{"description":"Free-text search across the masked hint, tags, category and blurb. Deliberately NOT the real name: searching for a name you already have in mind will not find it, whatever your plan and whatever tier the listing is on. Use `GET /domains/{id}` when you know the id.","in":"query","name":"q","schema":{"type":"string","minLength":1,"maxLength":80}},{"description":"Filter, passed through to the directory query.","in":"query","name":"rd","schema":{"type":"string","maxLength":128}},{"description":"Only listings that pass the recommended heuristic (categorised, at least one marquee linker, Revised Score floor).","in":"query","name":"recommended","schema":{"type":"boolean"}},{"description":"Filter, passed through to the directory query.","in":"query","name":"rsMax","schema":{"type":"integer","minimum":0,"maximum":100}},{"description":"Minimum Revised Score (lower bound of the published range).","in":"query","name":"rsMin","schema":{"type":"integer","minimum":0,"maximum":100}},{"description":"Filter, passed through to the directory query.","in":"query","name":"source","schema":{"type":"string","maxLength":512}},{"description":"Filter. One of: low, moderate.","in":"query","name":"spam","schema":{"type":"string","enum":["low","moderate"]}},{"description":"Filter. One of: open.","in":"query","name":"tier","schema":{"type":"string","enum":["open"]}},{"description":"Exact TLD, without the dot — `com`, `com.au`.","in":"query","name":"tld","schema":{"type":"string","maxLength":256}},{"description":"Filter, passed through to the directory query.","in":"query","name":"traffic","schema":{"type":"boolean"}}],"responses":{"200":{"content":{"application/json":{"schema":{"properties":{"data":{"items":{"oneOf":[{"$ref":"#/components/schemas/Listing"},{"$ref":"#/components/schemas/Tombstone"}]},"type":"array"},"has_more":{"type":"boolean"},"next_cursor":{"type":["string","null"]},"synced_at":{"description":"Watermark for your NEXT run. Taken before this page was read and deliberately held 120 seconds behind server time, so polling with it re-delivers rows you already have rather than risking one that was mid-commit. Pass the first page's value as the next run's `since`.","format":"date-time","type":"string"}},"required":["data","next_cursor","has_more","synced_at"],"type":"object"}}},"description":"A page of listings."},"400":{"$ref":"#/components/responses/Error"},"401":{"$ref":"#/components/responses/Error"},"402":{"$ref":"#/components/responses/Error"},"405":{"$ref":"#/components/responses/Error"},"429":{"$ref":"#/components/responses/Error"}},"summary":"List domains"}},"/api/v1/domains/{id}":{"get":{"description":"One listing by id. Carries its real name where you are entitled to it — see the `domain` field. 404 once a listing leaves the directory, and 404 as well while a listing is newer than your plan's access delay.","operationId":"getDomain","parameters":[{"description":"A listing id, exactly as the feed issued it: eight characters, CASE-SENSITIVE — `00aLaXEG`, not `00alaxeg`.","example":"00aLaXEG","in":"path","name":"id","required":true,"schema":{"maxLength":8,"pattern":"^[0-9A-Za-z]{8}$","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Listing"}}},"description":"The listing."},"400":{"$ref":"#/components/responses/Error"},"401":{"$ref":"#/components/responses/Error"},"402":{"$ref":"#/components/responses/Error"},"404":{"$ref":"#/components/responses/Error"},"405":{"$ref":"#/components/responses/Error"},"429":{"$ref":"#/components/responses/Error"}},"summary":"Get one domain"}},"/api/v1/domains/{id}/reveal":{"post":{"description":"Buy one name, and return the row with it. THE ONLY CALL ON THIS API THAT SPENDS: a page of listings never charges for the rows it matched, so on a plan with a monthly ceiling this is how a masked `domain` becomes a real one. It also records the reveal, which a hold requires — `POST /domains/{id}/hold` refuses with `REVEAL_REQUIRED` until this has run for the same listing and the same account. FREE in two cases, both of which still write the ledger entry: the listing is on the `open` tier, whose name is published anyway, or the plan has no reveal ceiling. Idempotent — a repeat call returns the original `revealed_at`, costs nothing and is the safe way to re-read a name you have already bought. The allowance is counted in CREDITS: an ordinary reveal costs 1, a featured listing costs the plan's featured weight (3 on Starter, 1 on Pro), and `credits_charged` says what this call took. If the availability re-check finds the name registered right after you paid for it, the credit is refunded: `refunded: true`, `credits_charged: 0`, and the name stays yours to see. Refused with `reveal_code` `QUOTA_EXHAUSTED` (402, carries `resets_at`, `credits_needed` and `credits_remaining`) when what is left does not cover this reveal, `FEATURED_REQUIRES_PRO` on the Free plan against a featured listing, `HELD_BY_OTHER` while another account holds it, and `NOT_FOUND` once it has left the directory.","operationId":"revealDomain","parameters":[{"description":"A listing id, exactly as the feed issued it: eight characters, CASE-SENSITIVE — `00aLaXEG`, not `00alaxeg`.","example":"00aLaXEG","in":"path","name":"id","required":true,"schema":{"maxLength":8,"pattern":"^[0-9A-Za-z]{8}$","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/Listing"},{"properties":{"credits_charged":{"description":"Credits this call took after any refund. 0 for an open listing, a repeat call, an unlimited plan or a refund.","type":"integer"},"credits_remaining":{"description":"Credits left this period after this call. null = unlimited.","type":["integer","null"]},"note":{"description":"Present with `refunded`: the sentence to show a person.","type":"string"},"refunded":{"description":"Present and true when the re-check found the name registered and the credit was given back.","type":"boolean"},"revealed_at":{"format":"date-time","type":"string"}},"required":["credits_charged","credits_remaining","revealed_at"],"type":"object"}]}}},"description":"The listing, in the same shape `GET /domains/{id}` returns, plus when this account first revealed it."},"400":{"$ref":"#/components/responses/Error"},"401":{"$ref":"#/components/responses/Error"},"402":{"$ref":"#/components/responses/Error"},"403":{"$ref":"#/components/responses/Error"},"404":{"$ref":"#/components/responses/Error"},"405":{"$ref":"#/components/responses/Error"},"429":{"$ref":"#/components/responses/Error"}},"summary":"Reveal a domain"}},"/api/v1/domains/{id}/hold":{"post":{"description":"Reserve this listing inside the directory. It requires a reveal of the same listing by this account first (`POST /domains/{id}/reveal`), never spends one, and refuses with `hold_code` `HELD_BY_OTHER` while somebody else holds it, `HOLD_CAP_REACHED` at the plan's ceiling, or `HOLD_COOLDOWN` for 30 days after YOUR OWN hold on that listing ends — another account's ended hold never blocks you, and the listing is free the moment their window closes. A hold reserves the listing here and nothing else: registrability on the open internet is not guaranteed.","operationId":"holdDomain","parameters":[{"description":"A listing id, exactly as the feed issued it: eight characters, CASE-SENSITIVE — `00aLaXEG`, not `00alaxeg`.","example":"00aLaXEG","in":"path","name":"id","required":true,"schema":{"maxLength":8,"pattern":"^[0-9A-Za-z]{8}$","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"properties":{"hold":{"$ref":"#/components/schemas/Hold"},"holds":{"$ref":"#/components/schemas/HoldQuota"}},"required":["hold","holds"],"type":"object"}}},"description":"The hold. Returned unchanged when this account already holds the listing — taking a hold you already have is not a second hold."},"400":{"$ref":"#/components/responses/Error"},"401":{"$ref":"#/components/responses/Error"},"402":{"$ref":"#/components/responses/Error"},"403":{"$ref":"#/components/responses/Error"},"404":{"$ref":"#/components/responses/Error"},"405":{"$ref":"#/components/responses/Error"},"429":{"$ref":"#/components/responses/Error"}},"summary":"Hold a domain"}},"/api/v1/holds":{"get":{"description":"This ACCOUNT's holds, not this key's: the ones still running, then the most recent that have ended with their outcome (`expired`, `released` or `registered`). Every key on the account sees, extends and can end the same holds, and an ended hold taken by one key appears here for all of them — so separate keys for separate systems separate the rate-limit bookkeeping, not the holds.","operationId":"listHolds","responses":{"200":{"content":{"application/json":{"schema":{"properties":{"active":{"items":{"$ref":"#/components/schemas/OwnedHold"},"type":"array"},"ended":{"items":{"$ref":"#/components/schemas/EndedHold"},"type":"array"},"holds":{"$ref":"#/components/schemas/HoldQuota"}},"required":["active","ended","holds"],"type":"object"}}},"description":"Your holds."},"400":{"$ref":"#/components/responses/Error"},"401":{"$ref":"#/components/responses/Error"},"402":{"$ref":"#/components/responses/Error"},"405":{"$ref":"#/components/responses/Error"},"429":{"$ref":"#/components/responses/Error"}},"summary":"List your holds"}},"/api/v1/holds/{id}":{"delete":{"description":"End a hold taken by ANY key on this account — holds are account-scoped, not key-scoped. `released` gives the listing back; `registered` records that you registered the name yourself, which triggers an availability re-check and retires the listing if it now resolves. Either way the listing is available to other accounts immediately, and you cannot hold that same listing again for 30 days. `expired` is not accepted — that outcome belongs to the clock.","operationId":"releaseHold","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"properties":{"outcome":{"default":"released","enum":["released","registered"],"type":"string"}},"type":"object"}}},"required":false},"responses":{"200":{"content":{"application/json":{"schema":{"properties":{"ended_at":{"format":"date-time","type":"string"},"holds":{"$ref":"#/components/schemas/HoldQuota"},"outcome":{"enum":["released","registered"],"type":"string"},"registration":{"description":"Only on a `registered` release: what the re-check found. `taken` means the name now resolves; `unknown` means no answer, which is not the same as available.","enum":["available","taken","unknown",null],"type":["string","null"]}},"required":["ended_at","outcome","holds"],"type":"object"}}},"description":"The hold has ended."},"400":{"$ref":"#/components/responses/Error"},"401":{"$ref":"#/components/responses/Error"},"402":{"$ref":"#/components/responses/Error"},"404":{"$ref":"#/components/responses/Error"},"405":{"$ref":"#/components/responses/Error"},"429":{"$ref":"#/components/responses/Error"}},"summary":"End a hold"}},"/api/v1/holds/{id}/extend":{"post":{"description":"Take the one extension this hold is allowed. The window extends from where it was going to end, not from now. A second attempt refuses with `hold_code` `ALREADY_EXTENDED`.","operationId":"extendHold","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"properties":{"hold":{"$ref":"#/components/schemas/Hold"},"holds":{"$ref":"#/components/schemas/HoldQuota"}},"required":["hold","holds"],"type":"object"}}},"description":"The extended hold."},"400":{"$ref":"#/components/responses/Error"},"401":{"$ref":"#/components/responses/Error"},"402":{"$ref":"#/components/responses/Error"},"404":{"$ref":"#/components/responses/Error"},"405":{"$ref":"#/components/responses/Error"},"429":{"$ref":"#/components/responses/Error"}},"summary":"Extend a hold"}},"/api/v1/me":{"get":{"description":"The plan behind this key, what it may do, and when the key was last seen. Call it first: it is the cheapest way to learn your rate limit, your remaining reveals and whether the `since` feed is available to you, and it costs one request and no reveals.","operationId":"getMe","responses":{"200":{"content":{"application/json":{"schema":{"properties":{"key":{"properties":{"created_at":{"format":"date-time","type":"string"},"last_used_at":{"format":"date-time","type":["string","null"]},"name":{"type":"string"},"prefix":{"type":"string"}},"type":"object"},"plan":{"description":"The OWNER's plan right now, resolved on every request. A lapsed subscription reads as `free` here without anything having revoked the key.","enum":["free","starter","pro","business"],"type":"string"},"rate_limit":{"description":"This key's bucket, after the call you just made. `limit` is the plan's per-minute ceiling; `remaining` is what is left in the current one-minute window.","properties":{"limit":{"type":"integer"},"remaining":{"type":"integer"}},"required":["limit","remaining"],"type":"object"},"reveals":{"description":"Counted in credits. `limit` is null on a plan with no reveal ceiling. On a plan WITH one, `remaining` is the budget for `POST /domains/{id}/reveal`, and it is also what decides whether a listing you have not revealed arrives with its `domain` or with a mask. `limit` includes `rollover`, unused credits carried in from the previous period.","properties":{"featured_credits":{"description":"Credits one featured reveal costs on this plan. null = this plan cannot reveal featured listings.","type":["integer","null"]},"limit":{"type":["integer","null"]},"remaining":{"type":["integer","null"]},"resets_at":{"format":"date-time","type":"string"},"rollover":{"type":"integer"},"used":{"type":"integer"}},"type":"object"},"sync_feed":{"description":"Whether `GET /domains?since=` is included on this plan. False means that one parameter answers 402; everything else on this API still works.","type":"boolean"}},"required":["plan","reveals","rate_limit","sync_feed","key"],"type":"object"}}},"description":"Who this key belongs to."},"400":{"$ref":"#/components/responses/Error"},"401":{"$ref":"#/components/responses/Error"},"402":{"$ref":"#/components/responses/Error"},"405":{"$ref":"#/components/responses/Error"},"429":{"$ref":"#/components/responses/Error"}},"summary":"Key and plan"}}},"security":[{"apiKey":[]}],"servers":[{"url":"https://getrevised.com"}]}