Overview
The TaxaFi API computes US sales and use tax for individual transactions and produces the state return a merchant must file. It is API-first and JSON over HTTPS. TaxaFi is the US system of record: it holds verified rates, jurisdiction boundaries, taxability rules, and every state return form, and returns both the computed figures and the assembled filing. The filing partner submits what TaxaFi returns.
Base URL
https://maxafi.com/taxafi/v1/{action} # equivalent explicit form: https://maxafi.com/taxafi/api/taxafi.php?action={action}
The current version is v1. The version is echoed on every response under
meta.api_version.
Authentication
Every request except health requires an API key passed in the
X-Api-Key header. Keys are prefixed tx_.
X-Api-Key: tx_1a2b3c4d5e6f...
Each key is bound to one merchant. Every authenticated call is automatically scoped to that
merchant — there is no cross-merchant access. Keys may be restricted to a set of scopes
(calculate, ingest, filing); a key with no scope list is
unrestricted. Missing or invalid keys return 401; a key lacking a required scope
returns 403.
Conventions
Response envelope
Every response is a JSON object with a boolean success, the endpoint payload, and a
meta block.
{
"success": true,
// ...endpoint-specific fields...
"meta": { "response_time_ms": 12, "api_version": "v1", "engine": "taxafi-engine/1.1.0" }
}
Money
Monetary fields are decimal dollars with two places: 100.00 means $100.00. Rates
are decimal fractions: 0.0825 means 8.25%.
Idempotency
For calculate, send an X-Idempotency-Key header (or idempotency_key
in the body) to make the recorded calculation idempotent — a retry with the same key returns the
original result instead of recording a duplicate.
Verified data
Calculation and rate responses include data_verified. When false, at least
one applied rate is unverified sample data and must not be used for real remittance; a
warnings entry explains which.
Filing lifecycle
The typical integration uses these calls:
| Step | Call | You send | You get |
|---|---|---|---|
| 1. Price each sale | POST calculate | the consumer ship-to address from the transaction, amount, category | combined rate, jurisdiction breakdown, tax (recorded) |
| 2. Find the return | POST filing_form | the merchant address (or state) | which form applies, its channel, and its field list |
| 3. Prepare figures | POST prepare_return | state + period | period totals and per-category figures rolled up from the recorded sales |
| 4. Build the filing | POST assemble_filing | the period (+ payment details) | the ready-to-submit payload and name→value field map (figures auto-derived) |
calculate scope: calculate
Computes tax for one transaction. Send the consumer's address from the transaction as
ship_to; that delivery address determines the jurisdictions and rate. Optionally send
ship_from (the merchant origin) for origin-sourced states.
Request
| Field | Type | Notes |
|---|---|---|
amount | number | Required. Transaction amount. |
ship_to | Address | Consumer delivery address: street, city, state, zip, plus4. |
category_code | string | Product category (see categories). Defaults to the merchant default or GEN. |
ship_from | Address | Optional merchant origin, used for origin-sourced states. |
currency | string | Defaults USD. |
date | string | YYYY-MM-DD; defaults to today. Selects effective-dated rates. |
source_txn_ref | string | Your transaction id, stored on the immutable calculation record. |
Example
# request curl -X POST https://maxafi.com/taxafi/v1/calculate \ -H "X-Api-Key: tx_..." -H "Content-Type: application/json" \ -H "X-Idempotency-Key: txn_88231" \ -d '{ "amount": 100.00, "category_code": "GEN", "ship_to": { "street": "1100 Congress Ave", "city": "Austin", "state": "TX", "zip": "78701" } }' # response { "success": true, "calculation": { "combined_rate": 0.0825, "subtotal": 100.00, "taxable_amount": 100.00, "tax_total": 8.25, "total": 108.25, "treatment": "taxable", "taxability_basis": "category_default", "resolution": { "confidence": "zip", "resolver": "zip5", "state": "TX" }, "breakdown": [ { "name": "Texas", "level": "state", "rate": 0.0625, "tax": 6.25, "verified": 1 }, { "name": "Austin", "level": "city", "rate": 0.02, "tax": 2.00, "verified": 1 } ], "data_verified": true, "warnings": [] }, "meta": { "api_version": "v1", "...": "..." } }
See the full field list under Data models → Calculation. Largest-remainder
rounding guarantees the per-jurisdiction tax values sum exactly to tax_total.
rates
Resolves the combined rate for an address without a dollar amount — a rate lookup. Returns the
resolved jurisdiction stack, the combined_rate, per-jurisdiction lines,
confidence, and data_verified. Provide at minimum zip (add
plus4 or street for rooftop accuracy).
jurisdictions
With ?zip= (or a street address) it returns the resolved jurisdiction stack for that
address with a confidence. With ?state= it lists all active jurisdictions
in the state ordered state → county → city → special district.
categories
Returns the tax category taxonomy: code, parent_code, name,
default_taxable, description. Use a category code as
category_code on calculate.
classify
Maps a free-text product description to a category code using AI. Advisory
only — intended to suggest a category for review, not to drive filing unattended.
{ "description": "wireless bluetooth headphones" } // -> { "classification": { "code": "GEN", ... } }
obligations scope: filing
The merchant's filing calendar. For each of the merchant's registrations (state + assigned frequency) it returns the just-closed period (fileable now) and the in-progress period, each with its form, due date, and status. Returns are sorted by due date so what is due next is first.
Response
{
"as_of": "2026-07-21",
"obligations": [
{ "state": "AL", "frequency": "monthly", "form_code": "2100",
"period": "2026-06", "period_start": "2026-06-01", "period_end": "2026-06-30",
"due_date": "2026-07-20", "status": "overdue" },
{ "state": "AL", "frequency": "monthly", "form_code": "2100",
"period": "2026-07", "due_date": "2026-08-20", "status": "upcoming" }
]
}
status is overdue, due_soon (within due_soon_days,
default 14), upcoming, or filed. Query params: as_of
(YYYY-MM-DD, defaults to today) and due_soon_days.
rule.source on due_date tells you which was used.
Per-state due days are still being filled in, so verify the exact day for a given state.due_date
The due date for one return. period is YYYY-MM, YYYY-Qn, or
YYYY.
{ "state": "AL", "frequency": "monthly", "period": "2026-06",
"period_end": "2026-06-30", "due_date": "2026-07-20",
"rule": { "due_day": 20, "due_month_offset": 1, "source": "default" } }
prepare_return scope: filing
Rolls the merchant's recorded calculations for a state and period into return figures — the
bridge between per-sale calculate and the period return. It
aggregates period totals, a per-category breakdown, and a per-jurisdiction tax rollup, and returns a
flat figures map keyed to match form fields.
Request
| Field | Type | Notes |
|---|---|---|
state or address | string / Address | Which state to summarize. An address is resolved to a state. |
period | string | YYYY-MM, YYYY-Qn, or YYYY. Or send explicit period_start + period_end (YYYY-MM-DD). |
Response
{
"state": "TX", "period": { "start": "2026-04-01", "end": "2026-06-30" }, "transactions": 1842,
"totals": { "gross_sales": 184320.00, "taxable_sales": 171200.00, "exempt_sales": 13120.00, "tax_total": 14124.00 },
"by_category": [ { "category": "GEN", "gross": 160000.00, "taxable": 160000.00, "exempt": 0.00, "tax": 13200.00 } ],
"by_jurisdiction": [ { "name": "Texas", "level": "state", "tax": 10700.00 } ],
"figures": { "gross_sales": 184320.00, "taxable_sales": 171200.00, "tax_due": 14124.00,
"gross:gen": 160000.00, "exempt:gen": 0.00, "...": "..." }
}
The figures keys use the same convention as form source_fields
(gross_sales, taxable_sales, exempt_sales, tax_due,
and per-category gross:<cat> / exempt:<cat> /
taxable:<cat> / tax:<cat>), so they feed straight into
assemble_filing. Categories here are TaxaFi category codes;
mapping them to a state form's own buckets (for example Alabama's amusement / automotive columns) is
a per-form step.
filing_form scope: filing
Resolves which return the merchant files and returns its structure. Send the merchant's
address (TaxaFi derives the state) or an explicit state. The form is chosen from the
merchant's filing frequency and filer type; SST-registered merchants resolve to the Simplified
Electronic Return.
Request
| Field | Type | Notes |
|---|---|---|
address | Address | Merchant business address. Provide this or state. |
state | string | Two-letter state, if you already know it. |
filing_frequency | string | monthly | quarterly | annual. The frequency the state assigned the merchant. |
filer_variant | string | standard (default) | ez | others. |
sst | boolean | True if the merchant is Streamlined-registered. |
Response
{
"state": "AL",
"form": { "form_code": "2100", "form_name": "Alabama Sales Tax Return (Form 2100)",
"filing_channel": "csv", "filing_frequency": "any", "filer_variant": "standard" },
"format_spec": { "layout": "positional_csv", "delimiter": ",", "date_format": "MM/DD/YYYY" },
"fields": [
{ "position": 1, "code": "account_id", "label": "Account ID",
"value_source": "account", "source_field": "ador_account_number", "data_type": "text" },
{ "position": 8, "code": "gross_amusement", "label": "Gross: Amusement",
"value_source": "figure", "source_field": "gross:amusement", "data_type": "currency" }
// ...
]
}
filing_channel tells you how the return is submitted:
csv (positional bulk upload), pdf (fillable form fields), xml
(SST SER), portal, or efile. The fields structure is the same
across channels — only the submission format differs. When a state has no return (e.g. Oregon), the
response has form: null with an explanatory note.assemble_filing scope: filing
Builds the filing for a period. TaxaFi selects the form, pulls each field from its source, formats
per the form rules, and returns both the ready-to-submit payload and a
field_map of name → value. You supply the period figures (TaxaFi supplies the form
structure, formatting, and validation).
Request
| Field | Type | Notes |
|---|---|---|
state or address | string / Address | Which state to file. Address is resolved to a state. |
period | string | YYYY-MM, YYYY-Qn, or YYYY. Or send an explicit period_end (YYYY-MM-DD). |
figures | object | Optional. Period figures keyed by the form's source_field, e.g. {"gross:amusement": 1500.00, "tax_due": 4231.55}. If omitted, they are auto-derived from the merchant's recorded calculations for the period (see prepare_return); anything you supply overrides the derived value. |
payment | object | Remittance details for banking fields: payment_type_code, routing_number, bank_account_number, bank_account_type, payment_amount. |
account | object | Account identifiers (e.g. ador_account_number). Falls back to the merchant's registration number. |
Response
{
"form": { "form_code": "2100", "channel": "csv", "version": "2026" },
"channel": "csv",
"period_end": "2026-06-30",
"payload": "0009988776,06/30/2026,E,062000019,0001234567,C,4231.55,1500.00,200.00,...",
"field_map": { "account_id": "0009988776", "filing_period": "06/30/2026",
"gross_amusement": "1500.00", "...": "..." },
"fields": [ { "position": 1, "code": "account_id", "formatted": "0009988776", "...": "..." } ],
"warnings": []
}
csv, payload is the ready
positional line and field_map gives the same values by name. For pdf /
xml / portal, payload equals field_map (name → value),
which the channel step fills into the PDF fields, SER elements, or portal inputs.figures are omitted they are auto-derived from recorded calculations
and mapped into the form's own category buckets via its category_map (e.g. Alabama
admissions to the amusement column, everything else to All Other Sales). The PDF / SER serializers
are still in progress; today the csv serializer is complete.assemble_pdf scope: filing
Produces the filled return PDF for states that file on paper or on a downloadable form.
It assembles the filing (same inputs as assemble_filing),
then applies the resulting field map to the blank PDF the state publishes. Two kinds of PDF are
handled: forms with real fields are filled directly, and flat forms have their values drawn at
the coordinates the spec defines.
Request
Same as assemble_filing: the state or merchant
address, the period, and optional figures / payment / account
overrides. Figures are auto-derived from recorded calculations when omitted.
Response
{
"channel": "pdf",
"form": { "form_code": "01-114", "form_name": "Texas Sales and Use Tax Return" },
"file": { "filename": "return_20260721_144802_a1b2c3.pdf", "bytes": 253982,
"blank": "tx_2025.pdf", "mode": "fill" },
"fields_applied": 21, "unknown_fields": [],
"field_map": { "Itm001": "184,320.00", "Itma15": "10,700.00", "...": "..." }
}
unknown_fields lists any spec field the blank PDF does not actually contain, which
is how a form spec drifting from a re-issued state form gets caught.
assemble_ser scope: filing
Builds the Streamlined Simplified Electronic Return (SER) XML for a period. The ~24
Streamlined member states accept this one format, so a single call covers all of them — no
per-state form needed. Figures are derived from the merchant's recorded calculations for the period
(same rollup as prepare_return).
Request
| Field | Type | Notes |
|---|---|---|
state or address | string / Address | The Streamlined member state to file. |
period | string | YYYY-MM, YYYY-Qn, or explicit period_start + period_end. |
sst_id | string | The seller's Streamlined (SST) ID. |
fein | string | Federal EIN. |
doc_type | string | O original (default) or A amended. |
Response
{
"channel": "ser", "state": "IN",
"period": { "start": "2026-06-01", "end": "2026-06-30" },
"totals": { "gross_sales": 184320.00, "taxable_sales": 171200.00, "tax_total": 11984.00 },
"schema_version": "SSTSER202409V01",
"payload": "<SSTSimplifiedReturnTransmission>...</SSTSimplifiedReturnTransmission>"
}
ingest scope: ingest
Lands declared figures directly into the Tax Authority Ledger as signed entries (declared mode) —
for reconciling externally computed tax rather than pricing individual transactions. Accepts one
entry or an entries array. Each entry: stream
(revenue|expense|adjustment), amount,
period_year, period_month, and one of {authority_id,
jurisdiction_id, state}.
ledger
Returns the net position per authority/period for the scoped merchant: tax_collected,
tax_expense, adjustments, net_due, and entry_count,
grouped by authority and period. Optional period filters.
health
No auth. Liveness plus engine version and table presence. Use for uptime checks.
{ "success": true, "status": "ok", "tables_present": { "...": true } }
Data models
Address
Used as ship_to (consumer), ship_from (merchant origin), and the merchant
address on filing calls.
| Field | Type | Notes |
|---|---|---|
street | string | Enables rooftop / ZIP+4 accuracy where available. |
city | string | |
state | string | Two-letter code. |
zip | string | 5-digit ZIP. |
plus4 | string | ZIP+4 add-on for the most precise jurisdiction match. |
county | string | Optional county name. Disambiguates ZIPs that span more than one county, and resolves the jurisdiction when no ZIP match is found. |
county_fips | string | Optional county FIPS code. Used the same way as county when you have the code. |
Calculation
| Field | Type | Notes |
|---|---|---|
combined_rate | number | Sum of applied jurisdiction rates (decimal fraction). |
subtotal / taxable_amount / exempt_amount | decimal $ | Amount, taxed base, and exempt portion. |
tax_total / total | decimal $ | Tax due and grand total. |
treatment / taxability_basis | string | taxable | exempt | reduced, and how it was decided. |
merchant_exempt | bool | Whether a merchant exemption certificate applied. |
sourcing / sourcing_used | string | origin vs destination sourcing. |
resolution | object | confidence (zip4|zip|county_fips|state|none), resolver, state. |
breakdown | array | Per-jurisdiction name, level, rate, tax, source, verified. |
data_verified | bool | False if any applied rate is unverified sample data. |
warnings | array | Human-readable cautions (unresolved address, state-only match, unverified data). |
Form field
| Field | Notes |
|---|---|
position | Order on the return (column index for CSV, line order for PDF). |
code / label | Field identifier and human label. |
value_source | Where the value comes from: figure, payment, account, period, constant, formula, manual. |
source_field | The specific key, interpreted per source (e.g. gross:amusement, routing_number). |
data_type / format_rule | Type and validation (mask, enum values, digit length, blank policy). |
Errors
Errors return success: false with an error message and the HTTP status below.
| Status | Meaning |
|---|---|
400 | Malformed request. |
401 | Missing or invalid API key. |
403 | Key lacks the required scope. |
404 | Unknown action, or address could not be resolved. |
405 | Wrong method (e.g. GET on a POST-only action). |
422 | Valid request but missing/invalid parameters. |
500 | Internal error. |
502 | Upstream AI error (classify). |
{ "success": false, "error": "amount is required.", "meta": { "...": "..." } }
TaxaFi API v1 · © 2026 Dapit Financial Infrastructure · confidential