Two loan comparison calculator
Put two loan offers side by side: 250,000 at 6.5% over 30 years costs 1,580.17 a month, while 5.9% over 15 years costs 2,096.16 but saves 191,552.52 in interest. See which wins on payment and which on total cost. Free in your browser.
- Free
- Runs in your browser
- No sign-up
- Your data stays on your device
Run — free
Worked example
- Principal a
- 250,000
- Annual rate a
- 6.5
- Term years a
- 30
- Principal b
- 250,000
- Annual rate b
- 5.9
- Term years b
- 15
- Payment count a
- 360
- Payment a
- 1,580.17
- Total paid a
- 568,861.22
- Total interest a
- 318,861.22
Choosing between two loan offers is not the same as reading two APRs. One quote can look cheaper each month because it lasts longer; the other can look more expensive on paper while saving tens of thousands in lifetime interest. Borrowers, advisors, and product teams need both numbers on the same page before anyone signs. This two loan comparison calculator accepts two independent offers — each with its own principal, nominal annual rate as a percent, and term in years — prices each as a fixed-rate fully amortizing schedule, and returns the periodic payment, total paid, and total interest for both sides plus signed deltas and a winner on cash flow and a winner on lifetime interest.
What a two-loan comparison actually answers
A loan offer is a triple: how much you borrow, what annual rate you pay, and how long you have to repay it. Two offers can differ on any of those three axes, so a single headline — the monthly payment, or the APR — cannot rank them. Stretching a mortgage from fifteen years to thirty years usually drops the payment and raises lifetime interest, sometimes by more than the original principal.
Cutting the rate by half a point on the same term does the opposite on a smaller scale: payment and interest both fall. Mixing a cash-out refinance with a larger principal and a shorter term mixes both effects at once. This calculator isolates those effects by pricing each offer as a fixed-rate fully amortizing loan.
Each period, interest is computed on the remaining principal at the contractual periodic rate, and the rest of the payment reduces principal. The closed-form payment is the unique constant amount that drives the balance to zero after a known number of payments. Total interest is then the sum of every payment minus the amount originally borrowed.
Comparing those two pairs — payment A versus payment B, and interest A versus interest B — is the side-by-side that households use when two lenders email competing term sheets, that advisors put on a one-page quote, and that marketplace landing pages need next to two competing APRs. When the cheaper payment and the cheaper interest belong to different offers, the result is not a bug: it is the real trade-off between cash flow and the cost of capital.
What you can do with it
Fifteen-year versus thirty-year mortgage
Hold principal and rate fixed, change only the term, and show that the lower monthly payment on thirty years is paid for with much higher lifetime interest.
Competing lender term sheets
Drop two quotes — different rates, maybe different principals after points or cash-out — into one request and rank them on payment and on total interest.
Refinance versus stay-put
Treat the current note as offer A and the refinance as offer B to see whether a new rate and term actually save interest or only shuffle the monthly bill.
FAQ
How does the two loan comparison calculator work?
Each offer is priced as a fixed-rate fully amortizing loan. The calculator returns both periodic payments, both lifetime interests, signed deltas (offer B minus offer A), and which side is cheaper on payment and on interest.
Why can one offer win on payment and the other on interest?
A longer term usually lowers the periodic payment and raises total interest. The two winners can differ; that is the cash-flow versus cost-of-capital trade-off, not an error.
Can I compare biweekly or weekly payments?
Yes. Set the number of payments per year, such as 26 for biweekly or 52 for weekly; it applies to both offers so they stay on the same calendar. Each term must work out to a whole number of payments.
How should I read the differences?
They are offer B minus offer A. In the example, B's payment is 515.99 higher each month while its lifetime interest is 191,552.52 lower, so a negative interest difference means B saves money.
Can the two offers have different amounts?
Yes. Each offer has its own principal, rate and term, so you can compare a larger loan with a smaller one. The payment and interest of each are worked out separately before the differences are shown.
Related tools
For developers — API access
Everything on this page is available programmatically. This section is for teams who want to wire it into their own systems; everyone else can just use the tool above.
API endpoint
Prefer to automate it? One authenticated POST creates the task; the result comes back by webhook or a signed link. The same capability also runs here on the web, by email and from Telegram — and soon from our app too.
Call it from your stack
curl -X POST https://api.kit.forhosting.com/finance/loan-comparison \
-H "Authorization: Bearer $KIT_KEY" \
-H "Content-Type: application/json" \
-d '{"principal_a":250000,"annual_rate_a":6.5,"term_years_a":30,"principal_b":250000,"annual_rate_b":5.9,"term_years_b":15}'const res = await fetch("https://api.kit.forhosting.com/finance/loan-comparison", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.KIT_KEY}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
"principal_a": 250000,
"annual_rate_a": 6.5,
"term_years_a": 30,
"principal_b": 250000,
"annual_rate_b": 5.9,
"term_years_b": 15
})
});
const { task_id } = await res.json();import os, requests
res = requests.post(
"https://api.kit.forhosting.com/finance/loan-comparison",
headers={"Authorization": f"Bearer {os.environ['KIT_KEY']}"},
json={
"principal_a": 250000,
"annual_rate_a": 6.5,
"term_years_a": 30,
"principal_b": 250000,
"annual_rate_b": 5.9,
"term_years_b": 15
},
)
task_id = res.json()["task_id"]<?php
$res = file_get_contents("https://api.kit.forhosting.com/finance/loan-comparison", false, stream_context_create([
"http" => [
"method" => "POST",
"header" => "Authorization: Bearer " . getenv("KIT_KEY") . "\r\nContent-Type: application/json",
"content" => '{"principal_a":250000,"annual_rate_a":6.5,"term_years_a":30,"principal_b":250000,"annual_rate_b":5.9,"term_years_b":15}',
],
]));
$task = json_decode($res, true);body := bytes.NewBufferString(`{"principal_a":250000,"annual_rate_a":6.5,"term_years_a":30,"principal_b":250000,"annual_rate_b":5.9,"term_years_b":15}`)
req, _ := http.NewRequest("POST", "https://api.kit.forhosting.com/finance/loan-comparison", body)
req.Header.Set("Authorization", "Bearer "+os.Getenv("KIT_KEY"))
req.Header.Set("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)Example request
{
"principal_a": 250000,
"annual_rate_a": 6.5,
"term_years_a": 30,
"principal_b": 250000,
"annual_rate_b": 5.9,
"term_years_b": 15
}Example response
{
"task_id": "tsk_a1b2c3d4e5f6a1b2c3d4e5f6",
"type": "finance.loan_comparison",
"status": "queued",
"_links": {
"result": "/tasks/tsk_…/result"
}
}The API is asynchronous: the call returns a task_id immediately and the result arrives by webhook. Polling is capped at 1 req/s per task.
Pricing
Published price — no tokens, no invented credits. A failed task is never charged.
Errors
| HTTP | Code | Meaning |
|---|---|---|
401 | unauthorized | Missing or invalid API key. |
402 | insufficient_balance | Your balance doesn't cover the task price. |
404 | unknown_type | That task type doesn't exist. |
429 | rate_limited | Too many requests. Use the webhook instead of polling. |
Technical notes
Input contract, payment formula, and comparison fields
The same frequency applies to both offers so the comparison stays on one calendar. Optionally set decimals from zero to eight for currency-style rounding (default two). Send a JSON object with six required finite numbers: principal_a and principal_b greater than zero (the amounts borrowed), annual_rate_a and annual_rate_b as nominal percents greater than or equal to zero (six point five means six point five percent per year, not a decimal fraction), and term_years_a and term_years_b greater than zero. Optionally set payments_per_year to an integer from one to three hundred sixty-five (default twelve for monthly; twenty-six biweekly, fifty-two weekly, four quarterly, or one annual are common). For each offer the engine forms payment_count as term_years times payments_per_year and requires that product to be a whole number of payments; fractional schedules fail with invalid_input. The periodic rate r is annual_rate over one hundred divided by payments_per_year. When r is zero, each payment is principal divided by payment_count and total interest is zero. When r is positive, payment equals principal times r times (one plus r) to the n, divided by (one plus r) to the n minus one. total_paid is payment times n; total_interest is total_paid minus principal. The response echoes both triples, both payment counts, both payments, both totals paid, and both lifetime interests, then adds payment_delta and total_interest_delta as offer B minus offer A so a positive delta means B costs more. cheaper_by_payment and cheaper_by_interest are a, b, or tie after rounding. Missing fields, non-finite numbers, non-positive principals or terms, negative rates, and out-of-range integers fail with invalid_input and are not billed.
Where a two-loan comparison API earns its keep
Mortgage and auto-loan shopping pages need a live side-by-side next to two competing quotes so visitors see that a lower payment can hide a larger lifetime bill. Personal finance apps and advisors run the same principal through two terms, or two rates through the same term, and show which offer wins on cash flow versus which wins on interest. Refinance desks compare the current note to a new offer without asking the borrower to rebuild a spreadsheet. Classroom and exam tools teaching amortization can assert both payments and both lifetime interests against known textbook cases with stable rounded goldens. Marketplace originators pre-fill both sides from two term sheets and need a deterministic parse endpoint that matches spreadsheet amortizations without a banking core. Sweep one rate or one term from your own code to build a table of deltas; pin payment_a, payment_b, and both total_interest fields in tests; and keep requests small.
Technical questions
Which fields are required?
principal_a, annual_rate_a, term_years_a, principal_b, annual_rate_b, and term_years_b. payments_per_year defaults to 12 (shared by both offers); decimals defaults to 2.
Does annual_rate_a mean 0.065 or 6.5 for 6.5%?
Use 6.5 for a 6.5% nominal annual rate on either offer. The engine divides by 100 internally before applying the periodic rate.
What does the two loan comparison calculator cost?
The browser tool is free. API requests use prepaid KIT balance at $0.002 per successful call; invalid input is not billed.