Standalone Tax APIs
Use the Tax Calculations and Transactions APIs directly for shipping, tax-inclusive pricing, and more.
The standalone Tax Calculations and Tax Transactions APIs lets you calculate tax, record transactions, and handle reversals directly. Use them with PaymentIntents or when processing payments off-Stripe. The following features are available when you use these APIs.
Stripe only calculates tax in jurisdictions where you have an active tax registration. Without a registration in the customer’s location, the calculation returns zero tax. To learn more, see Understand zero tax amounts.
Calculate tax on shipping costs
To calculate tax on shipping costs, use the shipping_ parameter:
curl https://api.stripe.com/v1/tax/calculations \ -u "sk_test_BQokikJOvBiI2HlWgH4olfQ2:" \ -d currency=usd \ -d "line_items[0][amount]=1000" \ -d "line_items[0][reference]=L1" \ -d "customer_details[address][line1]=920 5th Ave" \ -d "customer_details[address][city]=Seattle" \ -d "customer_details[address][state]=WA" \ -d "customer_details[address][postal_code]=98104" \ -d "customer_details[address][country]=US" \ -d "customer_details[address_source]=shipping" \ -d "shipping_cost[amount]=500" \ -d "shipping_cost[tax_code]=txcd_92010001"
Pass the ID of an existing ShippingRate to use its amount, tax_, and tax_:
curl https://api.stripe.com/v1/tax/calculations \ -u "sk_test_BQokikJOvBiI2HlWgH4olfQ2:" \ -d currency=usd \ -d "line_items[0][amount]=1000" \ -d "line_items[0][reference]=L1" \ -d "customer_details[address][line1]=920 5th Ave" \ -d "customer_details[address][city]=Seattle" \ -d "customer_details[address][state]=WA" \ -d "customer_details[address][postal_code]=98104" \ -d "customer_details[address][country]=US" \ -d "customer_details[address_source]=shipping" \ -d "shipping_cost[shipping_rate]=shr_1Mlh8YI6rIcR421eUr9SJzAD"
Estimate taxes with an IP address
If you provide your customer’s IP address, we geolocate it and use that location as your customer’s location. Use this to show your customer a tax estimate before they provide their postal address.
Caution
Because the location of an IP address might be some distance from the actual customer location, we recommend against using an IP address to determine the final amount of tax to collect.
Collect customer tax IDs
In some cases, such as the cross-border supply of services, your customer might need to account for tax on a reverse charge basis. Instead of collecting the tax, you must issue an invoice with the text, “Tax to be paid on reverse charge basis.” This informs your customer that they’re responsible for any tax on their purchase.
Provide your customer’s tax IDs to automatically determine when reverse charge applies:
curl https://api.stripe.com/v1/tax/calculations \ -u "sk_test_BQokikJOvBiI2HlWgH4olfQ2:" \ -d currency=usd \ -d "line_items[0][amount]=1000" \ -d "line_items[0][reference]=L1" \ -d "customer_details[address][country]=IE" \ -d "customer_details[address_source]=billing" \ -d "customer_details[tax_ids][0][type]=eu_vat" \ -d "customer_details[tax_ids][0][value]=DE123456789"
If you provide a tax ID with an invalid format, the calculation returns a tax_ error code:
{ "error": { "code": "tax_id_invalid", "doc_url": "https://docs.stripe.com/error-codes#tax-id-invalid", "message": "Invalid value for eu_vat.", "param": "customer_details[tax_ids][0][value]", "type": "invalid_request_error" } }
The Tax API doesn’t automatically validate tax IDs against government databases. To validate a tax ID before calculating tax, you must use customer tax ID validation.
Use tax-inclusive pricing
By default, tax is calculated on top of the line item and shipping cost amounts you provide. To calculate the tax included in your prices, set the tax_ to inclusive for the line item or shipping cost.
In the example below, the customer always pays 100 EUR:
curl https://api.stripe.com/v1/tax/calculations \ -u "sk_test_BQokikJOvBiI2HlWgH4olfQ2:" \ -d currency=eur \ -d "line_items[0][amount]=10000" \ -d "line_items[0][reference]=L1" \ -d "line_items[0][tax_behavior]=inclusive" \ -d "line_items[0][tax_code]=txcd_10103000" \ -d "customer_details[address][country]=IE" \ -d "customer_details[address_source]=billing"
The response returns the tax included:
{ ... "amount_total": 10000, ... "tax_amount_exclusive": 0, "tax_amount_inclusive": 1870, "tax_breakdown": [ { "amount": 1870, "inclusive": true, "tax_rate_details": { "country": "IE", "percentage_decimal": "23.0", "state": null, "tax_type": "vat" }, "taxability_reason": "standard_rated", "taxable_amount": 8130 } ], ... }
Use an existing Product object
You can provide a Product object for each line item. If the product has a tax_code, we use it as the line item’s tax_, if it’s not already populated. We don’t use other product values, including the tax_ and price, during tax calculation.
curl https://api.stripe.com/v1/tax/calculations \ -u "sk_test_BQokikJOvBiI2HlWgH4olfQ2:" \ -d currency=usd \ -d "line_items[0][amount]=1000" \ -d "line_items[0][reference]=L1" \ -d "line_items[0][product]=" \ -d "customer_details[address][country]=IE" \ -d "customer_details[address_source]=billing"{{PRODUCT_ID}}
Use an existing Account or Customer
The tax calculation automatically uses the relevant Customer address and tax IDs according to the availability of customer data:
- The customer shipping address populates the calculation’s
customer_.details. address - Otherwise, the customer address populates the calculation’s
customer_.details. address - The customer IP address populates the calculation’s
customer_.details. ip_ address - If the customer has tax exemption, it populates the calculation’s
customer_.details. taxability_ override - The customer’s tax IDs populate the calculation’s
customer_.details. tax_ ids
Override customer taxability
You don’t need to collect tax in certain cases, such as when your customer is tax-exempt. You can provide the tax exemption to Stripe Tax using the taxability_override parameter.
To provide the customer taxability override to your calculations:
curl https://api.stripe.com/v1/tax/calculations \ -u "sk_test_BQokikJOvBiI2HlWgH4olfQ2:" \ -d currency=usd \ -d "line_items[0][amount]=1000" \ -d "line_items[0][reference]=L1" \ -d "customer_details[address][line1]=920 5th Ave" \ -d "customer_details[address][city]=Seattle" \ -d "customer_details[address][state]=WA" \ -d "customer_details[address][postal_code]=98104" \ -d "customer_details[address][country]=US" \ -d "customer_details[address_source]=billing" \ -d "customer_details[taxability_override]=customer_exempt"
Reverse charge
Some regions, such as the European Union, implement a “reverse charge” scheme where the customer is responsible for accounting for tax if they’re purchasing as a business. For Stripe Tax to apply the correct tax treatment, we recommend you collect Tax IDs from your customers. Sometimes you might not have your customer’s tax IDs, or you’ve separately determined that the reverse charge scheme applies. In these types of scenarios, you can use taxability_ to force Stripe Tax to apply the reverse charge scheme.
To provide the customer taxability override to your calculations:
curl https://api.stripe.com/v1/tax/calculations \ -u "sk_test_BQokikJOvBiI2HlWgH4olfQ2:" \ -d currency=eur \ -d "line_items[0][amount]=1000" \ -d "line_items[0][reference]=L1" \ -d "customer_details[address][country]=IE" \ -d "customer_details[address_source]=billing" \ -d "customer_details[taxability_override]=reverse_charge"
Specify a ship-from location
If you ship goods from a location other than your main place of business, you can provide that address for tax calculations.
To provide a ship-from location, use the ship_ parameter. In this example, the user is based in Florida, their customer is based in Springfield, IL, and the user is shipping the goods from Naperville, IL:
curl https://api.stripe.com/v1/tax/calculations \ -u "sk_test_BQokikJOvBiI2HlWgH4olfQ2:" \ -d currency=usd \ -d "line_items[0][amount]=1000" \ -d "line_items[0][reference]=L1" \ -d "line_items[0][tax_behavior]=exclusive" \ -d "line_items[0][tax_code]=txcd_99999999" \ -d "customer_details[address][city]=Springfield" \ -d "customer_details[address][state]=IL" \ -d "customer_details[address][postal_code]=62704" \ -d "customer_details[address][country]=US" \ -d "customer_details[address_source]=billing" \ -d "ship_from_details[address][city]=Naperville" \ -d "ship_from_details[address][state]=IL" \ -d "ship_from_details[address][postal_code]=60540" \ -d "ship_from_details[address][country]=US"
The response returns the calculated tax based on the shipping origin of the order (Naperville, IL) instead of the destination (Springfield, IL) or the seller’s business origin:
{ ... "amount_total": 1078, ... "tax_amount_exclusive": 78, ... "tax_breakdown": [ { "amount": 78, "inclusive": true, "tax_rate_details": { "country": "US", "percentage_decimal": "7.75", "state": "IL", "tax_type": "sales_tax" }, "taxability_reason": "standard_rated", "taxable_amount": 1000 } ], ... }
To learn more about how ship-from addresses affect tax calculation in different jurisdictions, see Use ship-from addresses.
Calculate the retail delivery fee
Stripe Tax supports calculating the retail delivery fee in Minnesota and Colorado.
The retail delivery fee is a flat government fee on physical deliveries of taxable goods. It’s triggered by the physical delivery of taxable goods and not by the shipping charge amount. This means the fee applies even when shipping is free. The fee is a fixed amount that varies by state (not a percentage).
You must add the retail delivery fee to the customer’s order total and remit it to the appropriate state tax authority. Failing to collect it doesn’t eliminate your liability.
After you add a tax registration of the state_ type in the supported states, the retail delivery fee gets calculated on tax calculations.
To calculate the retail delivery fee, call the Tax Calculations API using a physical item product tax code, such as txcd_, which represents Clothing and Footwear.
Not all physical items trigger calculation of the retail delivery fee. Refer to the state’s documentation for when the tax applies:
curl https://api.stripe.com/v1/tax/calculations \ -u "sk_test_BQokikJOvBiI2HlWgH4olfQ2:" \ -d currency=usd \ -d "line_items[0][amount]=1000" \ -d "line_items[0][reference]=L1" \ -d "line_items[0][tax_behavior]=exclusive" \ -d "line_items[0][tax_code]=txcd_30011000" \ -d "shipping_cost[amount]=400" \ -d "customer_details[address][line1]=1437 Bannock St Room 451" \ -d "customer_details[address][city]=Springfield" \ -d "customer_details[address][state]=CO" \ -d "customer_details[address][postal_code]=80202" \ -d "customer_details[address][country]=US" \ -d "customer_details[address_source]=shipping"
The response returns the calculated tax with the retail delivery fee for Colorado. It’s an additional entry in the tax_ object, with tax_ set to flat_. The fee is associated with the delivery, not individual line items, so it also appears in shipping_ rather than in line items.
{ "amount_total": 1068, ... "shipping_cost": { "amount": 0, "amount_tax": 28, "tax_behavior": "exclusive", "tax_code": "txcd_92010001" }, "tax_amount_exclusive": 68, ... "tax_breakdown": [ { "amount": 40, "inclusive": false, "tax_rate_details": { "country": "US", "flat_amount": null, "percentage_decimal": "4.0", "rate_type": "percentage", "state": "CO", "tax_type": "sales_tax" }, "taxability_reason": "standard_rated", "taxable_amount": 1000 }, ... { "amount": 28, "inclusive": false, "tax_rate_details": { "country": "US", "flat_amount": { "amount": 28, "currency": "usd" }, "percentage_decimal": "0.0", "rate_type": "flat_amount", "state": "CO", "tax_type": "retail_delivery_fee" }, "taxability_reason": "standard_rated", "taxable_amount": 0 } ] }
Provide detailed line item tax breakdowns
The top-level tax_breakdown is always returned and provides a simple breakdown that’s suitable for displaying a list of taxes at checkout or on a receipt.
You can use the taxability_reason to understand why tax isn’t applied while building your integration. For example, not_ doesn’t collect tax in the country or state where tax would be due. Adding tax registrations to your account settings tells Stripe where you’re collecting tax. If you added registration for Washington, the taxability reason displayed in your result is standard_, which indicates that the product is taxed at the standard rate.
Expand the line item tax_breakdown attribute to get a detailed breakdown, including local taxes and attributes that explain the reason for each tax.
- The
tax_field from tax_rate_details is a high-level tax type indication that might not always match the type returned in reports and transaction exports. For example, it doesn’t distinguish between US sales tax and US use tax.type - Use the
display_field from tax_rate_details in your Checkout flow to show all of the taxes. The taxes are localized based on customer location and product tax information. For example, if VAT is applied for Germany because the customer is in Germany and the product is taxed at the destination, such as txcd_10103001: Software as a service (SaaS) for business use, we showname Umsatzsteuer (USt), which is the German representation for VAT. If VAT is applied for France because the head office address is set to France and the product is taxed at the origin, such as txcd_20030000: General - Services, we showTaxe sur la valeur ajoutée (TVA), which is the French representation of VAT.
curl https://api.stripe.com/v1/tax/calculations \ -u "sk_test_BQokikJOvBiI2HlWgH4olfQ2:" \ -d currency=usd \ -d "line_items[0][amount]=1000" \ -d "line_items[0][reference]=L1" \ -d "customer_details[address][line1]=920 5th Ave" \ -d "customer_details[address][city]=Seattle" \ -d "customer_details[address][state]=WA" \ -d "customer_details[address][postal_code]=98104" \ -d "customer_details[address][country]=US" \ -d "customer_details[address_source]=shipping" \ -d "expand[0]=line_items.data.tax_breakdown"
{ ... "tax_breakdown": [ { "amount": 103, "inclusive": false, "tax_rate_details": { "country": "US", "percentage_decimal": "10.25", "state": "WA", "tax_type": "sales_tax" }, "taxability_reason": "standard_rated", "taxable_amount": 1000 } ],
Resolve invalid tax code errors
If you receive an Invalid tax code error, refer to the Product tax codes for a list of available tax codes. Then, follow these steps to resolve the issue:
Check the tax code: Make sure you’re using a valid tax code from the list of available tax codes. Common mistakes include:
- Using an empty string or
nullas the tax code - Misspelling the tax code
- Using a non-existent tax code
- Using an empty string or
Update your code: Make sure you pass a valid tax code when creating a
TaxCalculation. For example:curl https://api.stripe.com/v1/tax/calculations \ -u "sk_test_BQokikJOvBiI2HlWgH4olfQ2:" \ -d currency=usd \ -d "line_items[0][amount]=1000" \ -d "line_items[0][reference]=L1" \ -d "line_items[0][tax_code]=txcd_10000000" \ -d "customer_details[address][line1]=354 Oyster Point Blvd" \ -d "customer_details[address][city]=South San Francisco" \ -d "customer_details[address][state]=CA" \ -d "customer_details[address][postal_code]=94080" \ -d "customer_details[address][country]=US" \ -d "customer_details[address_source]=shipping"Use the default tax code: Stripe Tax uses a default tax code for calculations when a specific tax code isn’t provided for a product or in a tax calculation request. You can view and update the default value in your tax settings.
Use the API to update the default tax code:
Review your product catalog: If you use tax codes associated with products in your Stripe product catalog, make sure the tax codes are correctly assigned to your products.
Check for data inconsistencies: Make sure the tax code is correctly passed from your database or front end to your server-side code that makes the API call to Stripe.
For more accurate tax calculations, use the most specific tax code that applies to your product or service. If you’re unsure which tax code to use, consult the tax codes documentation.
If you continue to experience issues, review the Tax Settings API documentation.