Loan Schedule API

Generate precise amortisation schedules with odd-day interest handling, 360/365-day basis, and API key authentication.

Authentication

All endpoints except GET /api/health require an API key when the API_KEYS environment variable is set. If API_KEYS is not set, auth is disabled (useful for local development).

Pass your key in the X-API-Key request header.
Query string ?api_key=... is also accepted as a fallback.
Generate a key (run once on your server)
python app.py --genkey

# Output:
Raw key  (share with clients): ls_a1b2c3d4e5f6...
Hash     (add to API_KEYS env): 9f8e7d6c5b4a...
Configure on Digital Ocean App Platform
# App Platform → Settings → Environment Variables
API_KEYS = 9f8e7d6c5b4a...

# Multiple keys: comma-separated hashes
API_KEYS = 9f8e7d6c...,ab12cd34...,ef56gh78...
HTTP status codes
CodeMeaning
401No key provided
403Key invalid
200Authenticated and successful
POST /api/schedule Requires API key
Request Fields
FieldTypeRequiredDescription
loan_amountnumberrequiredPrincipal loan amount
base_aprnumberrequiredAPR as a percentage (e.g. 600 for 600%)
num_paymentsintegerrequiredTotal payments (2–1200)
payment_frequencystringrequiredbi-weekly, weekly, monthly, monthly-28, or raw day count
loan_datestringrequiredYYYY-MM-DD or MM/DD/YYYY
first_payment_datestringrequiredMust be after loan_date
day_count_basisintegeroptional360 (default) or 365
Example Request
"loan_amount":        675,
"base_apr":           600,
"num_payments":       20,
"payment_frequency":  "bi-weekly",
"loan_date":          "2026-03-26",
"first_payment_date": "2026-04-03",
"day_count_basis":    360
Response Structure
{
  "inputs":  { /* parsed inputs + odd_days, payments_per_year */ },
  "derived": {
    "daily_rate":       "0.01666667",
    "periodic_rate":    "0.23076923",
    "odd_day_interest": "90.00",
    "annuity_factor":   "4.24949...",
    "standard_payment": "145.73",
    "last_payment":     "145.49"
  },
  "summary": { "finance_charge": "2239.36", "total_payments": "2914.36", ... },
  "schedule": [
    {
      "payment_number":   1,
      "due_date":         "2026-04-03",
      "due_date_display": "04/03/2026",
      "balance_in":       "675.00",
      "interest":         "90.00",
      "principal":        "55.73",
      "payment":          "145.73",
      "accum_principal":  "55.73",
      "accum_interest":   "90.00",
      "balance_out":      "619.27"
    }
  ]
}
GET /api/health No auth required
{ "status": "ok", "service": "loan-schedule-api", "auth_enabled": true }
Try it out
API Key (leave blank if auth is disabled)
Request Body
Calculating…
Response

      
curl Examples
With API key
curl -X POST https://your-app.ondigitalocean.app/api/schedule \
  -H "Content-Type: application/json" \
  -H "X-API-Key: YOUR_RAW_KEY" \
  -d '{
    "loan_amount": 675,
    "base_apr": 600,
    "num_payments": 20,
    "payment_frequency": "bi-weekly",
    "loan_date": "2026-03-26",
    "first_payment_date": "2026-04-03"
  }'
Python requests
import requests

resp = requests.post(
    "https://your-app.ondigitalocean.app/api/schedule",
    headers={"X-API-Key": "YOUR_RAW_KEY"},
    json={
        "loan_amount": 675,
        "base_apr": 600,
        "num_payments": 20,
        "payment_frequency": "bi-weekly",
        "loan_date": "2026-03-26",
        "first_payment_date": "2026-04-03",
    }
)
data = resp.json()
for row in data["schedule"]:
    print(row["due_date_display"], row["payment"], row["balance_out"])