> ## 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/quote

> What a fetch would cost, and whether your allowance covers it — before you run it.

## When to use this

Before a big run, and before an agent starts a loop.

Field prices are not uniform. Most fields cost 1 credit per location; the
seventeen fields backed by per-record-licensed county parcel data cost
**300 credits per location on free, build and growth** (150 on scale and
market) — charged once for the whole group, however many of its members you
request. **Five** of those seventeen do not start with `parcel_`, so you cannot
tell from a field name:

* `wetland_acres_on_parcel` and `wetland_fraction_of_parcel` clip wetland data
  to the parcel boundary, which means fetching the parcel.
* `developable_acres_proxy` and the two `onsite_solar_potential_mwac_*` fields
  are derived from parcel inputs, so requesting one fetches the record.

Quote the request and you do not have to know any of that.

## Request

```bash theme={null}
curl -s https://api.mireye.com/v1/fetch/quote \
  -H "Authorization: Bearer $MIREYE_API_TOKEN" \
  -H 'content-type: application/json' \
  -d '{
    "preset": "site_selection",
    "locations": 25
  }' | jq
```

Take the `fields` / `preset` from the request you are about to send and add
`locations` — 1 for `/v1/fetch`, up to 25 for `/v1/fetch/batch`, or however
many a run (`POST /v1/runs`) will carry.

**`locations` is a COUNT, not a list** — an integer, unlike the `locations`
field on `/v1/fetch/batch`, which is the array of locators. Same name, different
type, because this endpoint prices a shape rather than resolving places. Sending
the batch's list here gives `int_type: Input should be a valid integer`.

**No coordinates.** A fetch's price depends on which fields you ask for and how
many locations you send, never on *where* those locations are, so this endpoint
does not accept them. (An address-form fetch bills the fields only — we absorb
the geocode.)

## Response

```json theme={null}
{
  "fields": ["elevation", "coast_distance_m", "parcel_owner"],
  "field_count": 3,
  "preset": null,
  "locations": 25,
  "plan": "build",
  "credits_per_location": 302,
  "credits_total": 7550,
  "breakdown": {
    "per_field": {"fields": 2, "credits_each": 1, "credits": 2},
    "metered_groups": {
      "parcel_record": {
        "fields": ["parcel_owner"],
        "credits": 300,
        "billed": "once per location, however many of its fields you request"
      }
    }
  },
  "allowance": {
    "credits_included": 25000,
    "credits_used": 1200,
    "credits_remaining": 23800,
    "self_imposed_limit": null,
    "effective_limit": 25000,
    "resets_at": "2026-09-01T00:00:00+00:00",
    "would_exceed_allowance": false,
    "would_be_blocked": false
  },
  "notes": [
    "1 of the 3 fields ride the metered parcel record (parcel_owner), which adds 300 credits per location on your plan — regardless of how many of them you request.",
    "This request would take you past your included 25000 credits."
  ]
}
```

`would_exceed_allowance` and `would_be_blocked` answer different questions and
you should read both. Every plan stops at its included allowance: a request
that would land past it is refused with `402 credits_exhausted`, on any tier.
(Overage is not billed — it is simply not served; your allowance resets on the
1st of each month, and upgrading raises it immediately.)

The `402` carries a **`Retry-After` header as an HTTP-date**, naming the same
instant as `resets_at` in the body — so a standard backoff layer has something
to read without parsing the body. It is a date rather than delta-seconds on
purpose: a reset is often weeks out, and a seven-figure second count is the kind
of value client libraries clamp into a wrong retry time. Note the header tells
you when the *allowance* returns; if you upgrade, you can retry immediately.

Selections containing a parcel-group field are additionally bounded by a
monthly pool shared across all customers. `effective_limit` already accounts
for everything, and `limited_by` tells you which ceiling is in force
(`plan`, `self`, or `none`).

For a batch, `would_be_blocked` does **not** mean the call fails. A batch is
billed per location, so one that straddles your ceiling returns `200`:
`locations_affordable` run and are charged, and the rest come back as
per-location `credits_exhausted` entries.

## Pricing and billing

* **Quotes are free.** They are authenticated (the answer depends on your plan
  and your month-to-date usage) but unmetered — an account that has run out
  still needs to be able to find out why. They do count against your
  per-minute rate limit.
* **The quote is computed by the same code that charges you**, so it cannot
  drift from the bill. If a real fetch costs something other than the quoted
  `credits_per_location`, that is a bug — report it.
* **The quote prices the request shape**, which is the only honest pre-work
  price. Fields that come back `status: "failed"` are refunded after the fact,
  so a real run can cost *less* than quoted, never more.

## Setting your own limit

Every plan already stops at its included allowance. A self-limit lets you stop
EARLIER — a spend ceiling below your plan's, for a project budget or a safety
margin:

```bash theme={null}
curl -s -X PATCH https://api.mireye.com/v1/users/me/settings \
  -H "Authorization: Bearer $MIREYE_FIREBASE_ID_TOKEN" \
  -H 'content-type: application/json' \
  -d '{"monthly_credit_limit": 50000}' | jq .settings
```

* It can only **tighten**. A limit above your plan's allowance does not let you
  exceed the allowance, and it can never widen the per-account bound we apply to
  shared vendor data (parcel records, drive-time matrices).
* Set `0` to stop all metered requests — a deliberate pause, not a mistake. It
  takes effect fleet-wide within seconds, not on a cache expiry.
* Set `null` to remove the limit.
* It applies to your whole account, across every metered endpoint, and resets
  with the billing period.
* Lowering it below what you have already used **blocks further calls**; it does
  not retroactively undo or refund anything.

Once past it, metered requests return `402 credits_exhausted` with
`self_imposed_limit` set to your number, so a client can tell "you configured
this" apart from "your plan ran out" — the remedies are different, and upgrading
does nothing for a self-imposed limit. `/v1/fetch/quote` predicts it too:
`allowance.effective_limit` is whichever ceiling is actually in force.

## Related

* [`GET /v1/meta/fields`](/api-reference/meta-fields) carries a `billing` block
  per field and a top-level price list, if you would rather price things
  yourself.
* [`GET /v1/meta/plans`](/api-reference/meta-fields) has the full credit price
  list for every endpoint.


## OpenAPI

````yaml POST /v1/fetch/quote
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.16.0
servers: []
security: []
paths:
  /v1/fetch/quote:
    post:
      summary: Price a fetch or batch before running it
      description: >-
        Returns what the same field selection would cost, and how that compares
        to what is left of your monthly allowance. Authenticated (the answer
        depends on your plan) but UNMETERED and does no fetching — an account
        that has run out must still be able to find out why. Computed by the
        same functions that charge, so the quote cannot drift from the bill.
      operationId: fetch_quote_v1_fetch_quote_post
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/FetchQuoteRequest'
        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:
    FetchQuoteRequest:
      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:
          type: integer
          maximum: 25
          minimum: 1
          title: Locations
          description: >-
            How many locations the real request would carry. 1 for /v1/fetch, up
            to the batch maximum for /v1/fetch/batch. A run (POST /v1/runs)
            prices the same way per location.
          default: 1
      type: object
      title: FetchQuoteRequest
      description: >-
        What a /v1/fetch or /v1/fetch/batch request WOULD cost.


        Subclasses ``_FieldSelection`` on purpose: a fetch's price is a function
        of

        the resolved field list and the number of locations, and of nothing
        else.

        Coordinates are therefore not accepted — not as an omission but as the

        honest model. Taking them would imply the price varies by WHERE, invite

        addresses into a payload that has no use for them, and tempt this
        endpoint

        into a metered geocode to price something geocoding does not affect (an

        address-form fetch bills the fields only; we absorb the geocode).
    HTTPValidationError:
      properties:
        detail:
          items:
            $ref: '#/components/schemas/ValidationError'
          type: array
          title: Detail
      type: object
      title: HTTPValidationError
    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

````