audit.domains Appraisals API
Deterministic valuations and comparable sales for one domain
Deterministic valuations and comparable sales for one domain
Every API here is available over the APIs.io API and to AI agents over MCP.
One button, every client — Claude, Cursor, VS Code and the rest.
https://apis.io/mcp
find_apisBrowse and filter every API in the catalog.get_api_artifactsOne API's artifacts, grouped by type.get_openapiThe primary OpenAPI for this API.find_similar_apisAPIs that look like this one.apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.resolveTurn a domain, URL or GitHub org into the provider it belongs to.find_cohortsEvery scored population of providers in the catalog.curl "https://apis.io/api/v1/apis/audit-domains:audit-domains-appraisals-api"
curl "https://apis.io/api/v1/apis?limit=25"
Discovery needs no key. Ratings and market analysis are Pro.
Free tier, no form to fill in. Signing in shares your email address with us — we store it to create your key and to recognise you if you sign in with another provider. See our Privacy Policy and Terms.
A second provider on the same verified email joins the account you already have.
openapi: 3.2.0
info:
title: audit.domains public Appraisals API
version: 1.3.0
description: 'Deterministic domain appraisals, comparable sales, bulk valuation, and recorded-sales search. Document version 1.3.0. Since 1.3.0, `GET /api/v1/sales` serves only the curated verified corpus; the legacy `source=ledger` research source was retired so no browser-reachable database RPC can bypass the paid row-level research boundary. Since 1.0.0 the surface gained `GET /api/v1/usage`, a per-key request limiter (answered with `Retry-After` and `X-RateLimit-Reset`), and a monthly API call allowance separate from the site''s appraisal credits - reported by the `X-Credits-*` headers and refused with `402 api_quota_exhausted`. The path prefix stays `/v1`: the locked appraisal projection has not changed, key for key. What this document covers: the five key-authenticated paths listed below - `/api/v1/appraise`, `/api/v1/comps`, `/api/v1/bulk`, `/api/v1/usage`, `/api/v1/sales`. One further route exists under the same prefix and is NOT part of the public contract: `/api/v1/historical-sales/snapshots/active` is an internal serving seam for the website''s own comparables snapshot - anonymous, unmetered, edge-cached, does not use the error envelope described here, and may change without a version bump. Do not build against it. No key? A keyless $5 card lane for single appraisals is described in https://audit.domains/api/openapi-machine.json (POST https://audit.domains/api/machine/resolve); it is not part of this contract. API access is included with paid plans: every request requires an `ad_live_` API key (create one at https://audit.domains/profile?tab=api), keys work only on paid plans, and appraisal-producing calls consume the account''s monthly API allowance. That allowance is SEPARATE from the appraisal credits the website meters: spending one does not spend the other, and a large batch through the API leaves the site quota untouched. When it is spent, calls answer `402 api_quota_exhausted` until the monthly reset. `X-Credits-Limit` / `X-Credits-Remaining` / `X-Credits-Reset` response headers report that allowance and the date it refills, on every response that reached the meter (a 200, or a 402 for a spent allowance); requests rejected before metering - a malformed body, a missing key, a per-IP 429 - carry no meter to report, and `GET /api/v1/usage` reads the same figures without spending a call. `X-RateLimit-Limit` / `X-RateLimit-Remaining` currently repeat those credit figures; those names will be redefined to report the per-key request limiter after an announced notice period of at least 30 days. Key scopes: a key is created with `read` or `write` scope. Endpoints that spend an API call (`/appraise`, `/comps`, `/bulk`) require a `write` key and answer `403 insufficient_scope` otherwise; `/sales` and `/usage` accept either. Issue read-scoped keys to anything that only searches recorded sales or polls the allowance, so a leaked key from that integration cannot spend the account''s API allowance. Request limits: two limiters apply, and the tighter one decides. A per-address token bucket runs BEFORE the key is verified and is sized per endpoint - `/appraise` and `/comps` allow a burst of 10 then about 30 requests per minute, `/bulk` a burst of 4 then about 6 per minute, `/sales` and `/usage` a burst of 30 then about 60 per minute. Behind it, each key has a fixed limit per 60-second window, by plan: Plus 60/min, Pro 120/min, Max 300/min. Because the per-address bucket sits in front, one address cannot reach the per-key figure on `/appraise`, `/comps` or `/bulk` at all: on `/appraise` a single address is held near 30/min whatever the plan allows, and the Max figure of 300/min is reachable only by calling from several addresses. Both limiters answer `429 rate_limited` with `Retry-After` (seconds); only the per-key limiter also sends `X-RateLimit-Reset` (Unix seconds), so the presence of that header is what distinguishes the two. Determinism guarantee: the same domain on the same engine version always returns the same result.'
contact:
url: https://audit.domains/contact
termsOfService: https://audit.domains/terms
servers:
- url: https://audit.domains
security:
- bearerKey: []
- headerKey: []
tags:
- name: Appraisals
description: Deterministic valuations and comparable sales for one domain
paths:
/api/v1/appraise:
post:
summary: Appraise one domain (1 API call, write scope)
operationId: appraiseDomain
requestBody:
required: true
content:
application/json:
schema:
type: object
required:
- domain
properties:
domain:
type: string
examples:
- example.com
pattern: ^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z]{2,63}$
maxLength: 253
responses:
'200':
description: The locked public appraisal projection.
headers:
X-Request-Id:
$ref: '#/components/headers/X-Request-Id'
X-Credits-Limit:
$ref: '#/components/headers/X-Credits-Limit'
X-Credits-Remaining:
$ref: '#/components/headers/X-Credits-Remaining'
X-Credits-Reset:
$ref: '#/components/headers/X-Credits-Reset'
X-RateLimit-Limit:
$ref: '#/components/headers/X-RateLimit-Limit'
X-RateLimit-Remaining:
$ref: '#/components/headers/X-RateLimit-Remaining'
content:
application/json:
schema:
$ref: '#/components/schemas/Appraisal'
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
'402':
$ref: '#/components/responses/PaymentRequired'
'403':
$ref: '#/components/responses/InsufficientScope'
'405':
$ref: '#/components/responses/MethodNotAllowed'
'429':
$ref: '#/components/responses/RateLimited'
'500':
$ref: '#/components/responses/AppraisalFailed'
'503':
$ref: '#/components/responses/AuthUnavailable'
tags:
- Appraisals
/api/v1/comps:
parameters:
- name: domain
in: query
required: true
schema:
type: string
pattern: ^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z]{2,63}$
maxLength: 253
- name: limit
in: query
schema:
type: integer
minimum: 1
maximum: 5
default: 5
get:
summary: Comparable sales for a domain (1 API call, write scope)
operationId: getComps
responses:
'200':
description: Top comparable sales, indexed by the published methodology.
headers:
X-Request-Id:
$ref: '#/components/headers/X-Request-Id'
X-Credits-Limit:
$ref: '#/components/headers/X-Credits-Limit'
X-Credits-Remaining:
$ref: '#/components/headers/X-Credits-Remaining'
X-Credits-Reset:
$ref: '#/components/headers/X-Credits-Reset'
X-RateLimit-Limit:
$ref: '#/components/headers/X-RateLimit-Limit'
X-RateLimit-Remaining:
$ref: '#/components/headers/X-RateLimit-Remaining'
content:
application/json:
schema:
type: object
properties:
domain:
type: string
comps:
type: array
items:
$ref: '#/components/schemas/Comp'
methodologyVersion:
type:
- string
- 'null'
dataEdition:
type:
- string
- 'null'
generatedAt:
type: string
format: date-time
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
'402':
$ref: '#/components/responses/PaymentRequired'
'403':
$ref: '#/components/responses/InsufficientScope'
'405':
$ref: '#/components/responses/MethodNotAllowed'
'429':
$ref: '#/components/responses/RateLimited'
'500':
$ref: '#/components/responses/CompsFailed'
'503':
$ref: '#/components/responses/AuthUnavailable'
tags:
- Appraisals
head:
summary: Same as GET without the body - still spends 1 API call
operationId: headComps
description: 'HEAD runs the real GET handler and discards the body (RFC 9110), so it runs the engine and debits one API call from the monthly allowance exactly as GET does. Do NOT use it as an uptime probe: GET /api/v1/usage spends nothing and is the probe to use. Status codes and headers are those of GET.'
responses:
'200':
description: Headers only, per RFC 9110. The GET handler runs and its status and headers are returned with no body, so every status GET can answer is reachable here too.
headers:
X-Request-Id:
$ref: '#/components/headers/X-Request-Id'
X-Credits-Limit:
$ref: '#/components/headers/X-Credits-Limit'
X-Credits-Remaining:
$ref: '#/components/headers/X-Credits-Remaining'
X-Credits-Reset:
$ref: '#/components/headers/X-Credits-Reset'
X-RateLimit-Limit:
$ref: '#/components/headers/X-RateLimit-Limit'
X-RateLimit-Remaining:
$ref: '#/components/headers/X-RateLimit-Remaining'
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
'402':
$ref: '#/components/responses/PaymentRequired'
'403':
$ref: '#/components/responses/InsufficientScope'
'405':
$ref: '#/components/responses/MethodNotAllowed'
'429':
$ref: '#/components/responses/RateLimited'
'500':
$ref: '#/components/responses/CompsFailed'
'503':
$ref: '#/components/responses/AuthUnavailable'
tags:
- Appraisals
components:
schemas:
Comp:
type: object
properties:
domain:
type: string
priceUsd:
type: number
year:
type: string
venue:
type:
- string
- 'null'
similarity:
type: integer
minimum: 0
maximum: 100
basis:
type:
- string
- 'null'
description: Why this sale was matched, naming the channel the matcher scored on (shared keyword, word family, same industry, structural cohort, spelling, length/zone). null means the basis was not recorded for this comp - the match is unexplained, not explained by something unstated.
Error:
type: object
properties:
error:
type: string
description: Stable machine-readable code; the only field a client should match on. The full catalogue is published at https://audit.domains/developers#errors
message:
type: string
description: Human-readable and safe to display; never contains internals.
detail:
type: string
description: The actionable next step, when there is one.
requestId:
type: string
description: Correlation id for this request, always present; also sent as the X-Request-Id header on every response. Quote it in support requests.
docsUrl:
type: string
format: uri
description: Deep link to this code's entry in the published error catalogue.
retryable:
type: boolean
description: Whether repeating the identical request can succeed.
plan:
type: string
description: The account's plan id. Present on the per-key 429 rate_limited, on 403 insufficient_scope, and on the 402 codes.
scope:
type: string
description: The scope the key was created with. Present on 403 insufficient_scope.
requiredScope:
type: string
description: The scope this endpoint needs. Present on 403 insufficient_scope.
max:
type: integer
description: Maximum domains one bulk call accepts. Present on 400 too_many_domains.
allowed:
type: array
items:
type: string
description: Methods this path serves, matching the Allow header. Present on 405 method_not_allowed.
source:
type: string
description: The sales source that is not published. Present on 503 source_unavailable.
withoutKey:
type: array
items:
$ref: '#/components/schemas/FreeRoute'
description: What works with no key. Present on 401 key_required.
plans:
type: array
items:
$ref: '#/components/schemas/PlanOffer'
description: The plans whose keys can call this API, cheapest first. Present on 401 key_required and 402 paid_plan_required.
planTerms:
type: string
description: Whether a paid term renews. Present on 401 key_required.
pricing:
type: string
format: uri
description: The keyless API index, which carries the same plans. Present on 401 key_required.
required:
- error
- message
- requestId
- docsUrl
- retryable
Appraisal:
type: object
description: The locked v1 projection. Keys are contract-locked; new keys only arrive with a version bump.
properties:
domain:
type: string
valueUSD:
type: number
valueRange:
type: object
properties:
low:
type: number
mid:
type: number
high:
type: number
tier:
type: string
confidence:
type: number
factors:
type: array
items:
$ref: '#/components/schemas/Factor'
methodologyVersion:
type:
- string
- 'null'
engineVersion:
type: string
comps:
type: array
items:
$ref: '#/components/schemas/Comp'
maxItems: 5
appraisalHash:
type: string
description: SHA-256 over {commitment, result, hashVersion} - the appraisal's stable id (ODVS).
inputCommitment:
type: string
description: SHA-256 over {domain, engineId, engineVersion} - identity of the computation (ODVS).
resultDigest:
type: string
description: SHA-256 over the canonical numeric outputs (ODVS).
generatedAt:
type: string
format: date-time
dataEdition:
type:
- string
- 'null'
description: Version of the comparables snapshot in effect, when known.
Factor:
type: object
properties:
key:
type: string
label:
type: string
score:
type: integer
minimum: 0
maximum: 100
PlanOffer:
type: object
description: One paid plan whose key can call /api/v1, derived from the billing configuration (the same figures as https://audit.domains/pricing).
properties:
id:
type: string
description: Plan id, as /checkout?plan= names it.
name:
type: string
usdPerTerm:
type: number
description: Price of one term in USD.
term:
type: string
enum:
- month
monthlyCalls:
type: integer
description: /api/v1 calls included per term; a separate allowance from site appraisals.
usdPerCall:
type: number
description: Effective USD per included call, rounded to 4 places.
burstPerMinute:
type: integer
description: Per-key request limit per minute.
checkout:
type: string
format: uri
description: Where a person signs in and pays. An agent cannot complete it.
required:
- id
- name
- usdPerTerm
- term
- monthlyCalls
- usdPerCall
- burstPerMinute
- checkout
FreeRoute:
type: object
description: A route that works with no key. {name} stands for the domain.
properties:
what:
type: string
how:
type: string
required:
- what
- how
headers:
X-Request-Id:
description: Correlation id for this request, on every response including successes. Quote it in support requests.
schema:
type: string
X-Credits-Remaining:
description: API calls left in the current monthly allowance, after this call.
schema:
type: integer
X-Credits-Reset:
description: 'ISO 8601 instant the allowance refills: the first instant of the next UTC month.'
schema:
type: string
format: date-time
X-RateLimit-Remaining:
description: Currently repeats X-Credits-Remaining. Read X-Credits-Remaining for the allowance.
schema:
type: integer
X-RateLimit-Limit:
description: Currently repeats X-Credits-Limit. Read X-Credits-Limit for the allowance.
schema:
type: integer
X-Credits-Limit:
description: Monthly API call allowance for this account's plan.
schema:
type: integer
responses:
MethodNotAllowed:
description: method_not_allowed - this path does not serve that HTTP method. The `Allow` response header and the body's `allowed` array both list the methods it does serve.
headers:
Allow:
description: Comma-separated methods this path serves.
schema:
type: string
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
PaymentRequired:
description: 'paid_plan_required (the account is on the free plan, which does not include API access), upgrade_required (the plan does not include the requested feature - only the bulk endpoint is gated this way), or a spent allowance.
The code for a spent monthly API allowance is `api_quota_exhausted`. A deployment not yet running the separate API credit pool answers the older `quota_exhausted` for the same condition, so a client should treat both as "allowance spent" and branch the same way. Either way the website''s appraisal credits are a separate allowance and are unaffected by API calls.
The X-Credits-* headers are present on the two allowance codes, which are refused after the meter was read, and absent on paid_plan_required and upgrade_required, which are refused before it.'
headers:
X-Credits-Limit:
$ref: '#/components/headers/X-Credits-Limit'
X-Credits-Remaining:
$ref: '#/components/headers/X-Credits-Remaining'
X-Credits-Reset:
$ref: '#/components/headers/X-Credits-Reset'
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
BadRequest:
description: invalid_json (the body is not valid JSON) or invalid_domain (the domain is missing or not a registrable hostname). /api/v1/comps reads no body, so it can only answer invalid_domain.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
InsufficientScope:
description: insufficient_scope - the endpoint spends an API call and the key has read scope only. Create a write-scoped key at https://audit.domains/profile?tab=api. The body carries `scope` (the key's) and `requiredScope`.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
Unauthorized:
description: key_required (no key sent) or invalid_key (unknown, revoked, or expired). key_required also carries withoutKey (what works with no key) and plans (what a key costs).
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
headers:
WWW-Authenticate:
description: Bearer realm="audit.domains" on key_required; adds error="invalid_token" on invalid_key (RFC 6750).
schema:
type: string
AppraisalFailed:
description: 'appraisal_failed - the appraisal could not be completed. Retryable: retry with backoff, and quote the requestId if it persists.'
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
RateLimited:
description: rate_limited - either the per-address token bucket in front of key verification tripped, or the key is over its plan's per-minute limit (Plus 60, Pro 120, Max 300). See the request-limits note in the document description for the per-endpoint per-address figures. Both carry Retry-After; only the per-key limiter carries X-RateLimit-Reset and a `plan` field in the body, so that header is what tells you which limiter refused the call.
headers:
Retry-After:
description: Seconds to wait before retrying.
schema:
type: integer
X-RateLimit-Reset:
description: Unix seconds at which the current 60-second window ends. Present when the per-key limiter tripped.
schema:
type: integer
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
AuthUnavailable:
description: api_auth_unavailable - key verification is unreachable; the API fails closed rather than serving unmetered.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
CompsFailed:
description: 'comps_failed - the comparables could not be computed. Retryable: retry with backoff, and quote the requestId if it persists.'
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
securitySchemes:
bearerKey:
type: http
scheme: bearer
description: 'Authorization: Bearer ad_live_...'
headerKey:
type: apiKey
in: header
name: x-api-key