---
title: "Resolve Addresses Batch"
method: POST
path: "/api/v1/project/address/resolve-many"
tags: ["project"]
---

# Resolve Addresses Batch

`POST /api/v1/project/address/resolve-many`

Resolve a pasted handful of addresses in one batch for the paste review list.

One typed outcome per input row in input order (matched / ambiguous /
not_in_dataset / empty_input), county-bounded like the single resolve. Candidate
labels for every row are hydrated in a single read, then re-split per row. A read
failure surfaces as 503 rather than the raw error (which can carry a sandbox URI).

## Request body

- AddressResolveManyRequest — Batch resolve for paste-a-handful entry: a few free-text address rows, one per pasted line (blanks already dropped client-side but tolerated here). ``county_fips`` bounds every row to the soft-selected county, exactly like the single resolve. ``max_length`` is ``ADDRESS_ROW_CAP``, the same cap the inspect endpoint enforces on how many uploaded rows may ride to the client: an upload that reached the review list is exactly the list this endpoint is asked to resolve, so a lower cap here rejects work the server already handed out.
  - `queries` string[], required
  - `county_fips` string, nullable

## Response `200`

Successful Response

- AddressResolveManyResponse — Per-row outcomes for a pasted batch, one row per input in input order.
  - `rows` BatchResolutionRow[], required
    - `index` integer, required
    - `query` string, required
    - `kind` 'matched' | 'ambiguous' | 'not_in_dataset' | 'empty_input', required — Per-row bucket for a batch resolve, derived from a single row's ``ResolutionResult`` plus its raw input. Coarser than the single-address ``ResolutionOutcome``: it splits ``matched`` by CARDINALITY (one candidate vs. several) and distinguishes a blank input row, so a caller can route each row (seed / disambiguate / report-missing / flag-blank) without re-deriving. The accept/reject verdict on candidates still lives with the agent / human — this is a count, not a confidence threshold.
    - `candidates` AddressCandidate[], required
      - `source_table` 'parcel' | 'building' | 'place' | 'address' | 'school', required — The table a resolved feature lives in — what a caller pulls its data from, and the feature's kind. A text match in the ``address``, ``place`` or ``school`` table resolves (point-in-feature) to a ``parcel``/``building``; a ``parcel`` attribute match resolves to the parcel itself.
      - `feature_id` string, required
      - `label` string, required
      - `similarity` number, nullable
      - `matched_via` string, required
      - `county_fips` string, nullable
      - `request_match` 'confirmed' | 'partial' | 'contradicted' | 'not_checked' — Whether a candidate names the address that was asked for. The judgment a surface would otherwise try to recover from ``similarity``, which cannot carry it: a hit promoted from a street scores the street alone, so it reaches 1.0 while naming a different house, and the parcel row that names the right house scores the whole string and sinks as the user types more of it. ``partial`` separates "not finished typing" from "names another property" — the address comparison works on whole words, so a fragment reads as a different word and would otherwise be indistinguishable from a wrong address. It licenses showing a candidate, never calling it exact and never pre-selecting it.

## Other responses

- `422` — Validation Error

---

[API](https://skmtc.net/maia-analytics/apis/maia-api.md) · [All operations](https://skmtc.net/maia-analytics/apis/maia-api/llms.txt) · [OpenAPI document](https://skmtc-service-staging.skmtc.workers.dev/v1/apis/maia-analytics/maia-api/revisions/fff2cd2dfbac/schema)
