Hundred Summers
Proof of concept

Projection API

The engine behind every Hundred Summers figure, available to other software: send a plan, get back the year-by-year projection the app would show — UK income tax (including Scotland), National Insurance, pensions and their tax-free cash, ISAs and other savings, the State Pension, property, debts and inheritance tax — with the assumptions it used and a fingerprint of the engine that produced it.

Status: a proof of concept, version 0.1. Access is included in the Adviser plan, so a firm's own systems (a CRM, client portal or back office) can run plans through the same engine; other design partners by arrangement. Ask for a key at contact@hundredsummers.co.uk with a line on what you would build. Expect it to change as we learn from you.

Privacy: nothing is kept

The API is stateless. The plan you send is run in memory and the answer goes straight back; nothing is stored, and request bodies are never logged (we record only which key called, the response status and the time taken). Names are removed before the engine sees the plan and none are returned. Please send no names, contact details or other identifiers. This doesn't touch the app itself: our members' plans stay on their own devices, and the API holds none of them.

Endpoints

RequestKeyWhat it does
GET /api/v1/statusNoThe API version and the engine fingerprint now running.
POST /api/v1/projectionYesRuns a plan and returns the projection.

Send the key as Authorization: Bearer <key> and the body as JSON (Content-Type: application/json), at most 256 KB. Each key may make up to 60 requests a minute. Call it from your server, never from a web page: a key in page code is public, so browsers are not allowed to call it.

A worked example

A 45-year-old on £60,000 with a workplace pension and an ISA, retiring at 65. The request:

curl https://hundredsummers.co.uk/api/v1/projection \
  -H "Authorization: Bearer $HS_API_KEY" \
  -H "Content-Type: application/json" \
  -d @plan.json
{
  "facts": {
    "currentAge": 45,
    "retirementAge": 65,
    "planToAge": 92,
    "compensation": {
      "baseSalary": 60000
    },
    "taxOptimisation": {
      "niQualifyingYears": 24
    },
    "preRetirementExpense": 32000,
    "retirementExpense": 28000,
    "pensionPots": [
      {
        "id": 1,
        "label": "Workplace pension",
        "value": 150000,
        "annualContrib": 3000,
        "employerContrib": 3000,
        "riskProfile": "moderate"
      }
    ],
    "savingsAccounts": [
      {
        "id": 2,
        "label": "Stocks and shares ISA",
        "value": 40000,
        "annualContrib": 4000,
        "growthRate": 5,
        "taxWrapper": "isa",
        "assetClass": "equity"
      }
    ]
  },
  "options": {
    "money": "today",
    "currentYear": 2026
  }
}

The answer, computed by the engine when this page was built (in 2026 pounds, as asked): spending is covered in every year; at 65 the pensions hold £331,979 and the savings £280,836; lifetime tax comes to £324,748. Some of the rows:

AgePayState PensionFrom pensionsFrom savingsSpendingIncome taxPensionsSavingsNet worth
45£60,000£0£0£0£32,000£10,232£150,000£40,000£190,000
64£60,000£0£0£0£32,000£12,141£325,625£304,161£629,786
65£0£0£0£28,000£28,000£0£331,979£280,836£612,815
80£0£12,548£0£15,739£28,000£288£443,605£58,563£502,168
92£0£12,548£18,517£0£28,000£3,065£387,438£0£387,438

And the summary, as returned:

{
  "summary": {
    "spendingCovered": true,
    "firstShortfall": null,
    "atRetirement": {
      "age": 65,
      "year": 2046,
      "netWorth": 612815,
      "pensions": 331979,
      "savingsAndInvestments": 280836
    },
    "atPlanEnd": {
      "age": 92,
      "year": 2073,
      "netWorth": 387438,
      "pensionsAndSavings": 387438,
      "inheritanceTaxEstimate": 114245
    },
    "lifetimeUnfundedSpendingToday": 0,
    "lifetimeTaxToday": {
      "incomeTax": 264519,
      "nationalInsurance": 60229,
      "capitalGainsTax": 0,
      "savingsAndDividendTax": 0,
      "total": 324748
    }
  }
}

The plan you send

facts is a plan in the app's own format, and only currentAge, retirementAge and planToAge are required. Anything you leave out takes the value of a new, empty plan in the app, and empty means empty: no pensions, savings, property or spending, and no State Pension unless you give either statePension (a forecast, £ a year) or taxOptimisation.niQualifyingYears. Nested parts such as compensation merge field by field, so { "baseSalary": 60000 } keeps the other compensation defaults. The quickest way to see every field is to build a plan in the app, turn off backup encryption in Settings, save it to a file, and send that file as the body as it is: the API accepts the app's plan file and runs its active plan.

OptionValuesDefault
options.money"nominal" (each year's pounds) or "today" (deflated at the plan's inflation)"nominal"
options.currentYearThe year the plan starts in. Set it to make a result repeatable from one year to the next.This year
options.rowsfalse returns the summary alonetrue

The answer

summary says whether spending is covered every year (and, if not, when it first falls short), the position at retirement and at the end of the plan, and lifetime tax and lifetime unfunded spending in today's pounds. assumptions lists every rate the engine resolved (inflation, growth, allowances, charges), basis the year and money terms, and rows one entry a year, the first being today's position:

FieldMeaning
yearCalendar year.
ageAge during that year. The first row is today's position, before any flows.
retiredTrue from the retirement age.
employmentIncomeSalary and bonus from every role that year, after any phased-retirement step.
statePensionState Pension paid.
definedBenefitIncomeFinal-salary (defined benefit) pensions paid.
otherIncomeOther income lines as entered (rental and other taxable income).
annuityIncomeIncome from any annuity bought.
pensionDrawdownTaken from defined contribution pensions, gross (before income tax).
savingsDrawdownTaken from savings and investments.
totalIncomeAll income and withdrawals that year.
spendingPlanned living spending (working-years or retirement budget, with any later-life additions).
totalExpensesEverything paid out: spending, debt repayments, one-off costs and the like.
pensionContributionsPaid into pensions, by the person and any employer.
savingsContributionsPaid into savings and investments.
incomeTaxIncome tax on earnings, pensions and other income.
nationalInsuranceEmployee or self-employed National Insurance.
capitalGainsTaxCapital gains tax on investments and property.
savingsAndDividendTaxTax on savings interest and dividends.
studentLoanRepaymentStudent loan repaid.
unfundedSpendingSpending the plan could not pay for that year. Above zero means the plan runs short.
pensionsDefined contribution pensions at the end of the year.
savingsAndInvestmentsSavings and investments at the end of the year.
propertyProperty value at the end of the year.
liabilitiesMortgages and other debts outstanding.
netWorthEverything owned less everything owed.
inheritanceTaxEstimateInheritance tax if the estate passed that year, on the plan's estate settings.

Reproducing a result

Every answer carries engine.fingerprint, a hash of the engine's source code. The engine is deterministic: the same request, with options.currentYear set, to an engine with the same fingerprint gives the same answer, to the pound. A different fingerprint means the engine changed, usually because a tax rule or published figure did; every change is recorded on What's new and every assumption is published in the Methodology. The fixed-rule arithmetic is checked case by case in the validation pack.

Errors

StatusCodeWhen
400no_plan, bad_json, bad_optionNo plan in the body, the body isn't JSON, or an option is out of range.
401unauthorisedNo key, or a key that is wrong or revoked.
405, 404method_not_allowed, not_foundThe wrong method, or an unknown endpoint.
413, 415too_large, unsupported_media_typeA body over 256 KB, or not sent as JSON.
422invalid_plan, engine_errorA required age is missing or out of range, or the engine cannot run the plan.
429rate_limitedMore than 60 requests in a minute; wait for the Retry-After seconds.

Terms for the proof of concept

Questions, or a plan the engine gets wrong? contact@hundredsummers.co.uk