> ## Documentation Index
> Fetch the complete documentation index at: https://docs.quoindata.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Quoin API v3

> Free discovery, connected records, and a subject plus 50 comparable sales for $0.10.

# Quoin Property API v3

Released September 26, 2026.

Base URL: `https://portal.quoindata.com/api/v3`. Contract: `quoin-v3`.
Send `Authorization: Bearer <API key>` and a unique `Idempotency-Key` for each logical call. Keep the same key, body, API key and URL when retrying. Never expose API keys in browser code.

[OpenAPI](https://portal.quoindata.com/api/v3/openapi.json) · [Request examples](https://portal.quoindata.com/api/v3/examples.json) · [V2 reference](https://portal.quoindata.com/api/v2/reference.md)

## Prices

Prices are per successful API call, before welcome credit. There is no per-property surcharge or report fee.

| Tool                     | HTTP resource                                                | Price  | Result                                              |
| ------------------------ | ------------------------------------------------------------ | ------ | --------------------------------------------------- |
| `get_catalog`            | GET `/catalog`                                               | Free   | Coverage, field and source metadata                 |
| `resolve_entities`       | POST `/resolutions`                                          | Free   | Bounded identity candidates                         |
| `get_entity`             | GET `/entities/{entity_ref}`                                 | Free   | Identity and identifiers only                       |
| `search_properties`      | POST `/property-searches`                                    | Free   | Selective identity lookup only                      |
| `get_evidence`           | POST `/evidence-lookups`                                     | Free   | Previously delivered claims, scoped to your account |
| `query_properties`       | POST `/record-queries`, `mode: catalog` or approved `values` | Free   | Field catalog or finite code dictionary             |
| `query_properties`       | POST `/record-queries`, `mode: rows` or `summary`            | \$0.05 | Records or aggregates, per page                     |
| `get_property`           | POST `/property-queries`                                     | \$0.05 | Subject report or bounded property details          |
| `traverse_relationships` | GET `/entities/{entity_ref}/relationships`                   | \$0.05 | Connected entities and relationships                |

One subject report costs **$0.05**. A subject plus **up to 50 enriched comparable sale events costs $0.10**, using exactly two paid calls. More pages or additional details cost \$0.05 each. The API does not automatically select comparables or generate an appraisal. Your filters determine the sale set.

Free discovery and evidence do not consume welcome credit or require a card or completed billing setup. All calls require an active key and unsuspended account. Verified accounts receive the existing \$50 welcome credit once per email; paid usage beyond available credit requires a saved payment method and is billed monthly. The portal's usage totals count paid calls only.

Successful empty queries are paid. A mixed property batch costs once if at least one item succeeds; a wholly unavailable batch is uncharged. Invalid requests, denied access, rate limits, oversized responses and service failures are uncharged. Retrying a completed call with the same idempotency key adds no charge. A lost response may already have committed: always retry with that key.

[Executable Node.js example](https://portal.quoindata.com/report-v3.mjs): import `buildReport`, supply your resolved subject, selection filters and a persisted report ID. It performs exactly two paid calls and does not paginate automatically.

## Two-call report

Resolve the subject for free, and let the caller choose if multiple candidates match:

```json theme={null}
{"items":[{"client_id":"subject","area_uid":"area_us_dc","subject_kind":"tax_account","identifiers":[{"namespace":"dc_ssl","raw_value":"12820198"}]}],"candidate_limit":5}
```

POST this to `/resolutions`. Use a returned candidate's `property_ref`. Do not infer property-read support solely from an identifier prefix.

**Call 1 — POST `/property-queries`, \$0.05:**

```json theme={null}
{"property_refs":["<resolved property_ref>"],"projection":"report_subject_v1"}
```

The response `data.items[0]` contains:

* `sections.identity`: address, SSL, jurisdiction, property type/use and active identity.
* `sections.ownership`: current published owner names. `ownership_form` is explicitly unavailable; names do not establish ownership form or title.
* `sections.assessment`: reported land, improvement and total values in dollars, with source date. The assessment year is unavailable in this bounded summary.
* `sections.tax`: published annual tax and reported balance, with source date. The period is unavailable in this summary. This is not a current debt or legal delinquency finding.
* `sections.transfer`: latest source-reported sale date, price and source codes. A shared transaction amount may apply to multiple interests.
* `characteristics` and `sections.building`: bedrooms, separate full/half baths, living/gross area in square feet, year built/effective year when available, grade and condition.
* `sections.land`: reported lot area in square feet.
* `sections.permit`: count of linked permit-related records, up to five latest records, `has_more` and limitations. Issuance does not prove completion. For additional permit records, make an explicit paid property query with `sections: ["permit"]` and `section_limit`, then use `cursors.permit` if present.
* `location`: source-qualified point, or `null` when unavailable/ambiguous. Supported points are permit-derived, not guaranteed parcel centroids.

Facts preserve `value`, `status`, `source_refs` and available source dates. Building characteristics use one profile-level status: `reported`, `ambiguous`, or `unavailable`. Each null characteristic is unavailable. Multiple linked profiles produce `ambiguous` with null characteristic fields; no arbitrary building is selected. Current characteristics are not historical characteristics at the sale date.

**Call 2 — POST `/record-queries`, \$0.05:**

```json theme={null}
{
  "mode":"rows","dataset":"sales","projection":"report_comparables_v1",
  "subject_property_ref":"<resolved property_ref>",
  "filters":{
    "property":[{"field":"neighborhood","op":"eq","value":"Georgetown"}],
    "sale":[{"field":"sale_date","op":"gte","value":"2020-01-01"},{"field":"price_dollars","op":"gte","value":100000}]
  },
  "limit":50
}
```

`data.rows` contains up to 50 **sale events**, each with `record_ref`, `property_ref`, address, sale date/price/qualification, basic property type/use/lot area, location, and the same building characteristics. Repeated sales of one property remain separate events. Full ownership, tax and permit histories for each comparable are not included. Do not perform an additional property read for every row.

The subject is excluded when `subject_property_ref` is supplied. Ordering is sale date descending, then `record_ref` ascending. Null dates sort last. `returned_count`, `has_more` and `next_cursor` describe pagination. A next page is another \$0.05 call, never automatic. Cursors bind the account, normalized filters and published snapshot; restart when a cursor is invalid or the publication changes.

For a radius search, supply `radius_miles` with the subject reference. Missing/ambiguous subject coordinates return an uncharged error. Alternatively supply `location: {latitude, longitude, radius_miles}`. Radius queries exclude properties without supported locations and cannot promise complete geographic coverage. `distance_miles` is available only when a location filter is used.

Both reports include `sources` and `snapshot`. If the snapshots differ, show that the source publication changed between calls. The calls are not an atomic report snapshot. Preserve source dates, unavailable fields, source codes and caveats in your report.

## Free discovery boundaries

Catalog accepts `facet`: `jurisdictions`, `authorities`, `coverage`, `dictionary`, `capabilities`, or `sources`, with the filters described in OpenAPI. This returns metadata, not record counts or facts.

Resolution accepts up to 50 input identities, at most five candidates per identity and 50 candidates total. `more_candidates` signals a narrowed query is needed. It does not unlock a free export cursor.

Search requires exactly one exact `entity_ref`/`property_ref` or a selective address prefix containing a street number and street name (at least eight characters). It returns at most 20 identity summaries and has no cursor. Wildcard and broad neighborhood/owner searches are not free lookup operations. Example:

```json theme={null}
{"area_uid":"area_us_dc","subject_kind":"tax_account","filters":[{"field":"address","value":"1642 30TH"}],"sort":{"field":"address","direction":"asc"},"limit":20}
```

Identity summaries expose identity, matching identifiers, address, match basis and scoped source references. Ownership, valuation, tax, sale facts, building characteristics and relationship graphs require paid tools.

`POST /record-queries` with `{"mode":"catalog"}` lists supported fields and datasets. Free `values` accepts only `sale.qualified_code`, `property.use_code`, or `property.tax_class`, with no filters, location, grouping or cursor. It returns code/label pairs, never live record counts or arbitrary distinct values.

Example: `{"mode":"values","field":"property.use_code"}`. Arbitrary neighborhood, owner, address or price lists are not free vocabulary endpoints.

## Other paid tools and filters

A generic property query accepts up to five `property_refs` and existing section controls; the report projection accepts exactly one and no section/history selectors. Generic sections retain their documented bounded coverage and permit pagination.

Record queries support datasets `properties`, `sales`, `tax_periods`, `buildings`; up to 50 rows or groups; at most 20 filters. Scope keys are `property`, `sale`, `tax`, `building`. Get the exact fields from the free catalog. Supported operators: `eq`, `ne`, `in`, `gte`, `lte`, `between`, `contains`, `is_null`, `not_null`. Filters in one event/profile scope must match the same record; other child scopes use existence without multiplying rows. Unknown fields are rejected.

Summary mode supports one `group_by` field and one numeric `measure` from the selected grain. Ungrouped summaries are supported. Amounts in summaries may be decimal strings; `_dollars` means USD and `_sqft` means square feet. Generic record rows use stable identity order; the comparable projection uses sale-date order.

Relationships accept `direction`, optional `predicates`, `max_depth` (1–3), `max_nodes`/`max_edges` (up to 200), `limit`, and `cursor`. This remains a separate paid request. See OpenAPI for all input schemas.

## Scoped evidence and saved data

Send up to 50 returned `qev3.…` references to `/evidence-lookups`:

```json theme={null}
{"source_refs":["<source_ref returned to your account>"],"include_verification_routes":true}
```

Evidence returns a `claim` snapshot of information originally delivered to your account, its original source reference, and available official verification links. It does not expand underlying records or unlock neighboring fields. Unknown, cross-account, expired and raw v2 references return per-item `outcome: not_authorized`. Entitlements last 90 days. Evidence alone cannot refresh an old claim; use a paid reader for current facts.

You may save delivered responses for your own reports and reuse that data without another API charge. Preserve its source date and provenance; do not describe it as freshly retrieved. This permission does not grant resale, redistribution as a database, or bulk replication rights. Limits constrain extraction but cannot make delivered data impossible to copy.

## Idempotency, envelope and headers

Responses use `{contract_version, operation, request_id, status, data, error, meta}`. `status` is `ok`, `partial` or `error`. Errors have `data: null` and `{code, message, retryable}`. Inspect per-item outcomes on partial success.

A completed idempotency identity is scoped to your account and v3; its fingerprint also includes the API key and normalized request. Reusing it with another key/body/operation returns 409. Stored responses replay for seven days; after that the identity remains reserved and returns 409 without executing or charging again. Revocation and suspension apply to replays.

| Header                                      | Meaning                                                                    |
| ------------------------------------------- | -------------------------------------------------------------------------- |
| `X-Quoin-API-Version`                       | `quoin-v3`                                                                 |
| `X-Quoin-Rate-Version`                      | `portal-v3-2026-09`                                                        |
| `X-Quoin-Price-Micro-USD`                   | Original completed call price, 0 or 50000                                  |
| `X-Quoin-Charged-Micro-USD`                 | New gross usage before welcome credit; 0 on free calls, errors and replays |
| `X-Quoin-Replayed`                          | `true` when returning a stored response                                    |
| `X-Quoin-Balance-Micro-USD`                 | Welcome credit remaining at original settlement                            |
| `X-Quoin-Held-Micro-USD`                    | 0 after settlement                                                         |
| `X-Quoin-Calls-30d`                         | Paid calls in the last 30 days at original settlement                      |
| `X-RateLimit-Limit`, `-Remaining`, `-Reset` | Account minute limit, remaining requests, reset Unix timestamp             |
| `Retry-After`                               | Seconds to wait after HTTP 429                                             |

One dollar is 1,000,000 micro-USD. Charged usage is not necessarily a cash/card charge. Replay retains original balance and usage headers; it is not a live balance endpoint.

## Limits and errors

Limits apply across all keys on an account: 60 authenticated execution attempts per rolling minute; four concurrent requests; 10,000 free and 1,000 paid attempts per rolling 24 hours; 5,000 distinct identities and 2,000 distinct detailed property/entity references delivered per rolling 24 hours. Failed executions count toward attempt limits. Stored retries count only toward minute/concurrency limits and do not consume daily execution or another distinct-record allowance. Detailed relationship nodes count toward detail limits.

Request and final response limit: 256 KiB. SQL budget: up to eight seconds; execution/settlement deadline: 12 seconds. Oversized report responses fail explicitly without a charge or silent row reduction. Narrow the request instead of assuming every filter can return 50 records within these budgets.

| HTTP      | Meaning                                                                       |
| --------- | ----------------------------------------------------------------------------- |
| 400       | Invalid request/filter/cursor; fix input or restart pagination                |
| 401 / 403 | Invalid/revoked key, unavailable or suspended account                         |
| 402       | Paid call needs available credit or a saved payment method                    |
| 404       | Unknown route or missing reference                                            |
| 409       | Idempotency mismatch or expired stored response                               |
| 413 / 422 | Oversized input/output, unavailable projection or unsupported selection       |
| 429       | Account request or distinct-record limit; honor `Retry-After`                 |
| 503       | Data, accounting or timeout failure; retry only with the same idempotency key |

## Migrating from v2

V2 remains at `/api/v2` with its existing prices and response shapes. Switching to v3 is explicit; no special pricing header is required. Change the URL, use identity-only discovery, replace raw evidence references with returned v3 references, and use the two report projections to avoid per-comparable requests. Do not reuse v2 cursors in v3. Do not assume v2 entity facts or arbitrary values queries remain free in v3. Existing API keys work on both versions.

This release does not add a sandbox, usage/balance API, webhook feeds, volume tiers, Maryland or Virginia coverage. It does not provide appraisals, title conclusions or comprehensive source coverage.
