---
title: "Plan a year of tax-aware retirement withdrawals"
method: POST
path: "/v1/retirement/withdrawal-sequencing"
tags: ["Retirement"]
---

# Plan a year of tax-aware retirement withdrawals

`POST /v1/retirement/withdrawal-sequencing`

## Request

- Base URL: `https://api.numeratica.com`
- URL: `https://api.numeratica.com/v1/retirement/withdrawal-sequencing`
- Auth: one of:
  - HTTP bearer
  - API key in header `X-API-Key`

Decides how much to withdraw from taxable, tax-deferred, and tax-free (Roth) accounts to net a target after-tax spending amount, accounting for the tax the withdrawals themselves generate. Supports three strategies (conventional, bracket_fill, proportional), models taxable cost basis so only the gain is taxed, and — when an age is supplied — takes the year's RMD from the tax-deferred account first. Calculation engine output, not tax advice. Deterministic.

## Request body

- WithdrawalSequencingRequest
  - `tax_year` integer, required
  - `filing_status` 'single' | 'mfj' | 'mfs' | 'hoh', required
  - `spending_need` number, required: After-tax cash needed this year.
  - `strategy` 'conventional' | 'bracket_fill' | 'proportional', required: conventional = taxable -> tax-deferred -> Roth; bracket_fill = tax-deferred up to a target bracket then Roth/taxable; proportional = pro-rata across accounts.
  - `bracket_ceiling` number: Target marginal ordinary rate to fill up to. Required for the bracket_fill strategy.
  - `taxable_balance` number
  - `tax_deferred_balance` number
  - `tax_free_balance` number
  - `taxable_cost_basis_fraction` number: Share of a taxable withdrawal that is return of basis (untaxed); the rest is a long-term gain.
  - `other_ordinary_income` number: Other ordinary income this year (sets the tax baseline).
  - `other_qualified_income` number: Other long-term gains / qualified dividends this year.
  - `state_tax_rate` number
  - `age` integer: Optional. If set, the year's RMD is taken from the tax-deferred account first.
  - `birth_year` integer: Optional. Sets the SECURE 2.0 RMD beginning age.

## Response `200`

Withdrawal plan

- WithdrawalSequencingResponse
  - `result_id` string
  - `engine_version` string
  - `inputs` WithdrawalSequencingRequest
    - `tax_year` integer, required
    - `filing_status` 'single' | 'mfj' | 'mfs' | 'hoh', required
    - `spending_need` number, required: After-tax cash needed this year.
    - `strategy` 'conventional' | 'bracket_fill' | 'proportional', required: conventional = taxable -> tax-deferred -> Roth; bracket_fill = tax-deferred up to a target bracket then Roth/taxable; proportional = pro-rata across accounts.
    - `bracket_ceiling` number: Target marginal ordinary rate to fill up to. Required for the bracket_fill strategy.
    - `taxable_balance` number
    - `tax_deferred_balance` number
    - `tax_free_balance` number
    - `taxable_cost_basis_fraction` number: Share of a taxable withdrawal that is return of basis (untaxed); the rest is a long-term gain.
    - `other_ordinary_income` number: Other ordinary income this year (sets the tax baseline).
    - `other_qualified_income` number: Other long-term gains / qualified dividends this year.
    - `state_tax_rate` number
    - `age` integer: Optional. If set, the year's RMD is taken from the tax-deferred account first.
    - `birth_year` integer: Optional. Sets the SECURE 2.0 RMD beginning age.
  - `result` object
    - `strategy` string
    - `withdrawals` object
      - `taxable` number
      - `tax_deferred` number
      - `tax_free` number
    - `gross_withdrawal` number: Total pulled across all accounts.
    - `rmd_amount` number: Forced tax-deferred distribution included above.
    - `taxable_gain_realized` number
    - `ordinary_income_total` number
    - `qualified_income_total` number
    - `incremental_tax` number: Tax caused by the withdrawals.
    - `total_tax` number: Full-year tax including other income.
    - `after_tax_cash` number: Spendable cash delivered (gross minus incremental tax).
    - `shortfall` number: Unmet spending need if balances are insufficient.
    - `surplus` number: Cash beyond the need (e.g. when an RMD forces more).
    - `marginal_ordinary_rate` number
  - `disclaimer` string

## Other responses

- `400`: Invalid request
- `401`: Missing or invalid API key.
- `405`: Method not allowed
- `429`: The key's per-minute request budget is exceeded. Retry after the delay in the Retry-After header.
- `503`: A dependency (e.g. the key store) is temporarily unavailable, or the request exceeded its compute deadline. Retryable.

---

[API](https://skmtc.dev/numeratica/apis/numeratica-financial-planning-calculations-api.md) · [All operations](https://skmtc.dev/numeratica/apis/numeratica-financial-planning-calculations-api/llms.txt) · [OpenAPI document](https://skmtc.dev/numeratica/apis/numeratica-financial-planning-calculations-api/revisions/075bcfbd1ebf?raw)
