---
title: "Find best refinance offer per existing debt"
method: POST
path: "/v1/scan-savings"
tags: ["Data & Analytics"]
---

# Find best refinance offer per existing debt

`POST /v1/scan-savings`

## Request

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

For each debt in a borrower's stack, finds the best qualifying live market offer and quantifies monthly + lifetime refinance savings, flagging which debts are worth refinancing. Up to 25 debts per request. This route is the single source of truth; the MCP `scan_savings` tool calls it.

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

## Request body

- object
  - `state` string, required: 2-letter US state code
  - `credit_score` integer: Optional borrower credit score, used to qualify offers.
  - `debts` object[], required: 1-25 existing debts.
    - `product_type` 'auto_loan' | 'mortgage' | 'heloc' | 'personal_loan' | 'student_loan', required: Loan or deposit product this applies to.
    - `balance` number, required: Current balance in dollars.
    - `current_rate` number, required: Current rate (percent).
    - `term_months` integer: Optional remaining term in months.

## Response `200`

Refinance savings stack with per-debt best offers and totals.

- object
  - `state` string: Two-letter US state code.
  - `stack` object[]: One row per debt supplied, each judged against the best offer available for it.
    - `rank` integer: Position in the ranking, 1 being best.
    - `product_type` string: Loan or deposit product this applies to.
    - `current_rate` number: The rate the borrower pays on this debt today, as an annual percentage.
    - `balance` number: Outstanding balance on this debt, in US dollars.
    - `best_offer` object, nullable: The cheapest qualifying offer found for this debt, or null when none was found.
      - `lender` string: Name of the credit union offering this rate.
      - `apr` number: Annual percentage rate, which includes fees and is the figure offers are ranked on.
      - `rate` number: Note rate as an annual percentage, excluding fees.
      - `monthly_payment` number: Monthly principal-and-interest payment in US dollars.
    - `current_monthly_payment` number, nullable: What the borrower pays on this debt today, in US dollars, or null when it could not be computed.
    - `monthly_savings` number: Monthly payment reduction in US dollars.
    - `lifetime_savings` number: Total payment reduction over the full term, in US dollars.
    - `worth_it` boolean: True when refinancing this debt produces a material saving.
    - `reason` string: Why this debt is not worth refinancing: `no_qualifying_offer` when nothing beat it, `insufficient_savings` when something did but not by enough. Absent when `worth_it` is true.
  - `totals` object: What acting on every worthwhile row in the stack would be worth.
    - `total_monthly_savings` number: Combined monthly saving across the rows worth refinancing, in US dollars.
    - `total_lifetime_savings` number: Combined lifetime saving across the rows worth refinancing, in US dollars.
    - `debts_worth_refinancing` integer: How many rows in the stack are worth refinancing.

## 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)
