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.
Tax calculator
One endpoint, itemised taxes and fees for all 50 states and DC.
Calculator quick startGet a free test key
Try the API on your own test account. Our team sets it up for you.
Get a free test keySend 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.
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.
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:
{
"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
}
}
data.id: you use it to read the status and to add documents.
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.
| Step | What you do | What 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 -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"
}'
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));
}
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
$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";
}
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.
| Field | What it is | Rule |
|---|---|---|
policy_number | Your 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_code | The 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_address | Where 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_id | Line 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_date | Your own invoice reference and date. | invoice_date is the effective date if you leave it out. |
commission, policy_limit, class_code | Extra 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_numberas the original policy, withtype_codeENDORSEMENTorCANCELLATION. - Send the date the change takes effect as
effective_date, and the policy's expiration date asexpiration_date. The state filing useseffective_dateas 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/previewaccepts negative amounts and returns negative tax lines for a return of premium. - On read-back,
type.nameisNew,Renewal,EndorsementorCancelled.
{
"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.
{
"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.
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.
| Feature | What you send | What you get |
|---|---|---|
| Safe retries | An 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 name | insurers[].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 name | lob_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 date | transaction_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 answer | Nothing 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 warnings | Nothing extra. | warnings lists each coverage code that we could not match, so you can fix your codes. |
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"
}'
{
"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 }
]
}
"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].
| Message | What it means | The fix |
|---|---|---|
insurers[0].amount is required and must be a number | You 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 integer | An 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, CANCELLATION | type_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 found | lob_id is not one of our ids. | Use an id from GET /lobs, or leave lob_id out. |
Must be a valid date | A date is not in YYYY-MM-DD form. | Send dates like 2026-10-01. |
Must be a valid state_code | state_code is not a US state. | Send the 2-letter code, for example TX. DC is also accepted. |
This field is required | One 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 message | What it means | The fix |
|---|---|---|
State PA is not supported for filing | Your 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 zero | The 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 TX | The 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
- The invoice is stored in your SLTax360 account. Our team reviews it and fills any gaps.
- 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.
- You read the status with
GET /api/v1/invoices/{id}:status:new,sent,success,errororre-submitted.filed:trueonce the status issent,successorre-submitted.paid: alwaysfalsetoday. Do not use it yet.created: server time, US Central, without a time zone in the value.
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_codeENDORSEMENT. - 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_idwithGET /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, fromGET /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
- Interactive reference: Swagger UI. Click Authorize, paste your key, then use Try it out. Every call there is real.
- OpenAPI 3.0 spec: openapi.json, to generate a client in your language.
- Postman collection: sltax360-api.postman_collection.json. It follows the ladder above. Set
api_keybefore you run it. - Help: email support@sltax360.com with the
request_idfrom the reply.
Authentication
All API requests require a Bearer token in the Authorization 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.
Tax Calculator: Quick Start
Calculate surplus lines tax for a $50,000 premium in Texas with a single API call:
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:
{
"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
}
}
POST /estimate
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
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_tax | Surplus Lines Tax | percentage | Primary state surplus lines tax |
stamping_fee | Stamping Fee | percentage or flat | Stamping office processing fee |
fire_marshal | Fire Marshal Tax | percentage | State fire marshal assessment |
figa | FIGA Assessment | percentage | Guaranty association assessment |
service_fee | Service Fee | percentage | State service/processing fee |
surcharge | State Surcharge | percentage | Additional state surcharge |
regulatory_fee | Regulatory Fee | percentage | Regulatory compliance fee |
slas_fee | SLAS/Clearinghouse Fee | percentage | Clearinghouse or SLAS fee |
municipal_tax | Municipal Tax | percentage | City or local jurisdiction tax |
flat_fee | Filing Fee | flat | Fixed 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_company | Policy Fee | Company Policy Fee |
policy_fee_broker | Policy Fee | Broker Policy Fee |
technology_fee | Policy Fee | Technology Fee |
acca_dues | Policy Fee | ACCA Dues |
reinstatement_fee | Policy Fee | Reinstatement Fee |
equipment_charge | Policy Fee | Equipment Charge |
assessment_fee | Policy Fee | Assessment Fee |
vehicle_crime_fee | Policy Fee | Vehicle 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 |
other | Policy Fee | Other/miscellaneous fee |
inspection_fee | Inspection Fee | Inspection Fee |
broker_fee | Broker Fee | Broker Fee |
filing_fee | Pass-through | Surplus Lines Filing Fee |
clearinghouse_fee | Pass-through | Clearinghouse Fee |
fire_marshall_tax | Pass-through | Fire Marshall Tax |
fslso_service | Pass-through | FSLSO Service Fee |
sl_tax | Tax (billed) | Surplus Lines Tax (stored verbatim) |
stamping_fee | Tax (billed) | Stamping Fee (stored verbatim) |
fire_marshal | Tax (billed) | Fire Marshal Tax (alias of fire_marshall_tax) |
figa | Tax (billed) | FIGA Assessment |
service_fee | Tax (billed) | State Service Fee |
surcharge | Tax (billed) | State Surcharge |
regulatory_fee | Tax (billed) | Regulatory Fee |
slas_fee | Tax (billed) | SLAS Fee (alias of clearinghouse_fee) |
municipal_tax | Tax (billed) | Municipal Tax |
flat_fee | Tax (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.
/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
{
"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": true,
"data": { /* endpoint-specific data */ },
"meta": {
"timestamp": "2026-09-23T17:01:38Z",
"request_id": "req_0daae76f3d9022291759a04f",
"response_time_ms": 45
}
}
Error Response
{
"success": false,
"error": {
"code": "VALIDATION_ERROR",
"message": "Validation failed",
"details": { /* field-level errors when applicable */ }
},
"meta": { /* same as success */ }
}
Meta Object
| Field | Description |
|---|---|
timestamp | ISO 8601 timestamp of the response |
request_id | Unique request identifier for debugging. Include this in support requests. |
response_time_ms | Server-side processing time in milliseconds |
Error Codes
| HTTP Status | Error Code | Description |
|---|---|---|
400 | BAD_REQUEST | The request body is empty or is not valid JSON |
401 | UNAUTHORIZED | Missing, wrong, revoked or expired API key |
403 | FORBIDDEN | Your 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 |
403 | TIER_REQUIRED | The operation needs a higher tier, for example POST /invoices/batch needs Premium (t3) |
404 | NOT_FOUND | Endpoint or resource not found |
405 | METHOD_NOT_ALLOWED | Wrong HTTP method (e.g., GET on POST-only endpoint) |
409 | CONFLICT | The state has accepted the invoice (status success), so it cannot be changed or deleted. Also returned when a company with that name already exists |
422 | VALIDATION_ERROR | Request parameters failed validation. Check details.fields. |
429 | RATE_LIMIT_EXCEEDED | Too many requests. Retry after the period in the response. |
500 | INTERNAL_ERROR | Unexpected server error. Contact support with request_id. |
Validation Error Example
{
"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
| Header | Description |
|---|---|
X-RateLimit-Limit | Maximum requests allowed per hour |
X-RateLimit-Remaining | Requests remaining in the current window |
X-RateLimit-Reset | Unix timestamp when the limit resets |
Limits by API Tier
| Tier | Requests / Hour | What the key can do |
|---|---|---|
t1 Basic | 100 | Read data and preview invoices |
t2 Standard | 1,000 | Also create, update and delete invoices |
t3 Premium | 5,000 | Also send up to 500 invoices in one batch call |
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.
premium and policy_fees values.
Rounding Rules
| Rule | Behavior | Example |
|---|---|---|
standard | Round to nearest cent | $1,515.456 → $1,515.46 |
whole_dollar | Round to whole dollar | $1,515.456 → $1,515.00 |
no_rounding | No 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 -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"
}'
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);
}
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
Get a Test Key
Free, on a separate test account
Check Your Key
Call GET /auth/me with your key
request_id for fastest resolution.