---
title: "Discount-points / rate-buydown optimizer"
method: POST
path: "/v1/buydown"
tags: ["Data & Analytics"]
---

# Discount-points / rate-buydown optimizer

`POST /v1/buydown`

## Request

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

Given a candidate loan and a menu of points / lender-credit tradeoffs (each a `points` spend and the resulting `rate`), returns per-option break-even months, lifetime interest, savings over the hold horizon, and the optimal point-spend. The points analog of refinance break-even. Pure math — the optimization logic lives here as the single source of truth; the MCP `optimize_buydown` tool and the consumer app call this route.

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

## Request body

- object
  - `loan_amount` number, required: Loan principal in dollars (>0)
  - `term_months` integer, required: Loan term in months
  - `base_rate` number, required: The no-points (par) interest rate, percent
  - `options` object[], required: Points / lender-credit tradeoff menu to evaluate (max 25).
    - `points` number, required: Discount points as % of loan (positive = points spent) or lender credit (negative). 1 = 1% of loan.
    - `rate` number, required: Resulting interest rate at that point spend, percent.
  - `hold_months` integer: Expected time-in-loan horizon for lifetime savings. Defaults to term_months.
  - `state` string: Optional 2-letter US state code (validated only if supplied; not load-bearing for the math).

## Response `200`

Buydown analysis with per-option break-even and the optimal point-spend. When no option produces positive net savings over the hold horizon, `optimal` is null, `recommendation` is `no_points`, and `reason` is set.

- object
  - `base_rate` string: The rate before any points are bought, formatted as a percentage string.
  - `base_monthly_payment` string: Monthly payment at the base rate, formatted as a US dollar string.
  - `loan_amount` string: Loan amount in US dollars.
  - `term_months` integer: Loan term in months.
  - `hold_months` integer: How long the borrower is assumed to hold the loan. Defaults to the full term.
  - `state` string: Two-letter US state code.
  - `options` object[]: Each point-spend considered, including lender credits, from most points bought to most credit taken.
    - `points` number: Discount points priced into the rate.
    - `point_cost` string: Upfront cost of the points at this option, formatted as a US dollar string. Negative for a lender credit.
    - `rate` string: Note rate as an annual percentage, excluding fees.
    - `monthly_payment` string: Monthly principal-and-interest payment in US dollars.
    - `monthly_savings` string: Monthly payment reduction in US dollars.
    - `break_even_months` integer, nullable: Months until accumulated savings cover the upfront cost.
    - `savings_over_hold` string: Net saving over the hold horizon after the point cost, formatted as a US dollar string.
    - `lifetime_interest` string: Total interest paid over the full term, in US dollars.
  - `optimal` object, nullable: The option with the highest net saving over the hold horizon, or null when no option beats paying par.
    - `points` number: Discount points priced into the rate.
    - `rate` string: Note rate as an annual percentage, excluding fees.
    - `point_cost` string: Upfront cost of the optimal option, formatted as a US dollar string.
    - `break_even_months` integer, nullable: Months until accumulated savings cover the upfront cost.
    - `savings_over_hold` string: Net saving of the optimal option over the hold horizon, formatted as a US dollar string.
    - `why` string: Why this option wins, in plain language.
  - `recommendation` 'buy_points' | 'take_credit' | 'no_points': What to do: buy points, take a lender credit, or pay par.
  - `reason` string: Why no option beat paying par. Present only when `optimal` is null.
  - `assumed_defaults` object: Values filled in where the caller supplied none, so the result is reproducible.
  - `next_actions` unknown[]: Other endpoints or tools that are the natural next step from this result.
    - unknown
  - `disclosures` string[]: Statements that must be shown to a consumer alongside this result.

## Other responses

- `400`: Invalid request
- `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)
