Ranked financial product offers, with the reasoning attached.

POST /v1/decisions takes a consumer scenario — what they are borrowing for, how much, over what term, where they are — and returns ordered product offers with structured reasoning for the ordering. Pair it with the eligibility API and the ranking covers only products the consumer can actually get.

Explainable orderingNo paid placement, everBatch scoring availableFree tier: 20 requests/month

Recommendation is three problems, not one

Most teams discover this in the order below, usually after shipping the first one alone and finding it does not hold up.

1. What exists
Current, comparable product data across institutions. The part everyone starts with, and the part that quietly rots without a pipeline behind it.
2. What they can get
Eligibility. A cheaper product at an institution the consumer cannot join is not an offer, and ranking it first actively damages trust.
3. What is actually best
Ranking by real cost over the scenario, with reasoning you can show a user — or defend to a regulator.
Skipping step 2 is the most common and most expensive mistake. It is the difference between a comparison table and a recommendation: a table can list anything, a recommendation is a claim about this person.

The endpoints

POST/v1/decisions

One scenario in, ranked offers out, with the reasoning for the ordering.

POST/v1/decisions/batch

Many scenarios in one request — the shape for scoring an existing book rather than a live user.

POST/v1/explain-ranking

Expand one ordering in detail, for a “why this order?” surface or an audit log.

POST/v1/simulate-decision

Run a scenario without it counting as a live decision — useful in tests and in what-if UI.

Request

$POST /v1/decisions
curl -X POST "https://api.rateapi.dev/v1/decisions" \
-H "Authorization: Bearer $RATEAPI_KEY" \
-H "Content-Type: application/json" \
-d '{
"scenario": "auto_purchase",
"loan_amount": 35000,
"term_months": 60,
"state": "NC",
"vehicle_condition": "used"
}'

Eligibility-aware recommendation

The pattern worth copying: narrow to what the consumer can reach, then rank what is left.

JSEligibility → decisions
// The full shape: eligibility narrows the field, decisions ranks what is left.
const eligible = await rateapi("/v1/eligibility/search", {
home_zip: user.zip,
employer: user.employer,
willing_to_join_association: true,
});
const reachable = [...eligible.eligible, ...eligible.conditionally_eligible]
.map((institution) => institution.credit_union_id);
const ranked = await rateapi("/v1/decisions", {
scenario: "auto_purchase",
loan_amount: 35_000,
term_months: 60,
state: user.state,
});
// Show only what this person can actually get, cheapest real cost first.
const offers = ranked.actions[0].offers
.filter((offer) => reachable.includes(offer.credit_union_id));
Do not derive a market median from the cheapest offer. The top of a sorted list is the extreme of a distribution, not its centre. When you need a representative comparison point, use /v1/benchmarks, which publishes a median built for that purpose.

Ranking you can defend

Cost over the scenario
Ranked by what the consumer actually pays for the loan they described, not by the headline number that markets best.
Reasoning in the response
Every ordering carries structured reasoning. Render it, log it, or override it — but you always have it.
No paid placement
No institution can pay to appear, rank higher, or exclude a rival. A ranking that can be bought is advertising.
Deterministic
The same scenario against the same data produces the same order. Reproducibility is what makes an explanation worth anything.

Frequently asked