---
title: "Data coverage / freshness introspection"
method: POST
path: "/v1/coverage"
tags: ["Data & Analytics"]
---

# Data coverage / freshness introspection

`POST /v1/coverage`

## Request

- Base URL: `https://api.rateapi.dev`
- URL: `https://api.rateapi.dev/v1/coverage`
- Auth: API key in header `X-API-Key`

Introspect what data the API covers BEFORE scanning: which states, lenders, and product types are present, row counts per segment, APR ranges, and last-updated freshness per segment. Extends the global `/meta` counts into a per-segment coverage matrix grouped by state, product type, or both. This route is the single source of truth; the MCP `get_coverage` tool calls it.

**Authentication required**: Pass your API key in the `X-API-Key` header.

## Request body

- object
  - `group_by` 'state' | 'product' | 'state_product': Segmentation axis for the matrix. Default 'state'.
  - `product_category` 'mortgage' | 'auto' | 'heloc' | 'personal' | 'student' | 'credit_card': Optional. Restrict the matrix to one product category. Omit for all.
  - `state` string: Optional 2-letter US state code to scope the matrix to a single state.
  - `freshness_days` integer: Staleness window in days (clamped 1..90). Omit to use per-category freshness windows (recommended); supplying a value applies it uniformly to every category and is reported back as freshness_mode=override.

## Response `200`

Coverage matrix with per-segment counts and freshness.

- object
  - `generated_at` string, nullable: MAX(scraped_at) overall, or null if empty.
  - `freshness_days` integer: The window supplied by the caller, or the fallback default when per-category windows applied.
  - `freshness_mode` 'per_category' | 'override': Which window applied: 'per_category' (each category used its own window; freshness_days is only the fallback) or 'override' (freshness_days applied uniformly).
  - `totals` object: Totals across the whole served book, before segmentation.
    - `rows` integer: Served rate rows.
    - `credit_unions` integer: Distinct credit unions with at least one served row.
    - `states` integer: Distinct US states represented.
    - `product_types` integer: Distinct product types represented.
  - `segments` object[]: One row per requested grouping, with its own freshness verdict.
    - `key` string: Stable key for the segment, joining the grouping values with `|`, or `(all)` when nothing was grouped on.
    - `label` string: Human-readable name for the segment.
    - `state` string, nullable: Two-letter US state code.
    - `product_type` string, nullable: Loan or deposit product this applies to.
    - `product_category` string, nullable: Broad category the product classifies into.
    - `rows` integer: Served rate rows in this segment.
    - `credit_unions` integer: Distinct credit unions contributing to this segment.
    - `apr` object: Annual percentage rate, which includes fees and is the figure offers are ranked on.
      - `min` number, nullable: Lowest APR in this segment, or null when it has no rows.
      - `max` number, nullable: Highest APR in this segment, or null when it has no rows.
    - `last_updated` string, nullable: When this segment was last refreshed, or null when it has no rows.
    - `window_days` integer: The freshness window in days this segment was judged against (its category's window, or the caller's override).
    - `is_fresh` boolean: Whether `last_updated` falls inside `window_days`. Grouping by state can span product categories with different windows, so the window used is the widest any row in the segment could carry.
  - `coverage` object: What the served book spans overall.
    - `states` string[]: Every US state code with served rows.
    - `product_types` string[]: Every product type with served rows.
  - `institution_profiles` object: Aggregate coverage of the imported NCUA quarterly credit-union profile layer.
    - `source` string: The dataset the institution profiles came from.
    - `cycle_date` string, date, nullable: Quarter-end date of the call report cycle the profiles describe, or null when none is loaded.
    - `profiles` integer: Institution profiles held.
    - `linked_credit_unions` integer: Profiles successfully matched to a credit union in the rate book.
    - `websites` integer: Profiles carrying a website.
    - `member_counts` integer: Profiles carrying a member count.
    - `branches` integer: Branch locations held across all profiles.
  - `next_actions` string[]: Other endpoints or tools that are the natural next step from this result.
  - `disclosures` string[]: Statements that must be shown to a consumer alongside this result.
  - `note` string: Plain-language explanation of what this result means.

## Other responses

- `400`: The request was malformed — a missing required field, an unparseable body, or a parameter outside its allowed range. The `error` field names what was wrong; retrying the identical request will fail identically.
- `401`: No API key, or a key that is unknown or disabled. Send it in the `X-API-Key` header. Free keys are issued instantly by POST /keys with an email address. The response carries `WWW-Authenticate` pointing at this host's protected-resource metadata, and the `RateLimit-*` headers describing the Free-tier budget a key issued now would carry (scope `anonymous`) — a rejected request spends none of it, so `Remaining` equals `Limit`.
- `429`: Quota exhausted for the current window. `Retry-After` gives the seconds to wait; `upgradeInfo` explains how to lift the cap. Retrying before the reset will fail again and still counts as a request.

---

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