> ## Documentation Index
> Fetch the complete documentation index at: https://apidocs.taxwire.com/llms.txt
> Use this file to discover all available pages before exploring further.

# 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`.



## OpenAPI

````yaml POST /products/batch
openapi: 3.1.0
info:
  title: Taxwire Public API
  version: 0.1.0
servers: []
security: []
paths:
  /products/batch:
    post:
      tags:
        - Products
      summary: Upsert Products
      description: >-
        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`.
      operationId: batch_upsert_products_products_batch_post
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/BatchUpsertProductsRequest'
            examples:
              push:
                summary: Upsert products in two stores, and reject two
                value:
                  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
        required: true
      responses:
        '200':
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BatchUpsertProductsResponse'
              example:
                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
        '413':
          description: Request Too Large - The request body is over the size limit.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                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.
        '422':
          description: >-
            Validation Error - The request is malformed or has more than 1000
            products. An invalid product is reported as `failed` in a 200
            response instead.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                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
        '429':
          description: Rate limit exceeded. Try again later.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                errors:
                  - status: '429'
                    code: rate_limit_error
                    title: Too Many Requests
                    detail: Rate limit exceeded
          headers:
            Retry-After:
              schema:
                type: string
              description: Time in seconds until the rate limit resets
            X-RateLimit-Limit:
              schema:
                type: string
              description: The rate limit ceiling for the given request
            X-RateLimit-Remaining:
              schema:
                type: string
              description: The number of requests remaining in the time window
        '500':
          description: Internal Server Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                errors:
                  - status: '500'
                    code: system_internal_error
                    title: Internal Error
                    detail: An unexpected error occurred. Our team has been notified.
      security:
        - APIKeyHeader: []
        - HTTPBearer: []
components:
  schemas:
    BatchUpsertProductsRequest:
      properties:
        data:
          items:
            $ref: '#/components/schemas/CreateProductRequest'
          type: array
          maxItems: 1000
          title: Data
          description: >-
            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](/api-reference/products/create), 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.
      additionalProperties: false
      type: object
      required:
        - data
      title: BatchUpsertProductsRequest
      description: Upsert products request model.
    BatchUpsertProductsResponse:
      properties:
        object:
          type: string
          title: Object
          description: Always `batch`.
          default: batch
        summary:
          $ref: '#/components/schemas/BatchSummary'
        data:
          items:
            $ref: '#/components/schemas/BatchProductResult'
          type: array
          title: Data
          description: Results in the same order as the request.
      type: object
      required:
        - summary
        - data
      title: BatchUpsertProductsResponse
      description: Upsert products response model.
    ErrorResponse:
      properties:
        errors:
          items:
            $ref: '#/components/schemas/Error'
          type: array
          title: Errors
      type: object
      required:
        - errors
      title: ErrorResponse
      description: Standardized JSON:API error response model.
    CreateProductRequest:
      properties:
        source_id:
          type: string
          title: Source Id
          description: >-
            Your identifier for the product in the system it comes from (a SKU,
            a Stripe product id). Non-blank.
          examples:
            - SKU-123
        source:
          type: string
          enum:
            - business_central
            - campfire
            - chargebee
            - maxio
            - measure
            - monetizenow
            - netsuite
            - quickbooks
            - ramp
            - rillet
            - salesbricks
            - shopify
            - stripe
            - taxwire
            - xero
          description: The system the product comes from. Defaults to `taxwire`.
          default: TAXWIRE
          examples:
            - taxwire
        source_account_id:
          type: string
          title: Source Account Id
          description: >-
            The account the product belongs to within `source`. Omit when your
            org has a single account on that source — it resolves automatically.
            Sources with no account concept (`taxwire`, MonetizeNow, Maxio) must
            leave it `__default__`.
          default: __default__
          examples:
            - __default__
        name:
          type: string
          title: Name
          description: The product's name. Non-blank.
          examples:
            - Organic Coffee Beans
        description:
          anyOf:
            - type: string
            - type: 'null'
          title: Description
          description: The product's description.
          examples:
            - 12 oz bag of roasted coffee beans
        source_category:
          anyOf:
            - type: string
            - type: 'null'
          title: Source Category
          description: >-
            The product's category in the source system. Not a tax code and
            never used in a calculation.
          examples:
            - Food & Beverage > Coffee
        tax_codes:
          items:
            type: string
          type: array
          title: Tax Codes
          description: The tax codes to assign. Empty means the company default applies.
          examples:
            - - TANGIBLE_PERSONAL_PROPERTY.FOOD
        source_updated_at:
          anyOf:
            - type: string
              format: date-time
            - type: 'null'
          title: Source Updated At
          description: When the product last changed in the source system.
          examples:
            - '2026-09-14T19:58:12Z'
      additionalProperties: false
      type: object
      required:
        - source_id
        - name
      title: CreateProductRequest
      description: The fields to create a product with.
    BatchSummary:
      properties:
        created:
          type: integer
          title: Created
          default: 0
        updated:
          type: integer
          title: Updated
          default: 0
        stale:
          type: integer
          title: Stale
          default: 0
        deleted:
          type: integer
          title: Deleted
          default: 0
        failed:
          type: integer
          title: Failed
          default: 0
      type: object
      title: BatchSummary
      description: Number of items with each result.
    BatchProductResult:
      properties:
        index:
          type: integer
          title: Index
          description: Position of the product in the request, starting at 0.
          examples:
            - 0
        status:
          type: string
          enum:
            - created
            - updated
            - stale
            - deleted
            - failed
          title: Status
          description: >-
            Result for the product: `created`, `updated`, `stale` (ignored
            because its `source_updated_at` is older than the current one),
            `deleted` (matched a deleted product, so nothing changed), or
            `failed` (see `errors`; nothing was changed).
          examples:
            - created
        product:
          anyOf:
            - $ref: '#/components/schemas/ProductResource'
            - type: 'null'
          description: >-
            Product as stored after the request. Set for `created`, `updated`,
            and `stale`.
        errors:
          anyOf:
            - items:
                $ref: '#/components/schemas/Error'
              type: array
            - type: 'null'
          title: Errors
          description: >-
            Why the product failed. Set for `failed`. Each error's
            `source.pointer` names the field to fix, for example
            `/data/attributes/data/2/name`.
          examples:
            - null
      type: object
      required:
        - index
        - status
      title: BatchProductResult
      description: Result for one product in the request.
    Error:
      properties:
        status:
          type: string
          title: Status
        code:
          type: string
          title: Code
        title:
          type: string
          title: Title
        detail:
          type: string
          title: Detail
        source:
          anyOf:
            - $ref: '#/components/schemas/ErrorSource'
            - type: 'null'
        meta:
          anyOf:
            - additionalProperties: true
              type: object
            - type: 'null'
          title: Meta
      type: object
      required:
        - status
        - code
        - title
        - detail
      title: Error
      description: Individual JSON:API error object.
    ProductResource:
      properties:
        id:
          type: string
          title: Id
          description: Taxwire's stable identifier for the product.
          examples:
            - prd_01J9Z8Q4E7K2M3N4P5R6S7T8U9
        source_id:
          type: string
          title: Source Id
          description: >-
            Your identifier for the product in the system it came from (a SKU, a
            Stripe product id).
          examples:
            - SKU-123
            - prd_2344536
        source:
          type: string
          enum:
            - business_central
            - campfire
            - chargebee
            - maxio
            - measure
            - monetizenow
            - netsuite
            - quickbooks
            - ramp
            - rillet
            - salesbricks
            - shopify
            - stripe
            - taxwire
            - xero
          description: >-
            The system the product came from. `taxwire` for products you send
            directly to this API.
          examples:
            - taxwire
            - stripe
        source_account_id:
          type: string
          title: Source Account Id
          description: >-
            The account the product belongs to within `source`, for sources that
            have more than one. `__default__` when the source has a single
            account.
          examples:
            - acct_234234
        name:
          type: string
          title: Name
          description: The product's name.
          examples:
            - Organic Coffee Beans
        description:
          anyOf:
            - type: string
            - type: 'null'
          title: Description
          description: The product's description, if any.
          examples:
            - 12 oz bag of roasted coffee beans
        source_category:
          anyOf:
            - type: string
            - type: 'null'
          title: Source Category
          description: >-
            The product's category in the source system, kept for
            identification. Not a tax code and never used in a calculation.
          examples:
            - Food & Beverage > Coffee
        tax_codes:
          items:
            type: string
          type: array
          title: Tax Codes
          description: >-
            The tax codes you assigned to the product. Empty means no
            product-level assignment, so the organization default applies.
          examples:
            - - TANGIBLE_PERSONAL_PROPERTY.FOOD
        resolved_tax_codes:
          items:
            type: string
          type: array
          title: Resolved Tax Codes
          description: >-
            The tax codes actually used for the product during a calculation:
            `tax_codes` when set, otherwise your organization default. Never
            empty.
          examples:
            - - TANGIBLE_PERSONAL_PROPERTY
        source_updated_at:
          anyOf:
            - type: string
              format: date-time
            - type: 'null'
          title: Source Updated At
          description: >-
            When the product last changed in the source system, as reported by
            the sender. Null when unknown.
          examples:
            - '2026-09-14T19:58:12Z'
        created_at:
          type: string
          format: date-time
          title: Created At
          description: When Taxwire first stored the product.
          examples:
            - '2026-09-14T20:00:00Z'
        updated_at:
          type: string
          format: date-time
          title: Updated At
          description: >-
            When the product was last changed in Taxwire. Equals `created_at`
            until the first change.
          examples:
            - '2026-09-14T20:00:00Z'
      additionalProperties: false
      type: object
      required:
        - id
        - source_id
        - source
        - source_account_id
        - name
        - resolved_tax_codes
        - created_at
        - updated_at
      title: ProductResource
      description: A product in your Taxwire catalog and the tax codes assigned to it.
    ErrorSource:
      properties:
        pointer:
          anyOf:
            - type: string
            - type: 'null'
          title: Pointer
      type: object
      title: ErrorSource
      description: Identifies the source of the error, e.g., via a JSON Pointer.
  securitySchemes:
    APIKeyHeader:
      type: apiKey
      in: header
      name: X-API-Key
    HTTPBearer:
      type: http
      scheme: bearer

````

This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.