Upsert products (batch)
Create or update up to 1000 products in one request. Each product is matched on its source, source account ID, and source ID, so you can safely send the same request again. Each product succeeds or fails on its own: check status on every result, and errors on the ones that failed.
curl --request POST \
--url https://api.taxwire.com/products/batch \
--header 'Content-Type: application/json' \
--header 'X-API-Key: <api-key>' \
--data '
{
"data": [
{
"source_id": "SKU-123",
"source": "shopify",
"source_account_id": "acme-us.myshopify.com",
"name": "Organic Coffee Beans",
"tax_codes": [
"TANGIBLE_PERSONAL_PROPERTY.FOOD"
],
"source_updated_at": "2026-09-15T08:30:00Z"
},
{
"source_id": "SKU-124",
"source": "shopify",
"source_account_id": "acme-ca.myshopify.com",
"name": "Ceramic Mug",
"description": "12 oz stoneware mug",
"source_updated_at": "2026-09-15T08:29:41Z"
},
{
"source_id": "SKU-125",
"source": "shopify",
"name": "Travel Tumbler"
},
{
"source_id": "SKU-126",
"source": "shopify",
"source_account_id": "acme-us.myshopify.com"
}
]
}
'import requests
url = "https://api.taxwire.com/products/batch"
payload = { "data": [
{
"source_id": "SKU-123",
"source": "shopify",
"source_account_id": "acme-us.myshopify.com",
"name": "Organic Coffee Beans",
"tax_codes": ["TANGIBLE_PERSONAL_PROPERTY.FOOD"],
"source_updated_at": "2026-09-15T08:30:00Z"
},
{
"source_id": "SKU-124",
"source": "shopify",
"source_account_id": "acme-ca.myshopify.com",
"name": "Ceramic Mug",
"description": "12 oz stoneware mug",
"source_updated_at": "2026-09-15T08:29:41Z"
},
{
"source_id": "SKU-125",
"source": "shopify",
"name": "Travel Tumbler"
},
{
"source_id": "SKU-126",
"source": "shopify",
"source_account_id": "acme-us.myshopify.com"
}
] }
headers = {
"X-API-Key": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'X-API-Key': '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({
data: [
{
source_id: 'SKU-123',
source: 'shopify',
source_account_id: 'acme-us.myshopify.com',
name: 'Organic Coffee Beans',
tax_codes: ['TANGIBLE_PERSONAL_PROPERTY.FOOD'],
source_updated_at: '2026-09-15T08:30:00Z'
},
{
source_id: 'SKU-124',
source: 'shopify',
source_account_id: 'acme-ca.myshopify.com',
name: 'Ceramic Mug',
description: '12 oz stoneware mug',
source_updated_at: '2026-09-15T08:29:41Z'
},
{source_id: 'SKU-125', source: 'shopify', name: 'Travel Tumbler'},
{
source_id: 'SKU-126',
source: 'shopify',
source_account_id: 'acme-us.myshopify.com'
}
]
})
};
fetch('https://api.taxwire.com/products/batch', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.taxwire.com/products/batch",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'data' => [
[
'source_id' => 'SKU-123',
'source' => 'shopify',
'source_account_id' => 'acme-us.myshopify.com',
'name' => 'Organic Coffee Beans',
'tax_codes' => [
'TANGIBLE_PERSONAL_PROPERTY.FOOD'
],
'source_updated_at' => '2026-09-15T08:30:00Z'
],
[
'source_id' => 'SKU-124',
'source' => 'shopify',
'source_account_id' => 'acme-ca.myshopify.com',
'name' => 'Ceramic Mug',
'description' => '12 oz stoneware mug',
'source_updated_at' => '2026-09-15T08:29:41Z'
],
[
'source_id' => 'SKU-125',
'source' => 'shopify',
'name' => 'Travel Tumbler'
],
[
'source_id' => 'SKU-126',
'source' => 'shopify',
'source_account_id' => 'acme-us.myshopify.com'
]
]
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"X-API-Key: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.taxwire.com/products/batch"
payload := strings.NewReader("{\n \"data\": [\n {\n \"source_id\": \"SKU-123\",\n \"source\": \"shopify\",\n \"source_account_id\": \"acme-us.myshopify.com\",\n \"name\": \"Organic Coffee Beans\",\n \"tax_codes\": [\n \"TANGIBLE_PERSONAL_PROPERTY.FOOD\"\n ],\n \"source_updated_at\": \"2026-09-15T08:30:00Z\"\n },\n {\n \"source_id\": \"SKU-124\",\n \"source\": \"shopify\",\n \"source_account_id\": \"acme-ca.myshopify.com\",\n \"name\": \"Ceramic Mug\",\n \"description\": \"12 oz stoneware mug\",\n \"source_updated_at\": \"2026-09-15T08:29:41Z\"\n },\n {\n \"source_id\": \"SKU-125\",\n \"source\": \"shopify\",\n \"name\": \"Travel Tumbler\"\n },\n {\n \"source_id\": \"SKU-126\",\n \"source\": \"shopify\",\n \"source_account_id\": \"acme-us.myshopify.com\"\n }\n ]\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("X-API-Key", "<api-key>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.taxwire.com/products/batch")
.header("X-API-Key", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"data\": [\n {\n \"source_id\": \"SKU-123\",\n \"source\": \"shopify\",\n \"source_account_id\": \"acme-us.myshopify.com\",\n \"name\": \"Organic Coffee Beans\",\n \"tax_codes\": [\n \"TANGIBLE_PERSONAL_PROPERTY.FOOD\"\n ],\n \"source_updated_at\": \"2026-09-15T08:30:00Z\"\n },\n {\n \"source_id\": \"SKU-124\",\n \"source\": \"shopify\",\n \"source_account_id\": \"acme-ca.myshopify.com\",\n \"name\": \"Ceramic Mug\",\n \"description\": \"12 oz stoneware mug\",\n \"source_updated_at\": \"2026-09-15T08:29:41Z\"\n },\n {\n \"source_id\": \"SKU-125\",\n \"source\": \"shopify\",\n \"name\": \"Travel Tumbler\"\n },\n {\n \"source_id\": \"SKU-126\",\n \"source\": \"shopify\",\n \"source_account_id\": \"acme-us.myshopify.com\"\n }\n ]\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.taxwire.com/products/batch")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["X-API-Key"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"data\": [\n {\n \"source_id\": \"SKU-123\",\n \"source\": \"shopify\",\n \"source_account_id\": \"acme-us.myshopify.com\",\n \"name\": \"Organic Coffee Beans\",\n \"tax_codes\": [\n \"TANGIBLE_PERSONAL_PROPERTY.FOOD\"\n ],\n \"source_updated_at\": \"2026-09-15T08:30:00Z\"\n },\n {\n \"source_id\": \"SKU-124\",\n \"source\": \"shopify\",\n \"source_account_id\": \"acme-ca.myshopify.com\",\n \"name\": \"Ceramic Mug\",\n \"description\": \"12 oz stoneware mug\",\n \"source_updated_at\": \"2026-09-15T08:29:41Z\"\n },\n {\n \"source_id\": \"SKU-125\",\n \"source\": \"shopify\",\n \"name\": \"Travel Tumbler\"\n },\n {\n \"source_id\": \"SKU-126\",\n \"source\": \"shopify\",\n \"source_account_id\": \"acme-us.myshopify.com\"\n }\n ]\n}"
response = http.request(request)
puts response.read_body{
"object": "batch",
"summary": {
"created": 1,
"updated": 1,
"stale": 0,
"deleted": 0,
"failed": 2
},
"data": [
{
"index": 0,
"status": "updated",
"product": {
"id": "prd_01J9Z8Q4E7K2M3N4P5R6S7T8U9",
"source_id": "SKU-123",
"source": "shopify",
"source_account_id": "acme-us.myshopify.com",
"name": "Organic Coffee Beans",
"description": "12 oz bag of roasted coffee beans",
"tax_codes": [
"TANGIBLE_PERSONAL_PROPERTY.FOOD"
],
"resolved_tax_codes": [
"TANGIBLE_PERSONAL_PROPERTY.FOOD"
],
"source_updated_at": "2026-09-15T08:30:00Z",
"created_at": "2026-09-14T20:00:00Z",
"updated_at": "2026-09-15T08:30:05Z"
}
},
{
"index": 1,
"status": "created",
"product": {
"id": "prd_01J9ZB7V3M8N2K4P6Q1R5S7T9W",
"source_id": "SKU-124",
"source": "shopify",
"source_account_id": "acme-ca.myshopify.com",
"name": "Ceramic Mug",
"description": "12 oz stoneware mug",
"tax_codes": [],
"resolved_tax_codes": [
"TANGIBLE_PERSONAL_PROPERTY"
],
"source_updated_at": "2026-09-15T08:29:41Z",
"created_at": "2026-09-15T08:30:05Z",
"updated_at": "2026-09-15T08:30:05Z"
}
},
{
"index": 2,
"status": "failed",
"errors": [
{
"status": "409",
"code": "conflict",
"title": "Ambiguous source_account_id",
"detail": "Your organization has 2 connected shopify accounts; send source_account_id to say which one the product belongs to.",
"source": {
"pointer": "/data/attributes/data/2/source_account_id"
},
"meta": {
"source": "shopify",
"connected_accounts": [
"acme-ca.myshopify.com",
"acme-us.myshopify.com"
]
}
}
]
},
{
"index": 3,
"status": "failed",
"errors": [
{
"status": "422",
"code": "validation_required_field",
"title": "Missing name",
"detail": "Please provide a value for the required name field",
"source": {
"pointer": "/data/attributes/data/3/name"
}
}
]
}
]
}{
"errors": [
{
"status": "413",
"code": "request_too_large",
"title": "Request Too Large",
"detail": "The request body is 5242881 bytes; this API accepts at most 5242880. Split it into smaller requests."
}
]
}{
"errors": [
{
"status": "422",
"code": "validation_error",
"title": "Validation Error",
"detail": "List should have at most 1000 items after validation, not 1001",
"source": {
"pointer": "/data/attributes/data"
}
}
]
}{
"errors": [
{
"status": "429",
"code": "rate_limit_error",
"title": "Too Many Requests",
"detail": "Rate limit exceeded"
}
]
}{
"errors": [
{
"status": "500",
"code": "system_internal_error",
"title": "Internal Error",
"detail": "An unexpected error occurred. Our team has been notified."
}
]
}Authorizations
Body
Upsert products request model.
List of products to create or update, up to 1000 products and 5 MB per request. Each product takes the same fields as Create a product, and name is always required. For an existing product, omitted fields keep their current value; send null to clear description or source_category, and [] to clear tax_codes. source_updated_at can't be null: omit it to keep the current value. If the same product appears more than once, every copy fails.
1000Show child attributes
Show child attributes
curl --request POST \
--url https://api.taxwire.com/products/batch \
--header 'Content-Type: application/json' \
--header 'X-API-Key: <api-key>' \
--data '
{
"data": [
{
"source_id": "SKU-123",
"source": "shopify",
"source_account_id": "acme-us.myshopify.com",
"name": "Organic Coffee Beans",
"tax_codes": [
"TANGIBLE_PERSONAL_PROPERTY.FOOD"
],
"source_updated_at": "2026-09-15T08:30:00Z"
},
{
"source_id": "SKU-124",
"source": "shopify",
"source_account_id": "acme-ca.myshopify.com",
"name": "Ceramic Mug",
"description": "12 oz stoneware mug",
"source_updated_at": "2026-09-15T08:29:41Z"
},
{
"source_id": "SKU-125",
"source": "shopify",
"name": "Travel Tumbler"
},
{
"source_id": "SKU-126",
"source": "shopify",
"source_account_id": "acme-us.myshopify.com"
}
]
}
'import requests
url = "https://api.taxwire.com/products/batch"
payload = { "data": [
{
"source_id": "SKU-123",
"source": "shopify",
"source_account_id": "acme-us.myshopify.com",
"name": "Organic Coffee Beans",
"tax_codes": ["TANGIBLE_PERSONAL_PROPERTY.FOOD"],
"source_updated_at": "2026-09-15T08:30:00Z"
},
{
"source_id": "SKU-124",
"source": "shopify",
"source_account_id": "acme-ca.myshopify.com",
"name": "Ceramic Mug",
"description": "12 oz stoneware mug",
"source_updated_at": "2026-09-15T08:29:41Z"
},
{
"source_id": "SKU-125",
"source": "shopify",
"name": "Travel Tumbler"
},
{
"source_id": "SKU-126",
"source": "shopify",
"source_account_id": "acme-us.myshopify.com"
}
] }
headers = {
"X-API-Key": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'X-API-Key': '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({
data: [
{
source_id: 'SKU-123',
source: 'shopify',
source_account_id: 'acme-us.myshopify.com',
name: 'Organic Coffee Beans',
tax_codes: ['TANGIBLE_PERSONAL_PROPERTY.FOOD'],
source_updated_at: '2026-09-15T08:30:00Z'
},
{
source_id: 'SKU-124',
source: 'shopify',
source_account_id: 'acme-ca.myshopify.com',
name: 'Ceramic Mug',
description: '12 oz stoneware mug',
source_updated_at: '2026-09-15T08:29:41Z'
},
{source_id: 'SKU-125', source: 'shopify', name: 'Travel Tumbler'},
{
source_id: 'SKU-126',
source: 'shopify',
source_account_id: 'acme-us.myshopify.com'
}
]
})
};
fetch('https://api.taxwire.com/products/batch', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.taxwire.com/products/batch",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'data' => [
[
'source_id' => 'SKU-123',
'source' => 'shopify',
'source_account_id' => 'acme-us.myshopify.com',
'name' => 'Organic Coffee Beans',
'tax_codes' => [
'TANGIBLE_PERSONAL_PROPERTY.FOOD'
],
'source_updated_at' => '2026-09-15T08:30:00Z'
],
[
'source_id' => 'SKU-124',
'source' => 'shopify',
'source_account_id' => 'acme-ca.myshopify.com',
'name' => 'Ceramic Mug',
'description' => '12 oz stoneware mug',
'source_updated_at' => '2026-09-15T08:29:41Z'
],
[
'source_id' => 'SKU-125',
'source' => 'shopify',
'name' => 'Travel Tumbler'
],
[
'source_id' => 'SKU-126',
'source' => 'shopify',
'source_account_id' => 'acme-us.myshopify.com'
]
]
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"X-API-Key: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.taxwire.com/products/batch"
payload := strings.NewReader("{\n \"data\": [\n {\n \"source_id\": \"SKU-123\",\n \"source\": \"shopify\",\n \"source_account_id\": \"acme-us.myshopify.com\",\n \"name\": \"Organic Coffee Beans\",\n \"tax_codes\": [\n \"TANGIBLE_PERSONAL_PROPERTY.FOOD\"\n ],\n \"source_updated_at\": \"2026-09-15T08:30:00Z\"\n },\n {\n \"source_id\": \"SKU-124\",\n \"source\": \"shopify\",\n \"source_account_id\": \"acme-ca.myshopify.com\",\n \"name\": \"Ceramic Mug\",\n \"description\": \"12 oz stoneware mug\",\n \"source_updated_at\": \"2026-09-15T08:29:41Z\"\n },\n {\n \"source_id\": \"SKU-125\",\n \"source\": \"shopify\",\n \"name\": \"Travel Tumbler\"\n },\n {\n \"source_id\": \"SKU-126\",\n \"source\": \"shopify\",\n \"source_account_id\": \"acme-us.myshopify.com\"\n }\n ]\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("X-API-Key", "<api-key>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.taxwire.com/products/batch")
.header("X-API-Key", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"data\": [\n {\n \"source_id\": \"SKU-123\",\n \"source\": \"shopify\",\n \"source_account_id\": \"acme-us.myshopify.com\",\n \"name\": \"Organic Coffee Beans\",\n \"tax_codes\": [\n \"TANGIBLE_PERSONAL_PROPERTY.FOOD\"\n ],\n \"source_updated_at\": \"2026-09-15T08:30:00Z\"\n },\n {\n \"source_id\": \"SKU-124\",\n \"source\": \"shopify\",\n \"source_account_id\": \"acme-ca.myshopify.com\",\n \"name\": \"Ceramic Mug\",\n \"description\": \"12 oz stoneware mug\",\n \"source_updated_at\": \"2026-09-15T08:29:41Z\"\n },\n {\n \"source_id\": \"SKU-125\",\n \"source\": \"shopify\",\n \"name\": \"Travel Tumbler\"\n },\n {\n \"source_id\": \"SKU-126\",\n \"source\": \"shopify\",\n \"source_account_id\": \"acme-us.myshopify.com\"\n }\n ]\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.taxwire.com/products/batch")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["X-API-Key"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"data\": [\n {\n \"source_id\": \"SKU-123\",\n \"source\": \"shopify\",\n \"source_account_id\": \"acme-us.myshopify.com\",\n \"name\": \"Organic Coffee Beans\",\n \"tax_codes\": [\n \"TANGIBLE_PERSONAL_PROPERTY.FOOD\"\n ],\n \"source_updated_at\": \"2026-09-15T08:30:00Z\"\n },\n {\n \"source_id\": \"SKU-124\",\n \"source\": \"shopify\",\n \"source_account_id\": \"acme-ca.myshopify.com\",\n \"name\": \"Ceramic Mug\",\n \"description\": \"12 oz stoneware mug\",\n \"source_updated_at\": \"2026-09-15T08:29:41Z\"\n },\n {\n \"source_id\": \"SKU-125\",\n \"source\": \"shopify\",\n \"name\": \"Travel Tumbler\"\n },\n {\n \"source_id\": \"SKU-126\",\n \"source\": \"shopify\",\n \"source_account_id\": \"acme-us.myshopify.com\"\n }\n ]\n}"
response = http.request(request)
puts response.read_body{
"object": "batch",
"summary": {
"created": 1,
"updated": 1,
"stale": 0,
"deleted": 0,
"failed": 2
},
"data": [
{
"index": 0,
"status": "updated",
"product": {
"id": "prd_01J9Z8Q4E7K2M3N4P5R6S7T8U9",
"source_id": "SKU-123",
"source": "shopify",
"source_account_id": "acme-us.myshopify.com",
"name": "Organic Coffee Beans",
"description": "12 oz bag of roasted coffee beans",
"tax_codes": [
"TANGIBLE_PERSONAL_PROPERTY.FOOD"
],
"resolved_tax_codes": [
"TANGIBLE_PERSONAL_PROPERTY.FOOD"
],
"source_updated_at": "2026-09-15T08:30:00Z",
"created_at": "2026-09-14T20:00:00Z",
"updated_at": "2026-09-15T08:30:05Z"
}
},
{
"index": 1,
"status": "created",
"product": {
"id": "prd_01J9ZB7V3M8N2K4P6Q1R5S7T9W",
"source_id": "SKU-124",
"source": "shopify",
"source_account_id": "acme-ca.myshopify.com",
"name": "Ceramic Mug",
"description": "12 oz stoneware mug",
"tax_codes": [],
"resolved_tax_codes": [
"TANGIBLE_PERSONAL_PROPERTY"
],
"source_updated_at": "2026-09-15T08:29:41Z",
"created_at": "2026-09-15T08:30:05Z",
"updated_at": "2026-09-15T08:30:05Z"
}
},
{
"index": 2,
"status": "failed",
"errors": [
{
"status": "409",
"code": "conflict",
"title": "Ambiguous source_account_id",
"detail": "Your organization has 2 connected shopify accounts; send source_account_id to say which one the product belongs to.",
"source": {
"pointer": "/data/attributes/data/2/source_account_id"
},
"meta": {
"source": "shopify",
"connected_accounts": [
"acme-ca.myshopify.com",
"acme-us.myshopify.com"
]
}
}
]
},
{
"index": 3,
"status": "failed",
"errors": [
{
"status": "422",
"code": "validation_required_field",
"title": "Missing name",
"detail": "Please provide a value for the required name field",
"source": {
"pointer": "/data/attributes/data/3/name"
}
}
]
}
]
}{
"errors": [
{
"status": "413",
"code": "request_too_large",
"title": "Request Too Large",
"detail": "The request body is 5242881 bytes; this API accepts at most 5242880. Split it into smaller requests."
}
]
}{
"errors": [
{
"status": "422",
"code": "validation_error",
"title": "Validation Error",
"detail": "List should have at most 1000 items after validation, not 1001",
"source": {
"pointer": "/data/attributes/data"
}
}
]
}{
"errors": [
{
"status": "429",
"code": "rate_limit_error",
"title": "Too Many Requests",
"detail": "Rate limit exceeded"
}
]
}{
"errors": [
{
"status": "500",
"code": "system_internal_error",
"title": "Internal Error",
"detail": "An unexpected error occurred. Our team has been notified."
}
]
}