Bulk pricing import integration
The Bulk Pricing API allows for asynchronous processing of large-scale price updates by sending a single CSV file containing the price updates and letting the platform process them in the background. This guide is intended for developers and integrators who implement bulk pricing update flows on the VTEX platform.
To update the price of only one SKU, use the API
This API supports two types of pricing updates: base prices (cost price, base price, and list price per SKU) and fixed prices (price-table-specific prices with optional date ranges and minimum quantities). The workflow follows an async pattern where you start an import job, upload your CSV to a pre-signed URL, and then poll for status until processing completes.
The main actors involved are:
- External pricing system (ERP or custom integration): Prepares the CSV file and orchestrates the import workflow.
- Bulk Pricing API: Receives the import request, provides the upload URL, and processes the pricing data.
- Cloud storage: Hosts the pre-signed URL where the CSV file is uploaded directly.
This guide covers the following integration flows:
How it works
Every bulk pricing import follows the same three-step async pattern, regardless of whether you are importing base or fixed prices:
- Your system sends a
POSTrequest to the Import prices endpoint, specifying the import type (base-pricesorfixed-prices) and the file metadata (content type and size in bytes). The API returns a uniquebatchIdand a pre-signed upload URL. - Your system uploads the CSV file directly to the pre-signed URL using a
PUTrequest with the headers provided in the response. - Your system polls the Get batch status endpoint using the
batchIdto monitor processing progress. If errors occur, you retrieve the error details via the Get batch errors endpoint.
⚠️ The pre-signed upload URL has an expiration timestamp (expiresAt). You must complete the file upload before this time. If the URL expires, you need to start a new import job.
ℹ️ The pre-signed URL points directly to cloud storage. Do not replace it with the API base URL or any other endpoint.
_17sequenceDiagram_17 participant ExtSystem as External System_17 participant PricingAPI as Bulk Pricing API_17 participant Storage as Cloud Storage_17_17 ExtSystem->>PricingAPI: POST /api/price-importer/pvt/import/{importType}_17 PricingAPI-->>ExtSystem: 200 OK (batchId, pre-signed upload URL)_17 ExtSystem->>Storage: PUT CSV file to pre-signed URL_17 Storage-->>ExtSystem: 200 OK_17 loop Poll until terminal status_17 ExtSystem->>PricingAPI: GET /api/price-importer/pvt/batches/{batchId}_17 PricingAPI-->>ExtSystem: 200 OK (status, progress)_17 end_17 alt COMPLETED_WITH_ERRORS_17 ExtSystem->>PricingAPI: GET /api/price-importer/pvt/batches/{batchId}/errors_17 PricingAPI-->>ExtSystem: 200 OK (pre-signed URL to error CSV)_17 end
Permissions
Each endpoint requires specific License Manager resources. Requests made without the proper permissions return a 403 Forbidden error.
| Endpoint | Product | Category | Resource |
|---|---|---|---|
POST Import prices | Pricing | Price List | Modify prices |
GET Get batch status | Pricing | Price List | Read prices |
GET Get batch errors | Pricing | Price List | Read prices |
⚠️ There are no predefined roles for these resources. You must create a custom role and assign the appropriate resources to it.
ℹ️ To prevent integrations from having excessive permissions, follow the best practices for managing API keys when assigning License Manager roles to integrations.
Bulk import flow
Use this unified flow to import either base or fixed prices. The high-level process is the same for both import types; differences are noted inline where relevant.
When to use this flow
- Use this flow when you need to update pricing data for many SKUs at once.
- Use base-price imports when you need to update cost, base, and optional list prices for SKUs.
- Use fixed-price imports when you need to update sales-channel-specific prices, time-bound prices (Date From / Date To), or quantity-based rules (Min Quantity).
Main endpoints involved
POST /api/price-importer/pvt/import/{importType}– Start an import job.importTypemust bebase-pricesorfixed-prices. The response includesbatchIdand pre-signed upload details.GET /api/price-importer/pvt/batches/{batchId}– Poll batch status and progress.GET /api/price-importer/pvt/batches/{batchId}/errors– Retrieve a pre-signed URL to download a CSV with row-level errors.
Step-by-step
- Prepare a CSV file following the schema for the import type (see CSV schemas below). Ensure UTF-8 encoding, a comma delimiter, and a header row.
- Call
POST /api/price-importer/pvt/import/{importType}with a JSON body containingcontentTypeandcontentLengthBytes. Optionally setoutput=emailfor email notifications.- Example
importTypevalues:base-prices— base price import.fixed-prices— fixed price import.
- Example
- Extract
batchIdandupload.urlfrom the response. - Upload the CSV to
upload.urlwithPUT. IncludeContent-Type: text/csvand any headers inupload.headers. - Poll
GET /api/price-importer/pvt/batches/{batchId}until status is a terminal state (COMPLETED,COMPLETED_WITH_ERRORS,FAILED). - If
COMPLETED_WITH_ERRORS, call the errors endpoint to download a CSV withError CodeandError Messagecolumns, fix failing rows, and re-submit them as needed.
ℹ️ The upload URL expires at upload.expiresAt. If it expires before you upload, start a new import job.
Request examples
Start a base price import:
_10curl -X POST \_10 "https://{accountName}.vtexcommercestable.com.br/api/price-importer/pvt/import/base-prices?output=email" \_10 -H "Content-Type: application/json" \_10 -H "Accept: application/json" \_10 -H "X-VTEX-API-AppKey: {appKey}" \_10 -H "X-VTEX-API-AppToken: {appToken}" \_10 -d '{_10 "contentType": "text/csv",_10 "contentLengthBytes": 524288000_10 }'
Start a fixed price import:
_10curl -X POST \_10 "https://{accountName}.vtexcommercestable.com.br/api/price-importer/pvt/import/fixed-prices?output=email" \_10 -H "Content-Type: application/json" \_10 -H "Accept: application/json" \_10 -H "X-VTEX-API-AppKey: {appKey}" \_10 -H "X-VTEX-API-AppToken: {appToken}" \_10 -d '{_10 "contentType": "text/csv",_10 "contentLengthBytes": 10485760_10 }'
Generic response example (start import)
_12{_12 "batchId": "550e8400-e29b-41d4-a716-446655440000",_12 "status": "AWAITING_UPLOAD",_12 "upload": {_12 "method": "PUT",_12 "url": "https://pricing-bucket.s3.amazonaws.com/imports/550e8400.csv?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Expires=600",_12 "headers": {_12 "Content-Type": "text/csv"_12 },_12 "expiresAt": "2026-01-12T20:10:00Z"_12 }_12}
Upload the CSV file:
_10curl -X PUT \_10 "https://pricing-bucket.s3.amazonaws.com/imports/550e8400.csv?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Expires=600" \_10 -H "Content-Type: text/csv" \_10 --data-binary @your-import-file.csv
CSV schemas and examples
Base prices CSV schema
| Field | Type | Required | Description | Example |
|---|---|---|---|---|
| SKU ID | String | Yes | SKU identifier. | SKU-12345 |
| Cost Price | Decimal | Yes | Cost price of the item. | 10.00 |
| Base Price | Decimal | Yes | Base price of the item. | 25.00 |
| List Price | Decimal | No | List price of the item. | 30.00 |
CSV example (base prices):
_10SKU ID,Cost Price,Base Price,List Price,Child Account_101,45.50,59.99,65,_102,47.50,62.30,70.50,ChildAccountValue
Fixed prices CSV schema
| Field | Type | Required | Description | Example |
|---|---|---|---|---|
| SKU ID | String | Yes | SKU identifier. | SKU-12345 |
| Trade Policy | String | Yes | The price table to modify. | gold |
| Price | Decimal | Yes | The fixed price of the item in the price table. | 25.00 |
| List Price | Decimal | No | List price of the item in the price table. | 30.00 |
| Min Quantity | Integer | No | Minimum quantity required to start applying this price. | 2 |
| Date From | String | No | Date from which the price applies, in ISO 8601 UTC. | 2025-07-29T18:20:37Z |
| Date To | String | No | End date for the fixed price, in ISO 8601 UTC. | 2025-08-29T18:20:37Z |
CSV example (fixed prices):
_10SKU ID,Trade Policy,Price,List Price,Min Quantity,Date From,Date To,Child Account_1025,gold,10.80,20.00,2,2025-07-29T18:20:37Z,2025-08-29T18:20:37Z,ChildAccountValue_1025,silver,11.80,,2,2025-07-29T18:20:37Z,2025-08-29T18:20:37Z,_1026,gold,15.00,17.00,,,,
Error handling
| HTTP Status | Meaning | Action |
|---|---|---|
| 200 | Batch accepted for processing. | Extract batchId and upload.url, then upload the CSV. |
| 401 | Unauthorized. | Verify your API key credentials or user token. |
| 403 | Forbidden. | Ensure your credentials have the Modify prices permission for imports and Read prices for status/errors. |
| 413 | File exceeds the 500 MB size limit. | Split your CSV into smaller files and submit separate import jobs. |
| 422 | Validation error in the file or request body. | Review row-level errors returned in the error CSV. |
| 429 | Rate limit exceeded. | Wait and retry the request after a delay. |
| 503 | Service unavailable. | Retry with exponential backoff. |
⛔ If you receive a 413 error, do not retry with the same file. Split the CSV into multiple files, each under 500 MB, and submit them as separate import jobs.
⚠️ For fixed-price imports: Ensure that Date From is earlier than Date To. Rows with invalid date ranges will fail during processing and appear in the error report.
Batch monitoring and error handling flow
After uploading a CSV file, use this flow to track the import progress and retrieve error details when processing finishes with errors.
When to use this flow
- Use this flow after uploading a CSV file to monitor the batch processing status.
- Use this flow to determine when the import has completed and whether it succeeded or had errors.
- Use this flow to download a detailed error report when the batch finishes with the
COMPLETED_WITH_ERRORSstatus.
Main endpoints involved
GET /api/price-importer/pvt/batches/{batchId}– Retrieves the current status and progress of a batch job.GET /api/price-importer/pvt/batches/{batchId}/errors– Retrieves a pre-signed URL to download the error CSV.
Status lifecycle
A batch job progresses through the following statuses:
| Status | Description |
|---|---|
AWAITING_UPLOAD | Batch created, waiting for the CSV file upload. |
RECEIVED | File received, waiting for processing to start. |
VALIDATING | An initial file validation is being performed. |
PROCESSING | The batch is actively processing rows. |
DOCUMENTING | The import results are being generated. |
COMPLETED | All rows processed successfully. |
COMPLETED_WITH_ERRORS | Processing finished, but some rows had errors. |
FAILED | Processing failed entirely. |
ℹ️ Terminal statuses are COMPLETED, COMPLETED_WITH_ERRORS, and FAILED. Stop polling once the batch reaches one of these statuses.
Step-by-step
- After uploading the CSV file, start polling the batch status endpoint using the
batchIdfrom the import response. - Check the
statusfield in each response. Continue polling until the status reaches a terminal state. - Use the
bytesProcessedandbytesTotalfields to calculate progress during thePROCESSINGstage. - If the final status is
COMPLETED, all rows were imported successfully. - If the final status is
COMPLETED_WITH_ERRORS, call the Get batch errors endpoint to download the error report. TheerrorCountfield tells you how many rows failed. - If the final status is
FAILED, the entire batch failed. Review the batch metadata and your CSV file for issues.
⚠️ Implement a polling interval with exponential backoff to avoid excessive API calls. A reasonable starting interval is 5 seconds, increasing gradually for long-running imports.
Request example – Get batch status
_10curl -X GET \_10 "https://{accountName}.vtexcommercestable.com.br/api/price-importer/pvt/batches/550e8400-e29b-41d4-a716-446655440000" \_10 -H "Accept: application/json" \_10 -H "X-VTEX-API-AppKey: {appKey}" \_10 -H "X-VTEX-API-AppToken: {appToken}"
Response example – Get batch status
_13{_13 "batchId": "550e8400-e29b-41d4-a716-446655440000",_13 "type": "base",_13 "status": "COMPLETED_WITH_ERRORS",_13 "outputs": [_13 "email:user@example.com"_13 ],_13 "bytesProcessed": 13800000,_13 "bytesTotal": 13800000,_13 "errorCount": 12,_13 "createdAt": "2026-01-12T20:00:00Z",_13 "startedAt": "2026-01-12T20:01:00Z"_13}
type: Indicates the import type (basefor base prices,fixedfor fixed prices).status: Current processing status. Here,COMPLETED_WITH_ERRORSmeans some rows failed.bytesProcessed/bytesTotal: Track upload and processing progress.errorCount: Number of rows that failed. Use this to decide whether to retrieve the error report.outputs: Configured notification channels for when the job finishes.
Request example – Get batch errors
_10curl -X GET \_10 "https://{accountName}.vtexcommercestable.com.br/api/price-importer/pvt/batches/550e8400-e29b-41d4-a716-446655440000/errors" \_10 -H "Accept: application/json" \_10 -H "X-VTEX-API-AppKey: {appKey}" \_10 -H "X-VTEX-API-AppToken: {appToken}"
Response example – Get batch errors
_10{_10 "url": "https://pricing-bucket.s3.amazonaws.com/errors/550e8400-e29b-41d4-a716-446655440000.csv?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Expires=600"_10}
The url field contains a pre-signed URL to download a CSV file with error details. This CSV has the same format as your original import file, with two additional columns: Error Code and Error Message. Download the file, review the errors, fix the affected rows, and re-submit them in a new import job.
CSV format requirements
All CSV files must follow these formatting rules:
| Requirement | Value |
|---|---|
| Encoding | UTF-8 |
| Header row | Required (first row) |
| Delimiter | Comma (,) |
| Quote character | Double quote (") |
Output notifications
Use the output query parameter in the Import prices request to configure email notifications when the job finishes:
| Value | Description |
|---|---|
email | Sends an email notification when processing completes. The target email address is configured separately in the platform settings. |