CSV product import
Use a CSV import for catalog migrations, scheduled spreadsheet exports, or product sources without a managed commerce connector. Imports are scoped to one Upsurge site and process valid rows even when other rows fail validation.
Import from the dashboard
- Open Dashboard → Products and select Import CSV.
- Choose the destination site.
- Download the template, or prepare a UTF-8 CSV with the supported headers.
- Select a non-empty
.csvfile up to 1 GiB. - Start the import.
The browser uploads directly to platform storage in resumable 8 MiB chunks. After upload verification, Upsurge queues background processing. Keep the page open while the upload is in progress; you can leave after processing begins.
Only one CSV import can be active for a site at a time.
CSV columns
Headers are case-sensitive. Include each header at most once and do not add unsupported columns.
| Column | Required | Accepted value |
|---|---|---|
sku | Yes | Non-empty, at most 100 characters; identifies an existing product within the selected site |
name | Yes | Non-empty, at most 255 characters |
price | Yes | Non-negative number |
description | No | At most 5,000 characters |
category | No | One category, at most 100 characters |
brand | No | At most 100 characters |
tags | No | Comma-separated values; at most 20 unique tags, 50 characters each |
image | No | Valid http:// or https:// URL |
stock | No | Non-negative integer |
status | No | active, inactive, or draft |
sku,name,price,description,category,brand,tags,image,stock,status
SKU-001,Trail Shoe,89.95,Lightweight trail shoe,Footwear,Northstar,"trail,new",https://example.com/trail-shoe.jpg,24,active
SKU-002,Running Sock,12.00,,Accessories,Northstar,"running,socks",,100,draft
Quote a field when it contains commas, quotes, or line breaks. The tags value therefore usually needs CSV quotes.
Create versus update
The selected site and sku determine whether a row creates or updates a product.
- A new SKU creates a product with
USDcurrency, zero stock when omitted, anddraftstatus when omitted. - An existing SKU updates
nameandpricebecause both are required. - Blank optional cells preserve the existing field.
- A supplied optional value replaces that field; tags are trimmed and deduplicated.
- Repeated SKUs in one file are applied in row order, so the later row wins for fields it supplies.
- Every valid row refreshes product search data. Active products request refreshed personalization embeddings; inactive or draft products have active embeddings removed.
A blank optional cell means “leave unchanged,” not “delete.” Use the product editor or Product API when you need to explicitly clear a stored optional field.
Job lifecycle
| Status | Meaning |
|---|---|
uploading | The resumable file upload has not been finalized. |
queued | Upload metadata passed validation and processing is waiting to start or resume. |
processing | Rows are being parsed and upserted. |
retrying | A transient failure will be retried automatically. |
finalizing | Counts, error report, and downstream refresh work are being completed. |
completed | Every processed row succeeded. |
completed_with_errors | Valid rows were committed and one or more rows failed. |
failed | A file-level or repeated processing failure stopped the job. partial indicates whether earlier rows were committed. |
cancelling / cancelled | Cancellation was requested or completed. |
The Products dashboard shows progress, processed rows, created/updated/failed counts, and up to the first 100 error samples. When any rows fail, download the complete error CSV with row, sku, and error columns.
Cancellation does not roll back rows already committed.
Import API sequence
These endpoints require a Firebase ID token with merchant product permissions.
GET /api/v1/products/imports/template
GET /api/v1/products/imports?website_id={siteId}&limit=10
POST /api/v1/products/imports
GET /api/v1/products/imports/{jobId}
POST /api/v1/products/imports/{jobId}/complete
GET /api/v1/products/imports/{jobId}/errors
POST /api/v1/products/imports/{jobId}/cancel
The upload workflow has three steps:
- Create the import job with the exact file size.
- Upload the file to the returned resumable
upload.urlusing the returned chunk size andContent-Rangesemantics. - Call
/complete; Upsurge verifies the stored object's size and job metadata before queueing processing.
- cURL
- JavaScript
- Python
curl --request POST 'https://dashboard.upsur.ge/api/v1/products/imports' \
--header "Authorization: Bearer $FIREBASE_ID_TOKEN" \
--header 'Content-Type: application/json' \
--data '{
"website_id": "site_123",
"filename": "products.csv",
"size_bytes": 48291
}'
const response = await fetch(
'https://dashboard.upsur.ge/api/v1/products/imports',
{
method: 'POST',
headers: {
Authorization: `Bearer ${firebaseIdToken}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
website_id: 'site_123',
filename: file.name,
size_bytes: file.size,
}),
},
);
const {data} = await response.json();
import requests
response = requests.post(
"https://dashboard.upsur.ge/api/v1/products/imports",
headers={"Authorization": f"Bearer {firebase_id_token}"},
json={
"website_id": "site_123",
"filename": "products.csv",
"size_bytes": 48291,
},
timeout=30,
)
response.raise_for_status()
started = response.json()["data"]
The error-report endpoint returns a temporary redirect to a private, five-minute download URL.
Common errors
| Error | Cause and fix |
|---|---|
403 Website not found or not owned by merchant | Use a site from the authenticated workspace. |
409 An import is already active for this website | Wait for or cancel the existing site import. |
| Unsupported, missing, or duplicate headers | Start from the downloaded template and keep only supported column names. |
| Expected N columns but found M | Fix unquoted commas or inconsistent row width. |
| Upload validation fails at completion | Upload the exact file declared by size_bytes to the returned session URL. |
completed_with_errors | Download the error report, correct rejected rows, and import them again. |