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

# POST /v1/fetch/batch

> The same field fetch for up to 25 locations in one request.

## When to use this

You have a list — candidate properties, a portfolio, a county's worth of
addresses — and you want the same screen over all of them. This is
[`/v1/fetch`](/api-reference/fetch) applied to up to **25 locations** in one
request: one field selection, N locations, index-aligned results.

If you have one location, use `/v1/fetch`. If your list is bigger than 25,
page it — the bound exists so one request cannot stampede the \~84 upstream
sources every location fans out to.

## Request

```bash theme={null}
curl -s https://api.mireye.com/v1/fetch/batch \
  -H "Authorization: Bearer $MIREYE_API_TOKEN" \
  -H 'content-type: application/json' \
  -d '{
    "locations": [
      {"lat": 29.7604, "lng": -95.3698},
      {"address": "350 5th Ave, New York, NY 10118"},
      {"lat": 39.7392, "lng": -104.9903}
    ],
    "fields": ["elevation", "coast_distance_m"]
  }' | jq
```

Each entry in `locations` is the exact locator contract of `/v1/fetch`:
`lat`+`lng` **or** `address`, never both. The field selection (`fields` and/or
`preset`) is batch-wide by design — the batch exists for "the same screen over
a list", and per-location field lists would make the response shape
unpredictable for exactly the clients (agents, spreadsheets) it serves.

## Response

```json theme={null}
{
  "fetched_at": "2026-07-27T09:00:00+00:00",
  "results": [
    {"index": 0, "ok": true, "lat": 29.7604, "lng": -95.3698,
     "fetched_at": "…", "fields": {"…": "…"}, "partial_failures": [],
     "resolved_location": {"lat": 29.7604, "lng": -95.3698, "source": "coordinate"}},
    {"index": 1, "ok": true, "lat": 40.748377, "lng": -73.984854,
     "fetched_at": "…", "fields": {"…": "…"}, "partial_failures": [],
     "geocode": {"accuracy_type": "rooftop", "…": "…"},
     "resolved_location": {"lat": 40.748377, "lng": -73.984854, "source": "address"}},
    {"index": 2, "ok": false,
     "error": {"error": "coord_out_of_bounds", "message": "…", "retryable": false}}
  ]
}
```

Three properties to build on:

1. **Results are index-aligned with the request.** `results[i]` answers
   `locations[i]`, always, and carries `index` explicitly so a filtered or
   logged entry stays attributable.
2. **Each `ok: true` entry is a `/v1/fetch` response body.** Same `fields`
   shape, same tri-state field `status`, same `partial_failures`, same
   `geocode` echo when the location was an address. A client that parses the
   single endpoint parses the batch with no new code.
3. **A location's failure is an entry, never an HTTP failure.** Location #7's
   bad address cannot cost you the other 24 results: it becomes
   `{"ok": false, "error": {…}}` with the same error object (`error`,
   `message`, `retryable`) the single endpoint would have returned for that
   location — one error-handling code path for both endpoints.

## Two levels of partial failure

Don't conflate them:

* **Entry-level** (`ok: false`): the location itself failed — the address
  didn't geocode, the coordinate is outside the US envelope, or the worker
  shed the location at capacity (`fetch_busy`, retryable).
* **Field-level** (`partial_failures` inside an `ok: true` entry): the
  location was fine, but individual fields failed upstream — identical
  semantics to `/v1/fetch`.

A batch-level HTTP error is reserved for the caller's own mistakes, which are
identical for every location: unknown field names (`400 fields_unknown`), no
fields (`400 no_fields_requested`), too many explicit fields
(`400 fields_too_many`), an over-long list (`422`), or a malformed locator
(`422`).

## Limits

* **25 locations maximum** per request (`422` above it).
* **The `/v1/fetch` field rules apply unchanged**: 50 explicit fields max,
  presets exempt.
* **Locations are processed 4 at a time** inside the batch, and every location
  counts against the same per-worker admission gate as a single `/v1/fetch` —
  a batch is 25 requests' worth of work and is metered as such, not smuggled
  under one admission slot. A location shed at capacity returns as an
  `ok: false` `fetch_busy` entry (retryable) rather than failing the batch.
* **Worst-case latency ≈ 90 s** (every location cold, every source at its
  budget). Typical is far lower, but set your client timeout to **120 s+**,
  same guidance as `/v1/ask`.
* **Addresses bill one geocode lookup each** (cache-served repeats are free,
  30-day TTL), and metered sources (e.g. parcel lookups) bill **per
  location** — a 25-location batch with a parcel field is 25 metered calls.
* Batch is **REST-only** for now: the MCP `mireye_fetch` tool stays
  single-location (agents iterate naturally), and MCP hosts impose their own
  tool-response size limits a 25-location payload would fight.


## OpenAPI

````yaml POST /v1/fetch/batch
openapi: 3.1.0
info:
  title: Mireye Earth
  description: >-
    Provenance-tagged geospatial data for US coordinates. POST /v1/fetch for
    deterministic field values (POST /v1/fetch/batch for up to 25 locations at
    once); POST /v1/ask for natural-language Q&A; GET /v1/meta/fields for the
    catalog.
  version: 0.14.0
servers: []
security: []
paths:
  /v1/fetch/batch:
    post:
      summary: Fetch fields for up to 25 locations in one request
      description: >-
        The batch form of /v1/fetch: one field selection applied to a list of
        locations (each lat+lng or address). Results are index-aligned with the
        request and each is the same shape as a /v1/fetch response body; a
        location that fails resolves to an ok:false entry with the same error
        object the single endpoint would have returned, never failing the batch.
      operationId: fetch_batch_v1_fetch_batch_post
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/BatchFetchRequest'
        required: true
      responses:
        '200':
          description: Successful Response
          content:
            application/json:
              schema: {}
        '422':
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HTTPValidationError'
components:
  schemas:
    BatchFetchRequest:
      properties:
        fields:
          anyOf:
            - items:
                type: string
              type: array
            - type: 'null'
          title: Fields
        preset:
          anyOf:
            - type: string
              enum:
                - terrain
                - flood_risk
                - wildfire_underwrite
                - land_cover
                - site_selection
                - building_lookup
                - points_of_interest
                - utilities
                - boundaries
                - solar_siting
                - wind_siting
                - storage_siting
                - data_center_siting
                - grid_interconnect
                - natural_hazard
            - type: 'null'
          title: Preset
        locations:
          items:
            $ref: '#/components/schemas/BatchFetchLocation'
          type: array
          maxItems: 25
          minItems: 1
          title: Locations
      type: object
      required:
        - locations
      title: BatchFetchRequest
      description: |-
        N locations, one field selection.

        The field selection is deliberately batch-wide, not per-location: the
        batch exists for "the same screen over a list of candidate properties",
        and per-location field lists would make the response shape unpredictable
        for exactly the clients (agents, spreadsheets) the batch serves.
    HTTPValidationError:
      properties:
        detail:
          items:
            $ref: '#/components/schemas/ValidationError'
          type: array
          title: Detail
      type: object
      title: HTTPValidationError
    BatchFetchLocation:
      properties:
        lat:
          anyOf:
            - type: number
            - type: 'null'
          title: Lat
        lng:
          anyOf:
            - type: number
            - type: 'null'
          title: Lng
        address:
          anyOf:
            - type: string
              maxLength: 256
              minLength: 1
            - type: 'null'
          title: Address
      type: object
      title: BatchFetchLocation
      description: One batch entry — the exact locator contract of /v1/fetch.
    ValidationError:
      properties:
        loc:
          items:
            anyOf:
              - type: string
              - type: integer
          type: array
          title: Location
        msg:
          type: string
          title: Message
        type:
          type: string
          title: Error Type
        input:
          title: Input
        ctx:
          type: object
          title: Context
      type: object
      required:
        - loc
        - msg
        - type
      title: ValidationError

````