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 cost reasoning for the ordering. When membership matters, start with POST /v1/reachable-offers so the candidate products carry documented membership and product evidence. Neither endpoint decides underwriting or approval.
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 belongs in the comparison
- Membership matching. A cheaper product should not be promoted when there is no proven path connecting the customer to the institution.
- 3. What is actually best
- Ranking by real cost over the scenario, with reasoning you can show a user — or defend to a regulator.
The endpoints
/v1/decisionsOne scenario in, ranked offers out, with the reasoning for the ordering.
/v1/decisions/batchMany scenarios in one request — the shape for scoring an existing book rather than a live user.
/v1/explain-rankingExpand one lending decision ordering in detail. Deposit matches use the ranking basis, membership path, and selected-product evidence returned by reachable offers.
/v1/simulate-decisionRun a scenario without it counting as a live decision — useful in tests and in what-if UI.
Request
curl -X POST "https://api.rateapi.dev/v1/decisions" \ -H "Authorization: Bearer $RATEAPI_KEY" \ -H "Content-Type: application/json" \ -d '{ "decision_type": "financing", "context": { "geo": { "state": "NC", "zip": "28202" } }, "product_request": { "product_type": "auto_loan", "amount": 35000, "term_months": 60 } }'{ "request_id": "...", "decision_type": "financing", "as_of": "2026-08-22T01:00:00Z", "actions": [ { "type": "...", "offers": [ { "rank": 1, "credit_union_id": "electel cooperative|raleigh|NC", "credit_union_name": "Electel Cooperative Credit Union", "state": "NC", "product_type": "auto_new", "product_name": "New Auto Loan", "rate": 3.65, "apr": 3.65, "monthly_payment": 639.06, "last_updated": "2026-08-21T09:05:55.973Z", "eligibility": { "status": "unknown", "eligibility_type": "multi", "confidence": 0.98, "unknown_reason": "charter_type_not_geo_matchable", "requirements_summary": "Membership via employers (A &N Electric +49 more) or the North Carolina or Virginia electric or telephone cooperatives" }, "true_cost": { "...": "..." } } ] } ], "disclosures": ["..."]}Eligibility-aware product routing
The pattern worth copying: use graph-first product routing when membership matters, and preserve its ranking basis and proof.
// When membership matters, use the graph-first product-routing endpoint.const matched = await rateapi("/v1/reachable-offers", { person: { home_zip: user.zip, employer: user.employer, willing_to_join_association: true, }, product: { product_type: "auto_loan", intent: "purchase", amount: 35_000, term_months: 60, vehicle_condition: "new", audience: "consumer", },}); // Already ordered by matched.ranking_basis, with membership and product proof.const promotedMatches = matched.reachable_now;const oneStepMatches = matched.reachable_after_action;/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
/v1/rates returns raw rate rows — you filter, you rank, you decide. /v1/decisions takes a lending scenario and returns an ordered answer with cost reasoning attached. Use /v1/reachable-offers when membership determines which institution-products should enter the comparison; it applies the eligibility graph before joining the exact product search.
By the cost the consumer actually bears over the scenario you describe, not by headline APR alone. APR is a comparison tool with real limits: it does not capture fees the same way across product types, and it says nothing about whether the consumer can access the product at all. The response carries the reasoning so you can show, log or override it.
No. No institution can pay to appear, to rank higher, or to exclude a competitor. This is a fixed constraint on the product, not a current policy — the ordering is a function of the scenario and the data, and nothing else. If a ranking API can be bought, its rankings are advertising, and the whole value of an explainable recommendation disappears.
Use POST /v1/reachable-offers when membership matters. It evaluates confirmed facts against the active eligibility graph, then joins graph-positive institutions to one exact product search while preserving membership and product evidence. Unresolved is not rejection, and published pricing is not approval.
For lending decisions, the response carries cost reasoning and /v1/explain-ranking expands that loan ordering. Reachable deposit results use their own ranking_basis, membership paths, selected-product evidence, and observation dates; the loan explanation endpoint does not narrate them.
Yes. POST /v1/decisions/batch evaluates multiple scenarios in one request — the usual shape for scoring a book of existing customers rather than answering one live user.