Skip to main content
POST
Find parcels matching all filters
Search returns the first matches found, not ranked “top” matches. Every condition is an AND requirement. Source routing is automatic. Use Search to shortlist parcels. Set enrich: true to return every Enrich field for each match in the same request.

Quick example

Find Arizona parcels between 100 and 1,000 acres and include their calculated area:
Send this JSON directly to POST /v1/search with Authorization: Bearer <token>. The MCP tool mireye_search accepts the same object inside req.

Request

Each filter is {field, op, value}. Operators are eq, ne, lt, lte, gt, gte, in (nonempty array), and between (exactly two inclusive, ascending bounds). Geometry additionally supports intersects and within.
  • Text and boolean fields support eq, ne, and in.
  • Numeric fields support every operator. Supply JSON numbers, not numeric strings.
  • Null is never a filter operand. A genuinely absent Fetch value fails every hard filter, including ne. Calculation failures produce partial status instead of false non-matches.
  • State is required inside filters: use state or state2 with eq for one two-letter US state code or in for a nonempty list of codes. The 50 states and DC are accepted. A negative ne predicate alone does not satisfy this requirement; a county or geometry filter does not replace it.
  • County and acreage are optional filters. State, county and acreage are not top-level properties. The old parcel_filters and fetch_filters properties are rejected.

Field names, types and units

Search accepts 537 filter names and 539 selectable output names in the current V1 catalog, including a spatial geometry filter and three geometry output formats. The complete field reference lists names, types, units and output-only fields. Internal-only fields and internal parcel IDs cannot be filters or selected shortlist outputs. Shortlist geometry is available only when explicitly requested in fields. The optional enrichment object follows the Enrich catalog and includes its geometry and identifier fields. Stored parcel scalar fields use their loaded names and types. Codes remain strings; struct is boolean while qoz is a string such as "No". Dates use YYYY-MM-DD; timestamps use UTC ISO 8601 with up to millisecond precision. No geographic inference occurs. Calculations use the existing Fetch producers, including their configured live sources and fallbacks. Missing local datasets or credentials do not become false no-matches: failed filter calculations make results partial. Every source retains its existing budget, and all work shares Search’s configurable 180-second deadline. Search runs Enrich producers only when enrich is true. Unknown or unavailable names and incorrectly typed values are rejected before candidate reads. Fetch whole-parcel measures, such as wetland_fraction_of_parcel, use the original candidate boundary. Enrich-only calculations, including parcel_constraint_zones, parcel_flood_sfha_fraction and owner_assemblage, cannot be Search filters or selected through fields; they are included in the optional enrichment.

Search inside a polygon

Use geometry in the same filters list. It accepts WGS84 GeoJSON Polygon or MultiPolygon objects; positions are [longitude, latitude]. Every condition is ANDed.
  • intersects: any shared area or boundary point qualifies.
  • within: the entire parcel must be covered by the area; shared boundaries are allowed.
  • Holes exclude their interiors, and all MultiPolygon parts are respected. Matching tests parcel boundaries, not centroids. Acreage still means the whole parcel’s area.
  • Geometry filters do not add geometry to the response. Select geometry (GeoJSON object), parcel_boundary_geojson (JSON string), or parcel_geometry_wkt (WKT string) in fields only when those outputs are wanted. Every format retains all parcel parts.
Illustrative area request:
Rings must be closed and valid; self-intersections and overlapping MultiPolygon parts are rejected with a correction hint. Only type and coordinates are accepted (no Feature wrapper, alternate CRS, or altitude). Split antimeridian-crossing rings at +/-180 into a MultiPolygon. The default geometry budget is 10,000 vertices across all area filters, and 1,000,000 square kilometres per area. These are configurable safeguards, not extra response fields. Oversized inputs return 422; areas are never silently repaired. Parcel attribute predicates and conservative bounding boxes narrow database candidates. Exact polygon checks run before Fetch qualification. Bounding-box false positives do not consume result slots, and Search continues through pages under the same 180-second budget. fema_flood_zone refers to the Fetch calculation; parcel_fema_flood_zone selects the stored parcel value. census_subdivision selects the raw Census subdivision column, while city continues to mean the situs-address city.

Additional information with fields

Search automatically returns scalar values used in filters. A geometry filter never automatically returns or echoes a polygon. fields adds information without adding a condition. For example, requesting nearest_transmission_line_distance_m does not require a parcel to be near a line. Add a filter when proximity is a requirement. Extra calculations run only for matching parcels. Filter values already calculated are reused. Repeated field names are returned once. Missing extra data does not remove a match: the value is null, and field_status distinguishes absent (valid no-data) from failed (unavailable or unfinished). A failed extra calculation makes the response partial. Extra calculations share the search budget; requesting more information can increase latency.

Full enrichment with enrich

Send a JSON boolean; omission and false preserve the normal shortlist response. null, strings such as "true", and numbers are rejected with 422. For example:
Each returned match gains an enrichment object with fetched_at, analysis_point, fields, and partial_failures. Its fields map contains every field in the current Enrich catalog, including fields outside presets, geometry and identifiers. Each field retains Enrich’s value, status, units, provenance, and interpretation_hints. Fields remain in one flat map, ordered by Enrich preset groups, followed by fields outside presets. lifecycle is available only in field discovery metadata. Only failed fields include error and retryable; those keys are omitted for ok and absent fields. The shortlist’s fields and field_status maps keep their existing meaning; enrichment never overwrites them. Only returned matches are enriched. The selected original parcel and Search’s interior analysis_point are reused, without geocoding or a second parcel lookup. Point-based Enrich values can therefore differ from a separate /v1/enrich call, which uses its own stored-centroid/input-fallback policy. Whole-parcel values use the original boundary. Enrichment does not change eligibility, ordering, or the requested result limit. Missing data remains absent; producer errors and unfinished work are failed and listed in partial_failures. Failed enrichment keeps the matched parcel, makes Search partial, and uses field_data_unavailable unless a timeout or other earlier failure explains completion. Inspect enrichment.fields[name].status as well as field_status. A zero-match response performs no enrichment. Search and enrichment share one 180-second default deadline on every deployment. Full enrichment can produce a large response and invoke more sources; start with a small limit. If the deadline expires, confirmed matches remain and unfinished Enrich fields carry explicit failures with retry information. Completed source results and independent field calculations remain available even when another calculation in the same producer is unfinished, along with direct parcel attributes and valid reused Fetch fields. The response’s completion_reason is timeout. Waiting for the worker’s enrichment slot consumes the same deadline; unavailable Fetch capacity produces failed fields and a partial response.

Response

The response contains matches, status, completion_reason, request_id, and next_cursor. When next_cursor is non-null, send it as cursor in another POST /v1/search request with the same filters, fields, and enrich value. Set limit to any value from 1 to 100 on each page. For example, omit it on page 1 to receive up to 10 parcels, then request limit: 100 with its cursor on page 2, and limit: 1 with the next cursor on page 3. Omitted enrich and false are equivalent, including for cursors created before enrichment was available. Changing between true and false requires restarting without the cursor; otherwise the request returns 422 search_invalid_request. Each page has its own time and work budget. The token expires after one hour; if it is invalid or expired, restart without cursor. A null next_cursor means no known continuation is available. A partial page can still include a cursor when more candidates are known; its failed fields or candidates remain incomplete. Pages follow stored parcel order and are not a ranked result set. Underlying parcel data may change between calls, so pagination is not a snapshot. Search requires a configured API token signing secret to issue continuation cursors; a deployment without it returns 503 search_unavailable before reading candidate parcels. Each match contains APN, address, state/county/city, acreage, source, an interior analysis_point, a fields map, and a parallel field_status map (ok, absent, failed). The fields map includes scalar filter values plus explicitly requested additional information. For county, matches[].fields.county is the five-digit FIPS code when any county filter uses a FIPS code, even if county is also requested in fields or another county-name filter is present. Otherwise it is the county name. Request geoid to always include the FIPS code separately; the match’s top-level county remains the county name. Processing counts and internal limits are not returned. Shortlist fields expose their status; optional enrichment also exposes its field-level failure metadata and notes. The number returned is matches.length. enrichment is omitted entirely when enrich is omitted or false; it is not an empty or null object. An abbreviated example of a successful shortlist response (illustrative values):
With enrich: true, the enriched fields already belong to the selected match. With enrich: false, call Enrich separately when you want to investigate a chosen parcel. Point samples describe that point, not the entire parcel, unless the field explicitly measures a parcel calculation.

Agent workflow

Treat matches as a shortlist. Check status and completion_reason before saying the search is exhaustive: limited and partial can leave qualifying parcels unseen. Check field_status for each requested value; a failed value is not evidence that the parcel fails a condition. When enrich: true, read the per-field provenance and failures in enrichment. Otherwise, when a result needs deeper review, send its address or analysis_point to Enrich, then confirm the parcel identity in Enrich’s parcel_match. Do not assume the two calls selected the same parcel merely because their coordinates are close.

Correcting an invalid request

Validation is deterministic. HTTP 422 responses contain detail.error, message, issues, retryable: false, docs_url and request_id. Each issue identifies the input path, describes the problem, and supplies a correction hint; typed semantic errors can also include expected. Raw input values are not echoed. Missing state selection, invalid state codes, an empty state in list, or state predicates using only ne return 422 search_invalid_request. Add a state or state2 filter using eq with a valid two-letter code, or in with a nonempty list of valid codes. County names and FIPS codes do not replace this filter. For example, an acreage string is rejected with “acreage requires nonnegative numbers in acres.” at the offending filter. Replace the string with a JSON number. A limit above the supported maximum returns search_limit_exceeded and states the valid range. For an unknown field, consult this page and the field catalog; do not repeatedly retry an unchanged request. The default docs_url points to the server’s interactive OpenAPI documentation; operators can set MIREYE_SEARCH_DOCS_URL to this published Search page. Bearer authentication and account rate limits also apply. The development tariff is 0. For transient 429/503/504 errors, respect Retry-After (3 seconds for Search’s own errors; account rate limits can specify another delay). Authentication errors require valid credentials.

Execution and coverage

The defaults are 1,000 parcel candidates per batch and 10 returned matches per page, with a 100-result maximum per page. The budget is 180 seconds for each page request, including enrichment, in both application and production configuration. Application defaults have no total candidate/evaluation caps; production retains 1,000 total candidates and 500 evaluated parcels. A work_limit or timeout response can stop before all loaded parcels are examined. Use a client timeout above the server budget, for example 210 seconds. Search stops early as soon as enough matches are found. If the HTTP client disconnects, Search cancels and drains its coroutine tasks promptly. An already-running blocking computation may finish later; its enrichment and Fetch capacity stays reserved until that work stops. A timeout response can return while that cleanup finishes, and subsequent enrichment waits within its own request budget. Retrying starts a new search; disconnected searches are not saved as background jobs. A worker restart may also interrupt an in-flight request; process shutdown remains bounded separately from the request deadline. Stored parcel predicates run first, followed by catalog-field qualification in bounded batches. Search keeps reading candidate pages as needed. A one-row sentinel establishes whether another page exists. Stable source keys prevent duplicate source records; distinct parcels sharing an APN remain distinct. Search reuses the selected parcel for calculations; it does not perform a new paid parcel point lookup or geocoding. Operational counts, source aggregates, request structure and timing are logged internally. Raw filter values and parcel responses are excluded. Rejected requests are included in demand analysis. Logging uses bounded summaries and the existing background telemetry queue. For local operators: MIREYE_SEARCH_DEADLINE_SECONDS configures the budget; MIREYE_SEARCH_CANDIDATE_BATCH_SIZE controls the internal candidate batch size; MIREYE_SEARCH_MAX_CANDIDATES=0 and MIREYE_SEARCH_MAX_EVALUATED=0 disable optional count caps; MIREYE_SEARCH_PARCEL_CONCURRENCY=8 bounds qualification calculations; MIREYE_SEARCH_ENRICH_CONCURRENCY=1 bounds full enrichment across requests in each worker; MIREYE_SEARCH_DOCS_URL can override the correction link. MIREYE_SEARCH_MAX_GEOMETRY_VERTICES=10000 bounds vertices across all area filters; MIREYE_SEARCH_MAX_GEOMETRY_AREA_KM2=1000000 bounds each area. The checked-in Fly configuration uses the same 180-second budget and retains MIREYE_SEARCH_MAX_CANDIDATES=1000 and MIREYE_SEARCH_MAX_EVALUATED=500. Enrichment and Fetch slots remain held until already-running blocking work completes, including after deadline expiry or cancellation. This prevents a subsequent parcel from overlapping cancelled native work without extending the request or shutdown deadline. Invalid or unset enrichment concurrency falls back to one.

Body

application/json
filters
SearchFilter · object[]
required

All conditions are required; source routing is automatic.

Minimum array length: 1
limit
integer
default:10

Parcels on this page, from 1 to 100; may change between cursor requests.

Required range: x >= 1
cursor
string | null
Required string length: 1 - 2048
fields
string[]

Additional information to return for matches; never used to filter parcels.

Minimum string length: 1
Pattern: ^[a-z][a-z0-9_]*$
enrich
boolean
default:false

Return every Enrich field with provenance for each matched parcel.

Response

Successful Response

status
enum<string>
required
Available options:
complete,
limited,
partial
matches
Matches · object[]
required
request_id
string
required
next_cursor
string | null
required
completion_reason
enum<string>
required
Available options:
exhausted,
limit_reached,
timeout,
source_unavailable,
work_limit,
evaluation_failed,
field_data_unavailable