---
title: "Get Extraction Costs"
method: GET
path: "/api/v1/documents/extractions/costs"
tags: ["Document"]
---

# Get Extraction Costs

`GET /api/v1/documents/extractions/costs`

## Request

- Base URL: `https://api.gemina.co` (the document declares no server; this is the origin it was published from)
- URL: `https://api.gemina.co/api/v1/documents/extractions/costs`
- Auth: API key in header `X-API-Key`

Look up what a batch of extractions cost.

Lean by design: costs only — no signed document URLs, no extracted values.
Use this instead of the full extraction view when all you need is spend.

Pass one `ids` parameter per extraction, e.g.
`?ids=<uuid>&ids=<uuid>`.

Ids belonging to another API key, and ids that do not exist, are omitted
from the response rather than reported — the endpoint cannot be used to
discover whether an extraction id is real. Key the response by
`extractionId`; order is not guaranteed.

Each entry carries a `state`:
- `settled` — the charge is on record; the number is final
- `pending` — billing has not run yet; retry
- `not_charged` — billing finished with no charge; this never resolves

## Query parameters

- `ids` string[], required: Repeat the parameter once per id, e.g. ?ids=<uuid>&ids=<uuid>. Up to 100 per call.

## Response `200`

Successful Response

- ExtractionCostsOutDTO: Bulk cost response. ``createdAt`` stays null (an aggregate over many extractions has no single creation time); use ``servedAt`` as the as-of signal.
  - `servedAt` string, date-time
  - `servedAtTimestamp` number
  - `createdAt` string, date-time, nullable
  - `createdAtTimestamp` number, nullable
  - `status` 'pending' | 'in_process' | 'failed' | 'success' | 'partial' | 'empty', required
  - `meta` FoundationMetaDTO
    - `externalId` string, nullable
    - `correlationId` string, uuid, nullable
    - `userId` string, uuid, nullable
  - `data` ExtractionCostsDataOutDTO, required
    - `costs` ExtractionCostOutDTO[], required
      - `createdAt` string, date-time, nullable
      - `createdAtTimestamp` number, nullable
      - `extractionId` string, uuid, required
      - `creditsConsumed` string, nullable: Credits debited for this extraction. Null for enterprise customers, who are billed in contract dollars via costCents, and whenever state is not 'settled'.
      - `costCents` integer, nullable: Enterprise rate-card cost in cents. Null for credit-billed customers.
      - `meter` 'processing_credits' | 'filetag_credits': Discriminates which balance a credit transaction debits/credits. ``processing_credits`` covers both subscription and enterprise ledgers (distinguished by ``subscription_id IS NULL`` on enterprise rows). ``filetag_credits`` covers the per-user FileTag allowance stored in ``filetag_credit_balances``. Separate meters so reports and history views can filter cleanly without conflating the two accounting pools.
      - `costExplanation` string, nullable
      - `state` 'pending' | 'settled' | 'not_charged', required: Whether an extraction's cost is final, still coming, or never charged. Exists because a bare ``null`` cost is ambiguous across three genuinely different situations, and a client reconciling spend has to tell them apart: - ``pending`` -- the charge has not been written yet. Credits are deducted in the Celery post phase, which runs *after* the extraction result is available, so a freshly finished extraction legitimately has no cost on record. Retry. - ``settled`` -- a charge is on record. This is the authoritative number. - ``not_charged`` -- terminal: no charge exists and none is coming. Either the extraction FAILED (billing follows outcome, so a failure is never charged), or billing ran and did not charge -- usually a real billing failure, since ``post_summary_update_task`` swallows ``SubscriptionError`` to a log line. Never resolves on its own, so a client must not poll it. Two caveats on ``not_charged``, both from ``post_tasks_complete`` being a coarse proxy for "billing finished": 1. It records that ``post_summary_update_task`` *started*, not that it charged -- the flag is written as that task's first statement, well before the charge itself. A read landing in between sees ``not_charged`` for an extraction that is about to be billed. 2. It is per-correlation (per document), so on a multi-extraction document the first extraction to reach billing flips it for its siblings too. Both windows are short (tens of milliseconds), but they mean ``not_charged`` is advisory: alert on it only after a grace period, never treat a single reading as proof of an unbilled extraction.
  - `errors` object[]

## Other responses

- `422`: Request validation failed (`HTTPValidationError`), or the request was understood but cannot be applied — e.g. the extraction is under legal hold or did not complete successfully (`FoundationDTO`).

---

[API](https://skmtc.dev/gemina/apis/gemina-api.md) · [All operations](https://skmtc.dev/gemina/apis/gemina-api/llms.txt) · [OpenAPI document](https://skmtc.dev/gemina/apis/gemina-api/revisions/152fd3f6f6ec?raw)
