---
title: "Report drift between billed usage and the request log for one day"
method: GET
path: "/v1/usage/reconciliation"
tags: ["Usage"]
---

# Report drift between billed usage and the request log for one day

`GET /v1/usage/reconciliation`

## Request

- Base URL: `https://api.spicrawl.com`
- URL: `https://api.spicrawl.com/v1/usage/reconciliation`
- Auth: HTTP bearer

Compares billed usage for one UTC day with usage re-derived from the Postgres request log. Report only;
nothing is repaired (`repairable` is always false).

Pitfalls:
- Read `interpretation` before `drift`. In the current deployment the Postgres request log is usually
  empty (`request_log_rows: 0`), so every billed row shows as `absent_from_request_log`. That is not
  evidence of overbilling.
- Defaults to yesterday (UTC); today is still accumulating.

## Query parameters

- `day` string, date

## Response `200`

The drift report.

- UsageReconciliation
  - `day` string, date, required: UTC day reconciled.
  - `drift` UsageDriftItem[], required: Disagreements. Empty means no drift.
    - `project_id` string, required: Project the drift is in.
    - `metric` 'requests' | 'credits' | 'engine_ms' | 'bytes_egress' | 'bytes_ingress' | 'proxy_bytes_dc' | 'proxy_bytes_resi' | 'extractions' | 'ai_extractions' | 'batch_items' | 'requests_failed' | 'requests_timeout' | 'feature_actions' | 'feature_ai_extract' | 'feature_autoparse' | 'feature_extract' | 'feature_extract_preset' | 'feature_js_render' | 'feature_links' | 'feature_network_capture' | 'feature_pdf' | 'feature_proxy_country' | 'feature_proxy_custom' | 'feature_screenshot' | 'feature_session' | 'client_cli' | 'client_mcp' | 'client_sdk' | 'client_other', required: Usage metric name. `requests_failed`/`requests_timeout` count failed and timed-out requests, `feature_*` count requests that used a feature, `client_*` count requests by client surface (CLI, MCP, SDK, other). Billing metric name. `credits` is in micro-credits and is total credit spend, batch items included; `engine_ms` in milliseconds; byte metrics in bytes; the rest are counts. New metrics may appear as map keys in responses.
    - `stored_qty` integer, required: Billed quantity (usage_daily).
    - `observed_qty` integer, required: Quantity re-derived from the Postgres request log.
    - `delta` integer, required: Difference reported by the reconciliation function. Do not treat as a refund or charge without reading `interpretation`.
    - `action` 'missing_in_usage_daily' | 'absent_from_request_log' | 'undercounted' | 'overcounted', required: The reconciliation verdict. `absent_from_request_log` usually means the log expired, not overbilling.
  - `request_log_rows` integer, required: Rows the Postgres request log holds for the day. 0 means there was nothing to reconcile against.
  - `interpretation` string, required: What the drift can and cannot prove. Read this before acting on `drift`.
  - `repairable` false, required: Always false; the API never repairs billing data. Contact support to dispute.
  - `warnings` string[]: Extra caveats. Omitted when empty.

## Other responses

- `400`: The request is invalid. Not retryable; fix the request using `code` and `diagnostics.hint`.
- `401`: Missing, invalid, revoked or expired key. Not retryable with the same key.
- `403`: The key lacks the scope this route requires (`ERR::AUTH::INSUFFICIENT_SCOPE`), or the action is not permitted.
- `500`: Internal error. Retryable when `retryable` is true.

---

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