SLTax360 API

Send your policies to us for surplus lines filing, and calculate surplus lines tax and fees for all 50 states and DC.

https://api.sltax360.com/api/v1
POST /invoices
POST /invoices/preview
GET /invoices/{id}
POST /calculator/estimate
Documentation

Developer Hub

The API does two jobs with one key. Invoice push: your system sends each policy transaction, and we prepare and submit the surplus lines filing. Tax calculator: your system asks for the surplus lines tax and fees on a premium. Start with the part you need.

Send your first invoice

Six fields, one call, a 201 reply. Then add more data step by step.

Start here
Tax calculator

One endpoint, itemised taxes and fees for all 50 states and DC.

Calculator quick start
Full REST API (Swagger UI)

Every endpoint, with Try it out.

Open Swagger UI
OpenAPI 3.0 spec

Generate a client in your language from the machine-readable spec.

openapi.json
Postman collection

Every step of the integration ladder, ready to run.

Download
Get a free test key

Try the API on your own test account. Our team sets it up for you.

Get a free test key

Send your first invoice

SLTax360 files surplus lines tax for the policies your system writes.

  • You send each policy transaction from your system: a new policy, a renewal, an endorsement or a cancellation.
  • We prepare and submit the state filing. The licensed broker stays responsible for the filing.
  • You start small. Six fields are enough for the first call. Add more data later.

You need an API key. It looks like sltax_t2_ followed by 32 letters and digits. If you do not have one, get a free test key: our team sets up a separate test account for you. Put your key in an environment variable called SLTAX_API_KEY, so it stays out of your code.

Step 0: check your key
cURL
curl https://api.sltax360.com/api/v1/auth/me \
  -H "Authorization: Bearer $SLTAX_API_KEY"

A 200 reply with your key name and tier means the key works. A 401 means the key is missing or wrong.

Step 1: send an invoice with the 6 required fields
cURL
curl -X POST https://api.sltax360.com/api/v1/invoices \
  -H "Authorization: Bearer $SLTAX_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "policy_number": "DEMO-0001",
    "insured_name": "Harbor Point Marine LLC",
    "state_code": "TX",
    "net_premium": 12500.00,
    "effective_date": "2026-10-01",
    "expiration_date": "2027-10-01"
  }'

The reply is 201 Created. Your ids and times will be different:

Response: 201 Created
{
  "success": true,
  "data": {
    "id": 12832,
    "policy_number": "DEMO-0001",
    "invoice_number": null,
    "invoice_date": "2026-10-01",
    "insured_name": "Harbor Point Marine LLC",
    "state": { "id": 45, "code": "TX", "name": "Texas" },
    "type": { "id": 1, "name": "New" },
    "lob": null,
    "net_premium": 12500,
    "commission": null,
    "effective_date": "2026-10-01",
    "expiration_date": "2027-10-01",
    "status": "new",
    "filed": false,
    "paid": false,
    "created": "2026-09-23 11:55:21",
    "company": null,
    "policy_id": 6909,
    "policy_fee": 0,
    "stamps_fee": 0,
    "service_fee": 0,
    "notes": null,
    "fees": [],
    "coverages": [],
    "insurers": [],
    "documents": []
  },
  "meta": {
    "timestamp": "2026-09-23T16:55:21Z",
    "request_id": "req_070f04a750ae11ebefd69589",
    "response_time_ms": 9
  }
}
That is a real invoice in your SLTax360 account. Our team can start work on it now. Keep data.id: you use it to read the status and to add documents.
Every create stores an invoice, and a retry makes a second one, unless your account has the new features and you send an Idempotency-Key. Do your tests on a test account: get a free test key. To check a body without storing anything, send it to POST /api/v1/invoices/preview. If a create times out, check first with GET /api/v1/invoices?policy_number=DEMO-0001 before you send it again. That list shows every transaction on the policy, so compare the type, effective_date and net_premium.

The integration ladder

Climb only as far as you need. Each step works on its own.

StepWhat you doWhat you get
0. Hello GET /auth/me with your key. Proof that the key works, with its tier and rate limit.
1. First invoice POST /invoices with the 6 required fields. 201 with an invoice id. Our team does the rest.
2. Send what you have Add the address, coverages, insurers, fees and documents. See Step 2. Fewer questions from our team before we can file.
3. Use our lists Read our reference lists (states, lines of business, coverages, fee types, document types, company search) and send our exact codes and ids. Fewer codes for our team to match by hand. The lists change rarely, so you can cache them.
4. Go further POST /invoices/preview before you create, GET /invoices/{id} for status, PUT /invoices/{id}/documents/{docUUID} for late documents, POST /invoices/batch for bulk. Checks before you send, status read back, and bulk sends (Premium tier).

First invoice in your language

Each example sends the same 6-field invoice and prints the new invoice id. They all read your key from SLTAX_API_KEY.

cURL
curl -X POST https://api.sltax360.com/api/v1/invoices \
  -H "Authorization: Bearer $SLTAX_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "policy_number": "DEMO-0001",
    "insured_name": "Harbor Point Marine LLC",
    "state_code": "TX",
    "net_premium": 12500.00,
    "effective_date": "2026-10-01",
    "expiration_date": "2027-10-01"
  }'
JavaScript (Node.js 18 or later). Save as first-invoice.mjs, run: node first-invoice.mjs
const response = await fetch("https://api.sltax360.com/api/v1/invoices", {
  method: "POST",
  headers: {
    "Authorization": `Bearer ${process.env.SLTAX_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    policy_number: "DEMO-0001",
    insured_name: "Harbor Point Marine LLC",
    state_code: "TX",
    net_premium: 12500.00,
    effective_date: "2026-10-01",
    expiration_date: "2027-10-01",
  }),
});

const result = await response.json();
if (response.status === 201) {
  console.log("Created invoice", result.data.id, "status:", result.data.status);
} else {
  console.error(response.status, result.error.message, JSON.stringify(result.error.details));
}
Python 3 with requests (pip install requests)
import os
import requests

response = requests.post(
    "https://api.sltax360.com/api/v1/invoices",
    headers={"Authorization": f"Bearer {os.environ['SLTAX_API_KEY']}"},
    json={
        "policy_number": "DEMO-0001",
        "insured_name": "Harbor Point Marine LLC",
        "state_code": "TX",
        "net_premium": 12500.00,
        "effective_date": "2026-10-01",
        "expiration_date": "2027-10-01",
    },
    timeout=30,
)

result = response.json()
if response.status_code == 201:
    print("Created invoice", result["data"]["id"], "status:", result["data"]["status"])
else:
    print(response.status_code, result["error"]["message"], result["error"].get("details"))
PHP 7.4 or later with the curl extension
<?php
$payload = [
    'policy_number'   => 'DEMO-0001',
    'insured_name'    => 'Harbor Point Marine LLC',
    'state_code'      => 'TX',
    'net_premium'     => 12500.00,
    'effective_date'  => '2026-10-01',
    'expiration_date' => '2027-10-01',
];

$ch = curl_init('https://api.sltax360.com/api/v1/invoices');
curl_setopt_array($ch, [
    CURLOPT_POST           => true,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER     => [
        'Authorization: Bearer ' . getenv('SLTAX_API_KEY'),
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS     => json_encode($payload),
]);
$body   = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$result = json_decode($body, true);

if ($status === 201) {
    echo "Created invoice {$result['data']['id']} status: {$result['data']['status']}\n";
} else {
    echo "$status {$result['error']['message']}\n";
    echo json_encode($result['error']['details'] ?? null), "\n";
}
C# (.NET 8 or later, console app)
using System;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Text.Json;

var client = new HttpClient();
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(
    "Bearer", Environment.GetEnvironmentVariable("SLTAX_API_KEY"));

var payload = """
{
  "policy_number": "DEMO-0001",
  "insured_name": "Harbor Point Marine LLC",
  "state_code": "TX",
  "net_premium": 12500.00,
  "effective_date": "2026-10-01",
  "expiration_date": "2027-10-01"
}
""";

var response = await client.PostAsync(
    "https://api.sltax360.com/api/v1/invoices",
    new StringContent(payload, Encoding.UTF8, "application/json"));

var body = await response.Content.ReadAsStringAsync();
using var json = JsonDocument.Parse(body);
if ((int)response.StatusCode == 201)
{
    var data = json.RootElement.GetProperty("data");
    Console.WriteLine($"Created invoice {data.GetProperty("id")} status: {data.GetProperty("status")}");
}
else
{
    Console.WriteLine($"{(int)response.StatusCode} {body}");
}

Step 2: send what you have

All of these fields are optional. Send the ones your system has. The more you send, the less our team must ask you.

FieldWhat it isRule
policy_numberYour policy number (required).Send the same policy_number on every transaction of a policy: the new policy, each endorsement and the cancellation. That is how we link them.
type_codeThe kind of transaction.Exactly NEW, RENEWAL, ENDORSEMENT or CANCELLATION, in capitals. NEW if you leave it out. Any other value is an error. Audits, reinstatements and rewrites cannot be sent through the API yet.
insured_addressWhere the risk is: line1, line2, city, zip.The state comes from state_code. type_id is 2 (garaging, the default) or 1 (billing).
coverages[]Each coverage: code and amount (its premium).Codes differ by state, and GET /coverages does not show each code's state yet. Check your codes for a state with POST /invoices/preview. Our list does not have codes for every state: if no code fits, send your own code and a title. If you also send limits[], their premium values must add up to amount.
insurers[]The insurance companies: company_id and percent or amount.company_id is our id. Find it with GET /companies/search?q= and a name or NAIC code. One insurer with no percent means 100%.
lob_idLine of business.Our id from GET /lobs. An unknown id is an error, so leave it out if you are not sure.
fees[]Fees and taxes you billed: type and amount.Up to 25 items. Codes from GET /reference/fee-types. Create does not calculate the tax. Send the tax you billed as items too, with the code that preview returns in tax_calculation.line_items[].code (for example sl_tax and stamping_fee). To work the figures out first, use POST /invoices/preview.
invoice_number, invoice_dateYour own invoice reference and date.invoice_date is the effective date if you leave it out.
commission, policy_limit, class_codeExtra policy details.Optional. policy_limit cannot be negative. If we hold no such class_code for the state, create stores the invoice without it and returns a warning. Preview does not check class codes yet.
documents[]Files such as the declarations page: type and docUUID.A reference to a file in your own document store, not the file itself. Up to 10 per request. Talk to us first, because we must connect to your document store.

Endorsements and cancellations

  • Send the same policy_number as the original policy, with type_code ENDORSEMENT or CANCELLATION.
  • Send the date the change takes effect as effective_date, and the policy's expiration date as expiration_date. The state filing uses effective_date as the date of the change.
  • Known limit today: the API has one date pair, so the stored policy record takes these dates, and its start date then shows the date of the change. On accounts with the new features, send the policy term instead, and the date of the change in transaction_effective_date.
  • Every amount is the amount of the change, not the policy total: net_premium, coverage amounts, insurer amounts and the tax you billed on the change. Amounts that return premium are negative.
  • Use preview to work out the tax on the change. Unlike the calculator, POST /invoices/preview accepts negative amounts and returns negative tax lines for a return of premium.
  • On read-back, type.name is New, Renewal, Endorsement or Cancelled.
Example body for an endorsement that adds 1,200.00 (send it to POST /api/v1/invoices)
{
  "policy_number": "DEMO-0002",
  "insured_name": "Harbor Point Marine LLC",
  "state_code": "TX",
  "type_code": "ENDORSEMENT",
  "net_premium": 1200.00,
  "effective_date": "2027-01-15",
  "expiration_date": "2027-10-01",
  "coverages": [
    { "code": "9334", "amount": 1200.00 }
  ],
  "insurers": [
    { "company_id": 167, "percent": 100 }
  ],
  "fees": [
    { "type": "sl_tax", "amount": 58.20 },
    { "type": "stamping_fee", "amount": 0.48 }
  ]
}

A cancellation has the same shape, with "type_code": "CANCELLATION" and negative amounts, for example "net_premium": -6250.00.

Example body for step 2 (send it to POST /api/v1/invoices)
{
  "policy_number": "DEMO-0002",
  "insured_name": "Harbor Point Marine LLC",
  "state_code": "TX",
  "type_code": "NEW",
  "net_premium": 12500.00,
  "effective_date": "2026-10-01",
  "expiration_date": "2027-10-01",
  "insured_address": {
    "line1": "100 Harbor Drive",
    "city": "Galveston",
    "zip": "77550"
  },
  "coverages": [
    { "code": "9334", "amount": 12500.00 }
  ],
  "insurers": [
    { "company_id": 167, "percent": 100 }
  ],
  "fees": [
    { "type": "policy_fee_company", "amount": 150.00 }
  ]
}

The company_id above is only an example. Ids belong to your account, so always look them up with GET /companies/search.

What happens to a code we do not know. If a coverage code does not match our list, we keep your original coverage data on the invoice so our team can see it, but the coverage is not saved as a matched coverage, and it is not shown in the API reply. The reply does not warn you about this, unless your account has coverage warnings switched on. To check your codes first, send the body to POST /invoices/preview: it lists each unknown code in validation.errors.

Features on new accounts

These features are optional, and we switch them on per account. New integrations can ask for them when they request a key. On an account without them, the API ignores the extra fields and the header, exactly as described above.

FeatureWhat you sendWhat you get
Safe retriesAn Idempotency-Key header on POST /invoices: your own id for this request, up to 255 characters, no spaces.The same key and the same body within 24 hours return the first 201 again, with the header Idempotent-Replayed: true. No second invoice. The same key with a different body is a 409. A 422 does not use up the key.
Carrier by NAIC or nameinsurers[].naic or insurers[].company_name instead of company_id (also inside groups by line of business).We find the company for you. A company_id, when sent, always wins. No match is a 422 that names the value we looked for.
Line of business by namelob_name, for example "General Liability", when you do not send lob_id.We find the line of business. An unknown name is not an error: the invoice is stored without one, and warnings says so.
Transaction datetransaction_effective_date (YYYY-MM-DD) on an endorsement or cancellation. Keep effective_date and expiration_date as the policy term.We store it as the date of the change, and the policy keeps its real term. The reply shows transaction_effective_date.
The state's answerNothing extra: call GET /api/v1/invoices/{id}.A filing object: state_status (not_sent, sent, accepted, confirmed, rejected or refused), updated_at, confirmation_number, submission_number, and message with the reason for a rejection. The latest answer counts.
Coverage warningsNothing extra.warnings lists each coverage code that we could not match, so you can fix your codes.
Safe retry: send the same request again with the same key
curl -X POST https://api.sltax360.com/api/v1/invoices \
  -H "Authorization: Bearer $SLTAX_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: DEMO-0001-new-2026-10-01" \
  -d '{
    "policy_number": "DEMO-0001",
    "insured_name": "Harbor Point Marine LLC",
    "state_code": "TX",
    "net_premium": 12500.00,
    "effective_date": "2026-10-01",
    "expiration_date": "2027-10-01"
  }'
An endorsement with the new fields (send it to POST /api/v1/invoices)
{
  "policy_number": "DEMO-0002",
  "insured_name": "Harbor Point Marine LLC",
  "state_code": "TX",
  "type_code": "ENDORSEMENT",
  "net_premium": 1200.00,
  "effective_date": "2026-10-01",
  "expiration_date": "2027-10-01",
  "transaction_effective_date": "2027-01-15",
  "lob_name": "General Liability",
  "coverages": [
    { "code": "9334", "amount": 1200.00 }
  ],
  "insurers": [
    { "naic": "19437", "percent": 100 }
  ],
  "fees": [
    { "type": "sl_tax", "amount": 58.20 },
    { "type": "stamping_fee", "amount": 0.48 }
  ]
}
Part of the GET /api/v1/invoices/{id} reply, before the state has answered
"filing": {
  "state_status": "not_sent",
  "updated_at": null,
  "confirmation_number": null,
  "submission_number": null,
  "message": null,
  "api_error": null
}

Common errors and the fix

These are the errors partners really hit, taken from our request log. A 422 reply lists the problems in error.details.fields. Insurer and coverage errors are listed under insurers or coverages, and the message names the item, for example insurers[0].

MessageWhat it meansThe fix
insurers[0].amount is required and must be a numberYou grouped insurers by line of business (lob with companies), and a group has no amount.Add amount to every group.
Sum of insurers LOB amounts (10000) does not equal net_premium (12500)The group amounts do not add up to the premium.Make the group amount values add up to net_premium (1 cent tolerance).
coverages[0] (GL): amount 12500.00 does not equal the sum of its limits[].premium (10000.00)You sent limits[], and their premiums do not add up to the coverage amount.Make them add up, or leave out limits[] and send amount only.
insurers[0].company_id is required and must be an integerAn insurer has no company id.Look up the id with GET /companies/search?q= and the company name or NAIC code.
Must be one of: NEW, RENEWAL, ENDORSEMENT, CANCELLATIONtype_code is not one of the four values, or not in capitals.Send one of the four values exactly, or leave it out for NEW.
Referenced line_of_businesses not foundlob_id is not one of our ids.Use an id from GET /lobs, or leave lob_id out.
Must be a valid dateA date is not in YYYY-MM-DD form.Send dates like 2026-10-01.
Must be a valid state_codestate_code is not a US state.Send the 2-letter code, for example TX. DC is also accepted.
This field is requiredOne of the 6 required fields is missing.Send policy_number, insured_name, state_code, net_premium, effective_date and expiration_date.

On POST /invoices/preview, problems with the data itself come back in a 200 reply, inside validation.errors. The most common ones:

Preview messageWhat it meansThe fix
State PA is not supported for filingYour account has no direct electronic filing for this state.Nothing is wrong with your data. POST /invoices still accepts it. Ask us how we file that state for you.
Net premium cannot be zeroThe premium is 0.Send the real premium. POST /invoices does not block a zero premium, so check it on your side.
Coverage code 'GL' not found for state TXThe code is not in our coverage list for that state.Use a code from GET /coverages. Our list does not have codes for every state; if none fits, send your own code and a title. See the note in Step 2.

What happens after you send

  1. The invoice is stored in your SLTax360 account. Our team reviews it and fills any gaps.
  2. We prepare and submit the state filing. Direct electronic filing exists in 25+ states. In other states we prepare the filing for the state's own process. The licensed broker stays responsible for the filing.
  3. You read the status with GET /api/v1/invoices/{id}:
    • status: new, sent, success, error or re-submitted.
    • filed: true once the status is sent, success or re-submitted.
    • paid: always false today. Do not use it yet.
    • created: server time, US Central, without a time zone in the value.
The state's answer: on accounts with the new features, GET /api/v1/invoices/{id} also returns filing with the state's confirmation number or the reason for a rejection. On other accounts these are not returned (ask us, with the invoice id). Not available yet: webhooks (we do not call you when the status changes) and a list of class codes. Read the status on a schedule, for example once a day. For tests, use a free test account, or POST /invoices/preview, which stores nothing (it still counts toward your hourly limit).

Words you will see

You do not need to know insurance to connect. These are the terms the API uses.

Surplus lines
Insurance from a company that is not licensed in the state. It is allowed for risks that licensed companies do not cover. The state taxes it, and each policy must be filed with the state.
Admitted insurer
An insurance company that is licensed in the state. Its policies do not need a surplus lines filing.
Premium
The price of the policy. In the API: net_premium.
Endorsement
A change to a policy during its term, for example a new vehicle. It can add premium or return premium (a negative amount). Send it with type_code ENDORSEMENT.
Renewal and cancellation
A renewal is the next term of a policy (RENEWAL). A cancellation ends a policy early and usually returns premium (CANCELLATION).
NAIC code
A 5-digit number that identifies an insurance company in the US. Use it to find our company_id with GET /companies/search.
Stamping fee
A small fee that some states charge on top of the tax. It pays for the state office that checks the filings.
Line of business
The kind of insurance, for example general liability or commercial auto. In the API: lob_id, from GET /lobs.
Coverage code
A short code for one coverage in the policy. States use it to see what was insured. Codes differ by state.

Tools

Authentication

All API requests require a Bearer token in the Authorization header.

HTTP Header
Authorization: Bearer sltax_t2_your_api_key_here

Token Format

A key is sltax_, then the tier (t1, t2 or t3), then 32 lowercase letters and digits. The tier sets what the key can do and its hourly limit (see Rate Limits). Creating invoices needs Standard (t2) or higher. The calculator needs the calculator scope on the key. Your key also decides which SLTax360 account your data goes to.

Security: Never expose API keys in client-side code, public repositories, or browser requests. Always make API calls from your server.

Get a Free Test Key

Tax Calculator: Quick Start

Calculate surplus lines tax for a $50,000 premium in Texas with a single API call:

cURL
curl -X POST https://api.sltax360.com/api/v1/calculator/estimate \
  -H "Authorization: Bearer $SLTAX_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "state": "TX",
    "premium": 50000,
    "policy_fees": 500
  }'

The response includes an itemized breakdown of every tax and fee. Rates change over time; the reply always uses the rates in force on effective_date (today if you leave it out). This reply was captured on 2026-09-23:

Response (200 OK)
{
  "success": true,
  "data": {
    "state": { "id": 45, "code": "TX", "name": "Texas" },
    "input": { "premium": 50000, "policy_fees": 500, "effective_date": "2026-09-23" },
    "tax_base": 50500,
    "line_items": [
      { "name": "Surplus Lines Tax", "code": "sl_tax", "rate": 4.85, "rate_type": "percentage", "amount": 2449.25, "unrounded_amount": 2449.25 },
      { "name": "Stamping Fee", "code": "stamping_fee", "rate": 0.04, "rate_type": "percentage", "amount": 20.2, "unrounded_amount": 20.2 }
    ],
    "rounding_rule": "standard",
    "total_taxes_and_fees": 2469.45,
    "total_due": 52969.45,
    "special_notes": "Texas: 4.85% SL tax, 0.04% stamping fee to SLTX. Tax is on total gross premium including fees.",
    "calculated_at": "2026-09-23T12:01:38-05:00",
    "disclaimer": "Estimate only. Figures are calculated from published state rates and are not a guarantee of the amounts due. Verify with the applicable state or stamping office before filing."
  },
  "meta": {
    "timestamp": "2026-09-23T17:01:38Z",
    "request_id": "req_0daae76f3d9022291759a04f",
    "response_time_ms": 13
  }
}
Try it free: Test calculations interactively with our free online tax calculator before integrating the API.

POST /estimate

POST /api/v1/calculator/estimate Authenticated

Calculate surplus lines taxes and fees for a given state and premium amount. Returns an itemized breakdown of all applicable taxes and fees.

Request Parameters

Send as JSON request body.

Parameter Type Description
state required string 2-letter state code (e.g., "TX", "CA", "FL")
premium required number Premium amount in USD. Must be at least 0.01.
policy_fees optional number Total policy fees in USD. Defaults to 0. Some states include fees in the tax base. If fees array is provided, it takes precedence.
fees optional array Granular fee breakdown. Each item: {"type": "fee_code", "amount": 250}. When provided, the API calculates per-category tax base inclusion (e.g., Texas taxes policy fees + inspection + broker; California excludes inspection). Up to 15 items. The calculator accepts the policy fee, inspection, broker and pass-through codes in the Fee Types table below. The tax codes in that table are for invoices only.
effective_date optional string Date in YYYY-MM-DD format for rate lookup. Defaults to today. Use for historical calculations.

Response Fields

Field Type Description
state object State info: id, code, name
input object Echo of your input parameters
tax_base number Computed tax base (premium, or premium + fees depending on state rules)
line_items array Itemized list of taxes and fees. Each item has name, code, rate, rate_type, amount, unrounded_amount
rounding_rule string State rounding rule: standard, whole_dollar, or no_rounding
total_taxes_and_fees number Sum of all line item amounts
total_due number Premium + policy fees + total taxes and fees
fee_breakdown object|null Only present when fees array is provided. Contains: policy_fees_taxable, inspection_fees_taxable, broker_fees_taxable, non_taxable_fees, total_fees, total_taxable_fees
special_notes string|null State-specific notes or caveats, if any
calculated_at string ISO 8601 timestamp of the calculation
disclaimer string Always present. The figures are an estimate from published state rates.
warnings array Only present when there is something to warn about, for example a fee that does not apply in the state.

Line Item Codes

These codes appear in the response's line_items[].code. On /api/v1/calculator/estimate they are always computed by the API. On /api/v1/invoices (preview/create) they may also be submitted as fees[].type inputs to persist billed amounts verbatim. See the combined Fee / Tax Codes reference below.
Code Name Rate Type Description
sl_taxSurplus Lines TaxpercentagePrimary state surplus lines tax
stamping_feeStamping Feepercentage or flatStamping office processing fee
fire_marshalFire Marshal TaxpercentageState fire marshal assessment
figaFIGA AssessmentpercentageGuaranty association assessment
service_feeService FeepercentageState service/processing fee
surchargeState SurchargepercentageAdditional state surcharge
regulatory_feeRegulatory FeepercentageRegulatory compliance fee
slas_feeSLAS/Clearinghouse FeepercentageClearinghouse or SLAS fee
municipal_taxMunicipal TaxpercentageCity or local jurisdiction tax
flat_feeFiling FeeflatFixed dollar filing fee

Not all codes appear for every state. The response only includes line items applicable to the requested state.

Fee / Tax Codes (for fees[] array input)

All codes below are accepted as fees[].type on /api/v1/invoices (preview and create). Policy-level fees feed into the per-state tax base (e.g. Texas taxes Policy + Inspection + Broker; California excludes Inspection). Tax codes at the bottom of the table (sl_tax, stamping_fee, etc.) are accepted so callers can persist the billed amounts verbatim. The create endpoint stores fees[] as you send them; it does not recompute. One exception: in states with a flat stamping fee per policy, we may set that fee ourselves (it is 0 on an endorsement or a cancellation).

Fee Code Category Description
policy_fee_companyPolicy FeeCompany Policy Fee
policy_fee_brokerPolicy FeeBroker Policy Fee
technology_feePolicy FeeTechnology Fee
acca_duesPolicy FeeACCA Dues
reinstatement_feePolicy FeeReinstatement Fee
equipment_chargePolicy FeeEquipment Charge
assessment_feePolicy FeeAssessment Fee
vehicle_crime_feePolicy FeeVehicle Crime Fee. Not applicable to TX surplus lines (MVCPA fee requires a certificate of authority); on TX requests the amount is zeroed, excluded from tax, and a warning is returned
otherPolicy FeeOther/miscellaneous fee
inspection_feeInspection FeeInspection Fee
broker_feeBroker FeeBroker Fee
filing_feePass-throughSurplus Lines Filing Fee
clearinghouse_feePass-throughClearinghouse Fee
fire_marshall_taxPass-throughFire Marshall Tax
fslso_servicePass-throughFSLSO Service Fee
sl_taxTax (billed)Surplus Lines Tax (stored verbatim)
stamping_feeTax (billed)Stamping Fee (stored verbatim)
fire_marshalTax (billed)Fire Marshal Tax (alias of fire_marshall_tax)
figaTax (billed)FIGA Assessment
service_feeTax (billed)State Service Fee
surchargeTax (billed)State Surcharge
regulatory_feeTax (billed)Regulatory Fee
slas_feeTax (billed)SLAS Fee (alias of clearinghouse_fee)
municipal_taxTax (billed)Municipal Tax
flat_feeTax (billed)Flat Filing Fee (alias of filing_fee)

Aliases: fire_marshal/fire_marshall_tax, slas_fee/clearinghouse_fee, and flat_fee/filing_fee map to the same stored column. Send only one of each pair per request; mixing both returns 422.

Per-state tax base: Different states include different fee categories in the tax base. For example, Texas taxes Policy Fees + Inspection + Broker fees, while California excludes Inspection fees. The API handles this automatically: just provide all fees and the correct tax base is computed.
Calculator vs. invoices: /api/v1/calculator/estimate only accepts policy-level / inspection / broker / pass-through fees as inputs and always computes the tax line items. /api/v1/invoices (preview and create) additionally accepts the tax codes in fees[] so callers can persist the exact amounts they billed the insured. Amounts submitted this way are stored as you send them (except the flat stamping fee noted above) and are not checked against the calculator.

Example with fees array

Request with granular fees
{
  "state": "TX",
  "premium": 50000,
  "fees": [
    { "type": "policy_fee_company", "amount": 250 },
    { "type": "broker_fee", "amount": 150 },
    { "type": "inspection_fee", "amount": 100 },
    { "type": "filing_fee", "amount": 25 }
  ]
}

For Texas, the tax_base = $50,000 + $250 (policy) + $150 (broker) + $100 (inspection) = $50,500. The $25 filing fee is pass-through (not taxed). The response also includes a fee_breakdown object.

Response Format

All responses use a consistent JSON envelope with success, data/error, and meta fields.

Success Response

Success envelope
{
  "success": true,
  "data": { /* endpoint-specific data */ },
  "meta": {
    "timestamp": "2026-09-23T17:01:38Z",
    "request_id": "req_0daae76f3d9022291759a04f",
    "response_time_ms": 45
  }
}

Error Response

Error envelope
{
  "success": false,
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Validation failed",
    "details": { /* field-level errors when applicable */ }
  },
  "meta": { /* same as success */ }
}

Meta Object

FieldDescription
timestampISO 8601 timestamp of the response
request_idUnique request identifier for debugging. Include this in support requests.
response_time_msServer-side processing time in milliseconds

Error Codes

HTTP StatusError CodeDescription
400BAD_REQUESTThe request body is empty or is not valid JSON
401UNAUTHORIZEDMissing, wrong, revoked or expired API key
403FORBIDDENYour key cannot do this: for example the key has no calculator scope, the calculator is not enabled for your account, or a Basic (t1) key tries to create an invoice
403TIER_REQUIREDThe operation needs a higher tier, for example POST /invoices/batch needs Premium (t3)
404NOT_FOUNDEndpoint or resource not found
405METHOD_NOT_ALLOWEDWrong HTTP method (e.g., GET on POST-only endpoint)
409CONFLICTThe state has accepted the invoice (status success), so it cannot be changed or deleted. Also returned when a company with that name already exists
422VALIDATION_ERRORRequest parameters failed validation. Check details.fields.
429RATE_LIMIT_EXCEEDEDToo many requests. Retry after the period in the response.
500INTERNAL_ERRORUnexpected server error. Contact support with request_id.

Validation Error Example

422 Validation Error
{
  "success": false,
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Validation failed",
    "details": {
      "fields": {
        "state": ["Must not exceed 2 characters"],
        "premium": ["Must be at least 0.01"]
      }
    }
  }
}

Rate Limits

Each API key has an hourly limit, set by the key's API tier. The count starts again at the start of each clock hour. Every call counts, including invoice previews.

Response Headers

HeaderDescription
X-RateLimit-LimitMaximum requests allowed per hour
X-RateLimit-RemainingRequests remaining in the current window
X-RateLimit-ResetUnix timestamp when the limit resets

Limits by API Tier

TierRequests / HourWhat the key can do
t1 Basic100Read data and preview invoices
t2 Standard1,000Also create, update and delete invoices
t3 Premium5,000Also send up to 500 invoices in one batch call
Best practice: Cache results when the same state + premium + fees combination is requested repeatedly.

State-Specific Rules

Each state has unique tax rules that the API handles automatically. Here are the key concepts:

Tax Base Composition

Some states include policy fees in the tax base; others tax only the premium. The API determines this automatically based on the state, so you just provide both values.

The API automatically determines whether policy fees are included in the tax base for each state. Just provide both premium and policy_fees values.

Rounding Rules

RuleBehaviorExample
standardRound to nearest cent$1,515.456 → $1,515.46
whole_dollarRound to whole dollar$1,515.456 → $1,515.00
no_roundingNo rounding$1,515.456 stays $1,515.456

How many line items to expect

Some states charge only the surplus lines tax. Others add a stamping fee, a fire marshal tax or a flat filing fee, so a reply can have one line item or several. Rates change over time, so do not copy them into your code: read them from the API. Each reply uses the rates in force on the effective_date you send.

Calculator Code Examples

cURL
curl -X POST https://api.sltax360.com/api/v1/calculator/estimate \
  -H "Authorization: Bearer sltax_t2_your_api_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "state": "FL",
    "premium": 75000,
    "policy_fees": 250,
    "effective_date": "2026-03-01"
  }'
JavaScript (fetch)
const response = await fetch('https://api.sltax360.com/api/v1/calculator/estimate', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer sltax_t2_your_api_key_here',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    state: 'FL',
    premium: 75000,
    policy_fees: 250,
    effective_date: '2026-03-01',
  }),
});

const result = await response.json();

if (result.success) {
  console.log('Total due:', result.data.total_due);
  result.data.line_items.forEach(item =>
    console.log(`  ${item.name}: $${item.amount}`)
  );
} else {
  console.error('Error:', result.error.message);
}
Python (requests)
import requests

response = requests.post(
    "https://api.sltax360.com/api/v1/calculator/estimate",
    headers={
        "Authorization": "Bearer sltax_t2_your_api_key_here",
        "Content-Type": "application/json",
    },
    json={
        "state": "FL",
        "premium": 75000,
        "policy_fees": 250,
        "effective_date": "2026-03-01",
    },
)

result = response.json()

if result["success"]:
    data = result["data"]
    print(f"Total due: ${data['total_due']:,.2f}")
    for item in data["line_items"]:
        print(f"  {item['name']}: ${item['amount']:,.2f}")
else:
    print(f"Error: {result['error']['message']}")

Getting Started

1
Get a Test Key

Free, on a separate test account

2
Check Your Key

Call GET /auth/me with your key

3
First Invoice

Send the 6-field invoice from Send your first invoice

4
Add More Data

Climb the integration ladder as far as you need

Need help? Email us at support@sltax360.com with your request_id for fastest resolution.