> ## 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.

# Worked examples

| Operation                                                   | Method and path                                   | Price  |
| ----------------------------------------------------------- | ------------------------------------------------- | ------ |
| [get\_catalog](#get_catalog)                                | `GET /api/v2/catalog`                             | \$0.05 |
| [resolve\_entities](#resolve_entities)                      | `POST /api/v2/resolutions`                        | \$0.05 |
| [get\_entity](#get_entity)                                  | `GET /api/v2/entities/{entity_ref}`               | \$0.05 |
| [traverse\_relationships](#traverse_relationships)          | `GET /api/v2/entities/{entity_ref}/relationships` | \$0.05 |
| [search\_properties](#search_properties)                    | `POST /api/v2/property-searches`                  | \$0.05 |
| [get\_property](#get_property)                              | `POST /api/v2/property-queries`                   | \$0.05 |
| [get\_evidence](#get_evidence)                              | `POST /api/v2/evidence-lookups`                   | \$0.05 |
| [query\_properties](#comparable-sales-and-complete-permits) | `POST /api/v2/record-queries`                     | \$0.05 |

Typical flow: **catalog** to learn what is covered, **resolve** or **search** to obtain references, **get\_property** or **get\_entity** to read records, **traverse** to move between related entities, **get\_evidence** to verify.

***

### get\_catalog

`GET /api/v2/catalog` · \$0.05 per call

Describes what the API covers before you query it.

| Query parameter   | Type     | Notes                                                                               |
| ----------------- | -------- | ----------------------------------------------------------------------------------- |
| `facet`           | required | `jurisdictions`, `authorities`, `coverage`, `dictionary`, `capabilities`, `sources` |
| `area_uid`        | optional | Filters `jurisdictions` and `coverage` only                                         |
| `subject_kind`    | optional | `coverage`: domain key. `dictionary`: entity name                                   |
| `record_family`   | optional | `coverage`: property class. `dictionary`: data type                                 |
| `field_key`       | optional | `dictionary` only                                                                   |
| `limit`, `cursor` | optional | Pagination                                                                          |

Using a filter on a facet that does not support it returns HTTP 422 `unsupported_filter`.

| Facet           | Returns                                                                                                                  |
| --------------- | ------------------------------------------------------------------------------------------------------------------------ |
| `jurisdictions` | Areas, with official name and Census, USPS and FIPS identifiers. Today: `area_us_dc`                                     |
| `authorities`   | Issuing agencies, for example `auth_us_dc_otr` (Office of Tax and Revenue) and `auth_dc_mar` (Master Address Repository) |
| `coverage`      | Per area and domain: `availability_status`, `observed_record_count`, `generation_id`                                     |
| `dictionary`    | Field definitions: `field_key`, `title`, `definition`, `data_type`, `unit`, `definition_status`                          |
| `capabilities`  | The eight operations and the controls each supports                                                                      |
| `sources`       | Every ingested official dataset with `release_id`, `sha256`, retrieval time, official landing and download URLs          |

```bash theme={null}
curl "https://portal.quoindata.com/api/v2/catalog?facet=coverage&area_uid=area_us_dc&limit=5" \
  -H "Authorization: Bearer $QUOIN_API_KEY"
```

```json theme={null}
{
  "facet": "coverage",
  "items": [{
    "area_uid": "area_us_dc",
    "domain_key": "property_record",
    "property_class": "all",
    "generation_id": "10",
    "availability_status": "available",
    "availability_reason": null,
    "observed_record_count": 215744
  }]
}
```

Coverage status values: `available`, `partial`, `gated`, `unavailable`, `stale`, `source_unavailable`, `retired`, `not_applicable`. A missing record in an `available` domain means no matching record exists in the current publication, not that no public record exists anywhere.

***

### resolve\_entities

`POST /api/v2/resolutions` · \$0.05 per call, regardless of item count

Turns official identifiers or street addresses into entity references. Batch up to 50 claims per call.

**Request body**

| Field                                 | Type                   | Notes                                                                               |
| ------------------------------------- | ---------------------- | ----------------------------------------------------------------------------------- |
| `items[]`                             | 1–50                   | One claim each                                                                      |
| `items[].client_id`                   | string, required       | Your correlation id, echoed back                                                    |
| `items[].subject_kind`                | required               | `property`, `tax_account`, `parcel`, `address`, `unit`, `building`, `land_interest` |
| `items[].area_uid`                    | recommended            | `area_us_dc`. Required for address resolution                                       |
| `items[].identifiers[]`               | optional               | `{ namespace, raw_value, normalized_value? }`                                       |
| `items[].address`                     | optional               | `{ line, unit?, locality?, region?, postal_code? }`                                 |
| `items[].include_retired_identifiers` | boolean, default false | Also match retired identifiers                                                      |
| `candidate_limit`                     | 1–50, default 10       | Candidates returned per claim                                                       |

Identifier namespaces in the current publication:

| Namespace    | Meaning                                                          | Example                                    |
| ------------ | ---------------------------------------------------------------- | ------------------------------------------ |
| `dc_ssl`     | D.C. square-suffix-lot, 8 characters with or without punctuation | `04842250`, `0484--2250`                   |
| `mar`        | D.C. Master Address Repository id                                | `158900`                                   |
| `entity_ref` | A reference you already hold                                     | `qentity_0000181b67f859019efe1b488b88b1f0` |

**Response** `data.items[]`

| Field                | Meaning                                                                                                                                                                           |
| -------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `outcome`            | `resolved`, `ambiguous`, `not_found`, `address_found_no_parcel`, `retired_only`, `invalid_input`, `unavailable`                                                                   |
| `candidates[]`       | Entities that matched, each with `entity_ref`, `entity_kind`, `status`, `area_uid`, `canonical_identity`, `match_basis`, `precision`, `method`, and `property_ref` when supported |
| `address_resolution` | How the address was interpreted: `normalized_input`, `method`, `precision`, `parcel_count`, `address_count`                                                                       |
| `truncated`          | More candidates existed than `candidate_limit`                                                                                                                                    |

`precision` and `method` describe address-based matching only. For identifier matches (`match_basis: "identifier"`) both are `null`; the match is by official identifier. For address matches, `precision` is `exact` when the address maps to exactly one record, `contextual` when the match came through a parent (for example a unit resolved through its building's address), and `ambiguous` when several records fit. A contextual match is context, not proof of identity.

Address handling: punctuation and whitespace are normalized; a missing D.C. quadrant (`NW`, `NE`, `SW`, `SE`) is tried in all four positions and reported as `method: "dc_quadrant_fallback"` with `precision: "contextual"`; a `unit` value is carried through and matched separately.

```json theme={null}
{
  "items": [
    { "client_id": "docs-ssl", "area_uid": "area_us_dc", "subject_kind": "tax_account",
      "identifiers": [{ "namespace": "dc_ssl", "raw_value": "04842250" }] },
    { "client_id": "docs-address", "area_uid": "area_us_dc", "subject_kind": "property",
      "address": { "line": "1477 Newton St NW", "unit": "402" } }
  ]
}
```

```json theme={null}
{
  "items": [
    {
      "client_id": "docs-ssl",
      "outcome": "resolved",
      "candidates": [{
        "entity_ref": "qprop_00002606cee96372585578d3ec315b5d6a1b1213ddd998e2734b3ad46e114987",
        "property_ref": "qprop_00002606cee96372585578d3ec315b5d6a1b1213ddd998e2734b3ad46e114987",
        "entity_kind": "tax_account",
        "status": "current",
        "area_uid": "area_us_dc",
        "match_basis": "identifier",
        "precision": null,
        "method": null,
        "canonical_identity": { "ssl": "0484--2250", "account_id": 203149, "identifiers": [ { "namespace": "dc_ssl", "raw_value": "04842250" } ] }
      }]
    },
    {
      "client_id": "docs-address",
      "outcome": "resolved",
      "candidates": [
        { "entity_ref": "qprop_fbc19d2e1d64c54e97709ddc024f20456d1f40471db40138f8ee0e94a5758fbb",
          "property_ref": "qprop_fbc19d2e1d64c54e97709ddc024f20456d1f40471db40138f8ee0e94a5758fbb",
          "entity_kind": "tax_account", "match_basis": "address_to_property",
          "precision": "contextual", "method": "contextual_parent_address" },
        { "entity_ref": "qentity_ead0a7baf2ae5925b5a0a09a22ba8c3c",
          "entity_kind": "parcel", "match_basis": "address_to_parcel",
          "precision": "contextual", "method": "contextual_parent_address" }
      ],
      "address_resolution": {
        "outcome": "resolved", "precision": "contextual", "method": "contextual_parent_address",
        "parcel_count": 1, "address_count": 1,
        "normalized_input": { "line": "1477 Newton St NW", "unit": "402", "normalized_line": "1477 NEWTON ST NW" }
      }
    }
  ]
}
```

***

### get\_entity

`GET /api/v2/entities/{entity_ref}` · \$0.05

Reads one canonical entity of any kind. Unknown references return HTTP 404 `not_found`.

**Response** `data.entity`

| Field                                                              | Meaning                                                                                                              |
| ------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------- |
| `entity_ref`, `entity_kind`, `area_uid`, `generation_id`, `status` | Identity and lifecycle (`current`, `retired`, `superseded`)                                                          |
| `identifiers[]`                                                    | Official identifiers with `authority_uid`, `namespace`, `raw_value`, `normalized_value`, validity dates and `status` |
| `attributes`                                                       | Kind-specific attributes; addresses and units carry a structured `address` object                                    |
| `source_refs[]`                                                    | Canonical evidence references for the identifiers                                                                    |
| `coverage_status`                                                  | `available` or `retired`                                                                                             |

```bash theme={null}
curl https://portal.quoindata.com/api/v2/entities/qentity_0000181b67f859019efe1b488b88b1f0 \
  -H "Authorization: Bearer $QUOIN_API_KEY"
```

```json theme={null}
{
  "entity": {
    "entity_ref": "qentity_0000181b67f859019efe1b488b88b1f0",
    "entity_kind": "unit",
    "area_uid": "area_us_dc",
    "generation_id": "7",
    "status": "current",
    "identifiers": [{ "authority_uid": "auth_dc_mar", "namespace": "mar", "raw_value": "158900", "status": "current" }],
    "attributes": {
      "address": { "address_line": "1477 NEWTON STREET NW", "address_normalized": "1477 NEWTON ST NW",
                   "unit": "402", "locality": "WASHINGTON", "region": "DC", "address_kind": "unit" }
    },
    "source_refs": ["qev1.i.1.193.0e2a54ed…"],
    "coverage_status": "available"
  }
}
```

***

### traverse\_relationships

`GET /api/v2/entities/{entity_ref}/relationships` · \$0.05 per page

Walks sourced relationships from a starting entity.

| Query parameter | Default | Range                                                    |
| --------------- | ------- | -------------------------------------------------------- |
| `direction`     | `out`   | `out`, `in`, `both`                                      |
| `predicates`    | all     | Up to 50 relationship kinds, comma-separated or repeated |
| `max_depth`     | 1       | 1–3                                                      |
| `max_nodes`     | 200     | 1–200                                                    |
| `max_edges`     | 200     | 1–200                                                    |
| `limit`         | 20      | 1–100 nodes and edges per page                           |
| `cursor`        |         | From `meta.next_cursor`                                  |

Relationship kinds in the current publication and their direction:

| Predicate            | Subject → Object         | Count   |
| -------------------- | ------------------------ | ------- |
| `address_for_parcel` | address or unit → parcel | 432,567 |
| `account_for_parcel` | tax account → parcel     | 191,775 |

Because edges point toward parcels, use `direction=out` from an address, unit or tax account and `direction=in` (or `both`) from a parcel. About 12% of active tax accounts have no accepted parcel link in the current publication; traversing from them returns the root node and no edges.

**Response** `data`

| Field                    | Meaning                                                                                                                                                    |
| ------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `root_ref`, `depth`      | Echo of the request                                                                                                                                        |
| `nodes[]`                | `entity_ref`, `entity_kind`, `status`, `depth`, `canonical_identity`                                                                                       |
| `edges[]`                | `relationship_ref`, `subject_ref`, `predicate`, `object_ref`, `direction`, `precision`, `method`, `confidence`, `status`, `generation_id`, `source_refs[]` |
| `max_nodes`, `max_edges` | Bounds applied                                                                                                                                             |

```bash theme={null}
curl "https://portal.quoindata.com/api/v2/entities/qentity_0000181b67f859019efe1b488b88b1f0/relationships?direction=out&max_depth=2&limit=20" \
  -H "Authorization: Bearer $QUOIN_API_KEY"
```

```json theme={null}
{
  "root_ref": "qentity_0000181b67f859019efe1b488b88b1f0",
  "depth": 2,
  "nodes": [
    { "entity_ref": "qentity_0000181b67f859019efe1b488b88b1f0", "entity_kind": "unit", "depth": 0, "status": "current" },
    { "entity_ref": "qentity_ead0a7baf2ae5925b5a0a09a22ba8c3c", "entity_kind": "parcel", "depth": 1, "status": "current" }
  ],
  "edges": [{
    "relationship_ref": "qrel_88505784b8b3069d1e5e6eea1844035ce820cbfa7951a9dcdc43e0d4160345e8",
    "subject_ref": "qentity_0000181b67f859019efe1b488b88b1f0",
    "predicate": "address_for_parcel",
    "object_ref": "qentity_ead0a7baf2ae5925b5a0a09a22ba8c3c",
    "direction": "out",
    "precision": "contextual",
    "method": "contextual_parent_address",
    "confidence": 1,
    "status": "current",
    "generation_id": "7",
    "source_refs": ["qev1.r.d60e1625-…"]
  }],
  "max_nodes": 200,
  "max_edges": 200
}
```

***

### search\_properties

`POST /api/v2/property-searches` · \$0.05 per page

Bounded, deterministic search over entities of one kind in one area.

**Request body**

| Field             | Type                       | Notes                                                                                                             |
| ----------------- | -------------------------- | ----------------------------------------------------------------------------------------------------------------- |
| `area_uid`        | required                   | `area_us_dc`                                                                                                      |
| `subject_kind`    | required                   | `property`, `tax_account`, `parcel`, `address`, `unit`, `building`, `land_interest`                               |
| `area_relation`   | default `located_in`       | `located_in`, `taxed_in`, `assessed_in`, `served_by`, `permitted_by`                                              |
| `filters[]`       | up to 100                  | `{ field, value }` with `field` in `entity_ref`, `property_ref`, `address`                                        |
| `sort`            | required                   | `{ field, direction }`; `field` in `entity_ref`, `property_ref`, `address`; `direction` `asc` (default) or `desc` |
| `projection`      | default `identity_summary` | `fact_summary` is reserved and returns HTTP 422 `unavailable`                                                     |
| `limit`, `cursor` |                            | Pagination                                                                                                        |

Filter semantics:

* `entity_ref` / `property_ref`: exact match. Two different reference filters in one request match nothing.
* `address`: case-insensitive **prefix** on the normalized address. Punctuation, extra whitespace, street suffixes (`Street` → `ST`) and quadrant words are normalized; `%`, `_` and `\` are literal. Matching rows return the canonical address in `canonical_identity.address`.
* Sorting by `address` requires an `address` filter; an unfiltered address sort returns HTTP 400 `invalid_request`.

Ordering is stable with `entity_ref` as the tie-breaker, so pages never duplicate or skip records.

**Response** `data`

| Field        | Meaning                                                                                                                    |
| ------------ | -------------------------------------------------------------------------------------------------------------------------- |
| `items[]`    | `entity_ref`, `entity_kind`, `status`, `area_uid`, `canonical_identity`, `subject_kind`, and `property_ref` when supported |
| `projection` | Echo                                                                                                                       |

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

```json theme={null}
{
  "items": [{
    "entity_ref": "qprop_fbc19d2e1d64c54e97709ddc024f20456d1f40471db40138f8ee0e94a5758fbb",
    "property_ref": "qprop_fbc19d2e1d64c54e97709ddc024f20456d1f40471db40138f8ee0e94a5758fbb",
    "entity_kind": "tax_account",
    "status": "current",
    "area_uid": "area_us_dc",
    "canonical_identity": { "ssl": "2681--0698", "address": "1477 NEWTON ST NW", "account_id": 23910 }
  }],
  "projection": "identity_summary"
}
```

Unfiltered listing (`"filters": []`, sort by `entity_ref`) is supported and is how you enumerate an area; use the cursor to continue.

***

### get\_property

`POST /api/v2/property-queries` · \$0.05 per call

Reads the record sections of up to five tax accounts.

**Request body**

| Field                               | Type                    | Notes                                                                                                                                                                                                               |
| ----------------------------------- | ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `property_refs[]`                   | 1–5 `qprop_` references | From resolve or search                                                                                                                                                                                              |
| `sections[]`                        | optional                | Default: all 13. Any of `identity`, `assessment`, `tax`, `delinquency_tax_sale`, `special_assessment`, `ownership`, `transfer`, `deed_reference`, `permit`, `license`, `inspection_enforcement`, `building`, `land` |
| `section_limit`                     | 1–100, default 20       | Maximum records per section                                                                                                                                                                                         |
| `fields`, `as_of`, `publication_id` | reserved                | Return HTTP 422 `unavailable`                                                                                                                                                                                       |
| `cursors.permit`                    | optional                | Continue permits for one property with `sections: ["permit"]`                                                                                                                                                       |

**Response** `data.items[]`

| Field                                     | Meaning                                                                                                                                                                                                                                                                                                   |
| ----------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `property_ref`, `subject_kind`, `outcome` | `outcome` is `ok` or an explicit per-item failure                                                                                                                                                                                                                                                         |
| `sections.<name>`                         | Each section has `availability`, a records array named for the section (`assessments`, `sale_history`, `records`, …), `has_more` and `next_cursor` (permit continuation is supported; other sections remain bounded). Record sections also carry `status` and `limitations[]`; `identity` carries neither |

Source-backed values are `{ value, status, source_refs[] }` triples; `status` is `reported` when the value is taken directly from the source, and `source_refs` feed `get_evidence`. Record metadata such as `stage`, `tax_year` and `record_date`, and the whole `identity` section, are plain values.

**Permits support continuation.** When the permit section has `has_more: true`, send its `next_cursor` under `cursors.permit`, with one property, `sections: ["permit"]`, and the same `section_limit`. Other sections remain bounded. Responses retain the 256 KiB ceiling; shortened permit pages advance only over records actually returned.

```json theme={null}
{
  "property_refs": ["qprop_fbc19d2e1d64c54e97709ddc024f20456d1f40471db40138f8ee0e94a5758fbb"],
  "sections": ["identity", "assessment", "transfer"],
  "section_limit": 2
}
```

```json theme={null}
{
  "items": [{
    "property_ref": "qprop_fbc19d2e1d64c54e97709ddc024f20456d1f40471db40138f8ee0e94a5758fbb",
    "subject_kind": "tax_account",
    "outcome": "ok",
    "sections": {
      "identity": {
        "availability": "available",
        "records": [{ "ssl": "2681--0698", "account_id": 23910, "address": "1477 NEWTON ST NW WASHINGTON DC 20010" }],
        "has_more": false
      },
      "assessment": {
        "availability": "available", "status": "resolved", "has_more": true,
        "assessments": [{
          "stage": "current", "tax_year": 2026, "record_date": "2026-04-15",
          "land_value_dollars":        { "value": 1029900, "status": "reported", "source_refs": ["itspe_current|23911|assessment.current.land_value|26810698"] },
          "improvement_value_dollars": { "value": 1703810, "status": "reported", "source_refs": ["itspe_current|23911|assessment.current.improvement_value|26810698"] },
          "total_value_dollars":       { "value": 2733710, "status": "reported", "source_refs": ["itspe_current|23911|assessment.current.total_value|26810698"] }
        }],
        "limitations": ["Prior, current, and proposed are distinct source stages…"]
      },
      "transfer": {
        "availability": "available", "status": "resolved",
        "sale_history": [{
          "sale_date":          { "value": "2020-02-20", "status": "reported", "source_refs": ["cama_sales_current|476538993|sale.history.date|26810698"] },
          "sale_price_dollars": { "value": 0, "status": "reported", "source_refs": ["cama_sales_current|476538993|sale.history.price|26810698"] },
          "quality_flags": ["nominal_or_non_market_zero_price", "unqualified_sale"]
        }],
        "limitations": ["CAMA sale history is not a Recorder of Deeds chain of title, title report, or lien search."]
      }
    }
  }]
}
```

Assessment stages are `prior` (TY2025), `current` (TY2026) and `proposed` (TY2027), exactly as the Office of Tax and Revenue publishes them. The API does not interpolate or treat a proposed value as final.

***

### get\_evidence

`POST /api/v2/evidence-lookups` · \$0.05 per call

Expands source references into the official record they came from and a human verification route.

**Request body**

| Field                         | Type                                    |
| ----------------------------- | --------------------------------------- |
| `source_refs[]`               | 1–50 references taken from any response |
| `include_verification_routes` | boolean, default true                   |

**Response** `data`

| Field                   | Meaning                                                                                                                                                                                                                                                                                                                                                                                                      |
| ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `evidence[]`            | One item per reference. Legacy field references return `publisher`, `dataset_name`, `field_key`, `source_class`, `provenance` (dataset SHA-256, retrieval time, source record id, limitations) and `human_verification`. Canonical references (`qev1.…`) return `source_id`, `source_release_id`, `source_record_key`, `source_row_sha256`, the dated `claim`, and for relationships the full `relationship` |
| `verification_routes[]` | Portal name, link, ordered steps, exact lookup inputs, which `source_refs` and fields the route covers, and `property_link_scope` (`exact_property` or a contextual scope)                                                                                                                                                                                                                                   |
| `verification_policy`   | Always "Only official human-facing verification routes are returned."                                                                                                                                                                                                                                                                                                                                        |

A reference that cannot be resolved does not fail the call: the item carries `outcome: "invalid_reference"` or `"not_found"` and the envelope `status` is `partial`.

```json theme={null}
{ "source_refs": ["itspe_current|203150|assessment.current.total_value|04842250"] }
```

```json theme={null}
{
  "evidence": [{
    "source_ref": "itspe_current|203150|assessment.current.total_value|04842250",
    "field_key": "assessment.current.total_value",
    "publisher": "DC Office of Tax and Revenue",
    "dataset_name": "Integrated Tax System Property Extract",
    "source_class": "official_snapshot",
    "provenance": {
      "source_record_id": 203150,
      "source_sha256": "6feca10ca22887dbb5be66c6bb9d12c19625c48291a0f54db96fa95cb46de86e",
      "dataset_retrieved_at": "2026-09-02T03:07:09.795435+00:00",
      "source_limitations": "Record dates vary by row. Retrieval date is not the fact effective date."
    },
    "human_verification": {
      "portal_name": "MyTax.DC.gov Real Property Search",
      "portal_url": "https://mytax.dc.gov/_/#2",
      "search_inputs": { "ssl": "0484--2250", "property_address": "555 MASSACHUSETTS AVE NW UNIT P-4, Washington, DC" },
      "steps": ["Open the MyTax.DC.gov Real Property Search.", "Enter the supplied property address or SSL and select Search.", "…"]
    }
  }],
  "verification_routes": [{
    "title": "MyTax.DC.gov Real Property Search",
    "link": "https://mytax.dc.gov/_/?Link=PropertySearch",
    "covers": ["Current assessment"],
    "covered_fields": ["assessment.current.total_value"],
    "property_link_scope": "exact_property",
    "lookup": { "ssl": "0484--2250" }
  }],
  "verification_policy": "Only official human-facing verification routes are returned."
}
```

The live portal may be newer than the dated extract the fact came from. Compare the cited record date, not only the value.

## Comparable sales and complete permits

Use `POST /api/v2/record-queries` (`query_properties`) to discover fields and search records. The existing `POST /api/v2/property-queries` continues to read property details.

### Discover the source vocabulary

Send `{"mode":"catalog"}` to record queries for supported fields, datasets, operators, and source releases. Send `{"mode":"values","dataset":"sales","field":"sale.qualified_code"}` to inspect qualification codes.

Rows represent individual assessor sale events. Keep `record_ref` to distinguish repeated sales of the same property. `Q` is the source qualification code; it does not independently establish an arm's-length transaction. A shared transaction price may appear against multiple property interests.

### Find nearby sales

```bash theme={null}
curl https://portal.quoindata.com/api/v2/record-queries \
  -H "Authorization: Bearer $QUOIN_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $QUOIN_REQUEST_KEY" \
  --data '{"mode":"rows","dataset":"sales","limit":20,"location":{"latitude":38.93,"longitude":-77.03,"radius_miles":0.5},"filters":{"sale":[{"field":"sale_date","op":"gte","value":"2025-01-01"},{"field":"qualified_code","op":"eq","value":"Q"}]}}'
```

Set a unique `QUOIN_REQUEST_KEY` before the first attempt. Keep that same key and body if the connection fails. For the next page, send `meta.next_cursor` as `cursor` with the same filters and a new request key.

The query supports `catalog`, `values`, `rows`, and `summary` modes; datasets are `properties`, `sales`, `tax_periods`, and `buildings`. Pages contain at most 50 rows. The query payload is also capped at 60,000 bytes; lower `limit` if it returns `response_too_large`. Filters in the same sale scope match the same event. Radius boundaries are inclusive; distance uses the returned WGS84 point and a spherical great-circle calculation.

`location` contains `latitude`, `longitude`, `point_type`, and `source_refs`, or is null. Currently, points are consistent DOB permit locations linked to the property by exact SSL. They are not parcel centroids or surveyed building locations. Conflicting or absent points remain null and are excluded from radius searches. This limits geographic coverage.

### Read characteristics

Send the returned `property_ref` to property queries with `sections: ["building", "land"]`. CAMA records in `sections.building.records` carry the following facts and evidence:

| Fact                              | Source field                             |
| --------------------------------- | ---------------------------------------- |
| `bedrooms`                        | `BEDRM`                                  |
| `condition`                       | `CNDTN_D`                                |
| `attachment_type`                 | `STRUCT_D`                               |
| `heat_type`                       | `HEAT_D`                                 |
| `air_conditioning_type`           | `AC`, preserving the source code         |
| `roof_type`                       | `ROOF_D`                                 |
| `exterior_wall`                   | `EXTWALL_D`                              |
| `interior_finish`                 | `INTWALL_D`, preserving the source label |
| `kitchens`, `fireplaces`, `rooms` | `KITCHENS`, `FIREPLACES`, `ROOMS`        |
| `effective_year_built`            | `EYB`; distinct from actual year built   |

Read lot area from `sections.land.land_area_square_feet` (ITSPE `LANDAREA`); query results also expose `property.land_area_sqft`. Null or an absent fact means unavailable, while zero remains a reported value.

`gross_building_area_square_feet` uses `GBA`, falling back to `LIVING_GBA` when GBA is absent. `living_area_square_feet` uses `LIVING_GBA`. Neither should be treated as verified finished living space without confirming the source definition and the property. Historical `as_of` remains unavailable. Permit issuance does not establish completion, and missing cost or completion fields remain unavailable.

### Retrieve every permit

Read one property with `sections: ["permit"]`. While `sections.permit.has_more` is true, send its `next_cursor` under `cursors.permit`, keeping the same `section_limit`. Continuation requires exactly one property and only the permit section. A new page is a new billable request; retrying that page with its original key is free.

```javascript theme={null}
const base = "https://portal.quoindata.com/api/v2";
async function post(path, body) {
  const key = crypto.randomUUID(); // One key per logical page.
  for (let attempt = 0; attempt < 3; attempt++) {
    let delay = 1000 * (attempt + 1);
    try {
      const response = await fetch(`${base}/${path}`, {
        method: "POST",
        headers: {
          Authorization: `Bearer ${process.env.QUOIN_API_KEY}`,
          "Content-Type": "application/json",
          "Idempotency-Key": key,
        },
        body: JSON.stringify(body),
        signal: AbortSignal.timeout(15000),
      });
      const result = await response.json();
      if (response.ok) return result;
      if (!result.error?.retryable) throw Object.assign(new Error(result.error?.message), { permanent: true });
      delay = Math.max(delay, (result.error.retry_after_seconds ?? 0) * 1000);
    } catch (error) {
      if (error.permanent || attempt === 2) throw Object.assign(error, { requestKey: key });
    }
    await new Promise(resolve => setTimeout(resolve, delay));
  }
  throw Object.assign(new Error("Request did not complete"), { requestKey: key });
}
let cursor;
do {
  const result = await post("property-queries", {
    property_refs: [process.env.QUOIN_PROPERTY_REF],
    sections: ["permit"],
    section_limit: 20,
    ...(cursor ? { cursors: { permit: cursor } } : {}),
  });
  const item = result.data.items[0];
  if (item.outcome !== "ok") throw new Error(item.outcome);
  const page = item.sections.permit;
  for (const record of page.records) console.log(JSON.stringify(record));
  cursor = page.has_more ? page.next_cursor : null;
  if (page.has_more && !cursor) throw new Error("Missing continuation cursor");
} while (cursor);
```

Permit pages retain the 256 KiB ceiling. Cursors advance only over returned records, including pages shortened by the byte limit. Altered, expired, or publication-mismatched cursors produce explicit errors; restart the read. Other property sections remain bounded without continuation.
