Guides · curves

Bootstrap a yield curve from deposits and swaps via REST API

POST deposit and swap quotes to /v1/curves/bootstrap and you get back a discount curve: discount factor and continuously compounded zero rate at each pillar you ask for. QuantLib's piecewise log-linear-discount bootstrapper does the solving; every convention is stated in the response.

01The call

Two deposits for the short end, three par swaps for the long end, four output pillars:

request
curl -s https://quantbox.dev/v1/curves/bootstrap \
  -H "X-API-Key: $KEY" -H "Content-Type: application/json" \
  -d '{"valuation_date": "2026-07-01",
       "deposits": [{"tenor": "3M", "rate": 0.031},
                    {"tenor": "6M", "rate": 0.032}],
       "swaps":    [{"tenor": "2Y",  "rate": 0.034},
                    {"tenor": "5Y",  "rate": 0.036},
                    {"tenor": "10Y", "rate": 0.0375}],
       "output_tenors": ["1Y", "2Y", "5Y", "10Y"]}'
response
{
  "valuation_date": "2026-07-01",
  "points": [
    {"tenor": "1Y",  "date": "2027-07-01", "years": 1.0,
     "discount_factor": 0.967570, "zero_rate": 0.032968},
    {"tenor": "2Y",  "date": "2028-07-03", "years": 2.008219,
     "discount_factor": 0.935148, "zero_rate": 0.033388},
    {"tenor": "5Y",  "date": "2031-07-01", "years": 5.002740,
     "discount_factor": 0.837636, "zero_rate": 0.035415},
    {"tenor": "10Y", "date": "2036-07-01", "years": 10.008219,
     "discount_factor": 0.690776, "zero_rate": 0.036964}
  ],
  "conventions": {
    "deposits": "act/360, ModifiedFollowing, TARGET, 2 fixing days",
    "swaps": "fixed leg annual 30/360 vs Euribor6M, TARGET",
    "zero_rates": "continuously compounded, act/365",
    "interpolation": "log-linear on discount factors"
  }
}

Runs on the free tier — 500 calls/month, no credit card. Get a key.

02What bootstrapping actually does

A yield curve is not observed; it is implied. The market quotes instruments — a 3M deposit at 3.1%, a 5Y swap at 3.6% — and bootstrapping means finding the one discount curve that reprices every input exactly. The solver works piecewise from the short end out: deposits pin down the first pillars directly, then each swap adds one pillar chosen so the swap's fixed leg equals its floating leg at par.

Between pillars the curve interpolates log-linearly on discount factors — equivalent to assuming constant forward rates within each segment, the standard robust choice. The two rate families deliberately keep their own market conventions: deposits accrue act/360, swap fixed legs pay annually on 30/360 against Euribor 6M. The output normalizes everything into one comparable pair per pillar: a discount factor, and its continuously compounded act/365 zero rate.

03Reading the pillars

TenorDateYears (act/365)Discount factorZero rate
1Y2027-07-011.0000000.9675703.2968%
2Y2028-07-032.0082190.9351483.3388%
5Y2031-07-015.0027400.8376363.5415%
10Y2036-07-0110.0082190.6907763.6964%

Note the 2Y pillar: 2028-07-01 is a Saturday, so ModifiedFollowing on the TARGET calendar rolls it to Monday 2028-07-03. Its year fraction is therefore 733/365 = 2.008219, not 2.0 — the API reports the dates and year fractions it actually used, so downstream math stays honest. Each row satisfies DF = exp(−z·t) exactly; the conversion is worked through in discount factor vs zero rate.

The zero curve rises from 3.30% at 1Y to 3.70% at 10Y. If you expected the 10Y zero to sit above the 3.75% par swap quote (the textbook rule for upward-sloping curves), mind the compounding: 3.6964% continuous equals 3.7656% annually compounded — indeed just above the par rate. Comparing rates across compounding conventions without converting is the classic way to misread a curve.

04Conventions, spelled out

InputConvention
Depositsact/360 day count, ModifiedFollowing adjustment, TARGET calendar, 2 fixing days
SwapsFixed leg annual on 30/360, versus Euribor 6M floating, TARGET calendar
Zero rates (output)Continuously compounded, act/365
InterpolationLog-linear on discount factors

These are standard EUR market conventions. They matter: quoting the same 3.1% deposit as act/365 instead of act/360 moves the short-end discount factors by more than a basis point, silently. The conventions block ships with every response so results are auditable months later.

05Pin the valuation date, get reproducible curves

valuation_date is optional — omitted, it defaults to today, adjusted to a TARGET business day. That is convenient and unreproducible: the same request gives different pillars tomorrow. Passing "valuation_date": "2026-07-01" freezes the reference date, which is why every number on this page can be re-run and matched to the last digit. Fix it in anything you test, cache or audit.

06Same thing in Python

python
import requests

r = requests.post("https://quantbox.dev/v1/curves/bootstrap",
    headers={"X-API-Key": KEY},
    json={"valuation_date": "2026-07-01",
          "deposits": [{"tenor": "3M", "rate": 0.031},
                       {"tenor": "6M", "rate": 0.032}],
          "swaps":    [{"tenor": "2Y",  "rate": 0.034},
                       {"tenor": "5Y",  "rate": 0.036},
                       {"tenor": "10Y", "rate": 0.0375}],
          "output_tenors": ["1Y", "2Y", "5Y", "10Y"]})

for p in r.json()["points"]:
    print(f'{p["tenor"]:>4}  DF {p["discount_factor"]:.6f}  '
          f'zero {p["zero_rate"]:.4%}')

The obvious next step is to price something on these pillars: price a bond on a bootstrapped zero curve feeds them straight into POST /v1/bonds/price.

Bootstrap your own quotes. Key in seconds, 500 free calls a month, full request schema in the docs.

Get a free API key

No credit card · schemas and error codes in /docs

Related: Discount factor vs zero rate · Price a bond on a bootstrapped zero curve · Bond duration, convexity and BPV over HTTP