QuantCalc QuantCalc Federal tax engine docs

Federal tax / Library

The federal tax engine as a library

The engine behind POST /api/federal-tax is also a library you run inside your own application (libfedtax 1.0.0, engine 1.3.0, tax year 2026): the same C code, the same rules, the same citations and the same output, byte for byte. No network call, no per-request latency, and no household data leaving your system. It is the sibling of the state tax engine library.

One engine, two doors

The JSON entry point (qcft_compute_json) is the API's own request parser, field table, engine call and response builder, compiled unchanged: for any request it returns the bytes the API returns, including the 400 body of a refusal. The schema and rules documents (GET /api/federal-tax/schema and /rules) are there too, byte for byte. The typed entry point (qcft_compute) takes a C struct and returns the result's figures; it runs the API's own parser and evaluator on the request, so a refused typed request carries the API's message.

What you get

ArtefactSizeFor
libfedtax-1.0.0-linux-x86_64.tar.gz261 KBC/C++ and any language with a C FFI: libfedtax.so.1, libfedtax.a, the header, a CMake package and pkg-config
quantcalc-fedtax-1.0.0.tgz99 KBnpm: WebAssembly, ESM glue and TypeScript types (Node 18 or later, browsers, workers)
quantcalc_fedtax-1.0.0-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl122 KBPython 3.9 or later: one wheel for every CPython version, typed
libfedtax-1.0.0-src.tar.gz178 KBthe C source drop, when the licence includes source

Thin bindings for Go, Rust, Java, .NET and Swift call the JSON entry point of the same shared library. Sizes measured on the 1.0.0 release build (engine 1.3.0) of 2026-09-28; every artefact is rebuilt byte for byte from the same sources, with SHA-256 checksums and a manifest of the pinned toolchains.

Without the citations

Each full response carries the official sources of every step on the return, with their checked quotes. A build without them answers every request as the full library does, minus rules.sources and rules.unsourced (the blocks includeSources: false leaves out); every figure is the same. The sources stay published on the sources page.

ArtefactSizeFor
libfedtax-nosources-1.0.0-linux-x86_64.tar.gz168 KBthe C library without the citations
quantcalc-fedtax-nosources-1.0.0.tgz75 KBnpm @quantcalc/fedtax-nosources
quantcalc_fedtax_nosources-1.0.0-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl85 KBPython distribution quantcalc-fedtax-nosources (import name quantcalc_fedtax)
libfedtax-nosources-1.0.0-src.tar.gz178 KBthe source drop, built without the citations

Size budgets are enforced on every release build (bytes, measured and budget):

BinaryFullWithout citations
libfedtax.so.1, stripped370,688 (budget 445,000)194,560 (budget 229,000)
libfedtax.a514,608 (budget 613,000)260,088 (budget 310,000)
fedtax.wasm, raw229,436 (budget 274,000)146,295 (budget 175,000)
npm WebAssembly + glue + wrapper + layout, gzipped91,413 (budget 111,000)65,770 (budget 81,000)

The typed request

The typed request is generated from the API's field table, so it has one member per request field and nothing else. When the API gains a field, a regeneration and a rebuild add it at the end of the struct, and the build fails until that is done. The offsets below are the library's binary interface: they never move within a major version.

KindC typeNot sent
one of the listed stringsint32_t (index)QCFT_ENUM_ABSENT
whole numberint32_tQCFT_INT_ABSENT
booleanint32_tQCFT_BOOL_ABSENT
amountdoubleQCFT_ABSENT
list of {year, taxable, nontaxable}_count + qcft_roth_conversion[16]count QCFT_LIST_ABSENT
up to 4 amounts_count + double[4]count QCFT_LIST_ABSENT
up to 4 dates_count + int32_t[4] (YYYYMMDD)count QCFT_LIST_ABSENT
JSON fieldC memberKindOffset
filingStatusfiling_statusone of the listed strings: single, mfj, married_filing_jointly, mfs, married_filing_separately, hoh, head_of_household, qss, qualifying_surviving_spouse8
ageagewhole number12
spouseAgespouse_agewhole number16
mfsLivedApartmfs_lived_apartboolean20
iraDistributionsira_distributionsamount24
rothConversionroth_conversionamount32
pensionpensionamount40
otherOrdinaryIncomeother_ordinary_incomeamount48
shortTermGainsshort_term_gainsamount56
longTermGainslong_term_gainsamount64
socialSecurityGrosssocial_security_grossamount72
taxExemptInteresttax_exempt_interestamount80
includeSourcesinclude_sourcesboolean88
birthMonthbirth_monthwhole number92
blindCountblind_countwhole number96
saltIncomeOrSalesTaxsalt_income_or_sales_taxamount104
saltRealEstateTaxsalt_real_estate_taxamount112
saltPersonalPropertyTaxsalt_personal_property_taxamount120
otherTaxesother_taxesamount128
mortgageInterestmortgage_interestamount136
investmentInterestinvestment_interestamount144
charityCashcharity_cashamount152
charityCashDafcharity_cash_dafamount160
charityCapitalGainPropertycharity_capital_gain_propertyamount168
medicalExpensesmedical_expensesamount176
otherItemizedother_itemizedamount184
itemizeModeitemize_modeone of the listed strings: auto, itemize, standard192
mfsSpouseItemizesmfs_spouse_itemizesboolean196
employerPlanDistributionemployer_plan_distributionamount200
separationYearAgeseparation_year_agewhole number208
publicSafetyEmployeepublic_safety_employeeboolean212
seppDistributionsepp_distributionamount216
disableddisabledboolean224
iraBasisira_basisamount232
iraYearEndValueira_year_end_valueamount240
rothDistributionroth_distributionamount248
rothContributionsroth_contributionsamount256
rothFirstContributionYearroth_first_contribution_yearwhole number264
rothConversionsroth_conversionslist of {year, taxable, nontaxable}268
iraBalancePriorYearEndira_balance_prior_year_endamount656
spouseSoleBeneficiaryspouse_sole_beneficiaryboolean664
beneficiarySpouseAgebeneficiary_spouse_agewhole number668
rmdShortfallrmd_shortfallamount672
rmdShortfallCorrectedrmd_shortfall_correctedboolean680
priorYearTaxprior_year_taxamount688
priorYearAgiprior_year_agiamount696
priorYearFullYearReturnprior_year_full_year_returnboolean704
withholdingwithholdingamount712
estimatedPaymentsestimated_paymentsup to 4 amounts720
estimatedPaymentDatesestimated_payment_datesup to 4 dates760
taxableInteresttaxable_interestamount784
ordinaryDividendsordinary_dividendsamount792
qualifiedDividendsqualified_dividendsamount800
capitalLossRulescapital_loss_rulesboolean808
capitalLossCarryoverShortTermcapital_loss_carryover_short_termamount816
capitalLossCarryoverLongTermcapital_loss_carryover_long_termamount824

With the state engine

The state tax library (libstatetax 1.6.0) carries this engine: its JSON entry point answers computeFederal: true as POST /api/state-tax does, with the federal engine supplying the federal figures the state rules read. It is one binary, so the two engines are always the same version.

The same answer, checked on every build

A parity gate runs on every build. On 2026-09-28 (engine 1.3.0) it sent a generated corpus of 16,551 requests (13,478 computed, 3,073 refused: every filing status and age gate, every one of the 56 request fields at and past its limits, extended returns, every refusal the API makes, malformed bodies) through four surfaces: the C library, the WebAssembly package, the Python package and the API's own request handler. All four must produce the same bytes for every request, and the 11,300 requests the typed entry point can express must give the same document through it. The build also runs the whole test suite under address, undefined-behaviour and thread sanitizers, fuzzes both entry points, and checks that the binary exports only its own functions.

Performance

Measured 2026-09-28 (engine 1.3.0) on Intel Core i7-12800H, one core; 1,547,861 typed calls a second on 8 threads.

SurfaceCallCalls a secondMicroseconds a call
Ctyped (qcft_compute)278,5283.6
CJSON, includeSources false36,86427.1
CJSON, the full document with its sources7,339136.3
CJSON, an extended return with its sources3,584279.0
WebAssembly (Node)typed (compute)153,2716.5
WebAssembly (Node)JSON, includeSources false20,08949.8
WebAssembly (Node)JSON, the full document3,906256.0
Pythontyped (compute)61,05216.4
Pythontyped, compute_batch of 1,00069,43314.4
PythonJSON, the full document6,462154.8

Most of a JSON call is building and printing the document, and the sources are most of the document: send includeSources: false, or use the typed entry point, when a response does not need them.

Versions and updates

The library version follows semantic versioning (additions are minor versions; a break is a new major version with a new shared-library name); the engine version, reported as engineVersion, moves whenever a rule, rate or threshold moves; the tax year is the law the build encodes.

Licence

The library is licensed commercially. Ask at [email protected].