Skip to main content
POST
/
v1
/
products
/
csv
Import products from CSV
curl --request POST \
  --url https://api-eu.flexportal.io/v1/products/csv \
  --header 'Authorization: Bearer <token>' \
  --header 'Content-Type: multipart/form-data' \
  --header 'Tenant-ID: <tenant-id>' \
  --form csvFile='@example-file'
{
  "success": true,
  "message": "<string>",
  "created": 123,
  "updated": 123,
  "errors": [
    {
      "row": 123,
      "error": "<string>"
    }
  ]
}

Overview

Bulk import products and variants from a CSV file. This is the fastest way to populate your catalog when onboarding or syncing from external systems.
This endpoint uses upsert behavior: existing products (matched by Product SKU) are updated, and new products are created. Variants are matched by Variant SKU.

Common Use Cases

Initial Catalog Setup

Quickly populate your catalog when first setting up FlexPortal

Supplier Updates

Import updated pricing or new products from supplier catalogs

Bulk Price Changes

Update pricing across many products at once

Inventory Sync

Periodic sync from inventory management systems

CSV Format

The CSV file must include a header row with these columns:
ColumnRequiredDescription
Product NameYesDisplay name (e.g., “MacBook Pro 16"")
Product SKUYesParent product SKU
CategoryYesProduct category
BrandYesBrand/manufacturer
SpecificationYesTechnical specs (e.g., “M3 Pro, 18GB”)
GradeNoCondition: A, B, C, D, E (default: A)
ColorNoColor option
Variant SKUYesUnique SKU for this variant
List PriceYesMSRP/retail price
Acquisition CostYesYour cost to acquire
6mo_PriceNoMonthly price for 6-month contract
12mo_PriceNoMonthly price for 12-month contract
24mo_PriceNoMonthly price for 24-month contract
36mo_PriceNoMonthly price for 36-month contract

Example CSV

Product Name,Product SKU,Category,Brand,Specification,Grade,Color,Variant SKU,List Price,Acquisition Cost,6mo_Price,12mo_Price,24mo_Price,36mo_Price
MacBook Pro 16",MACBOOK-PRO-16,Laptops,Apple,M3 Pro 18GB 512GB,A,Silver,MBP16-M3PRO-18-512-SIL-A,2499,1800,199,159,129,109
MacBook Pro 16",MACBOOK-PRO-16,Laptops,Apple,M3 Pro 18GB 512GB,A,Space Black,MBP16-M3PRO-18-512-BLK-A,2499,1800,199,159,129,109
MacBook Pro 16",MACBOOK-PRO-16,Laptops,Apple,M3 Max 64GB 1TB,A,Space Black,MBP16-M3MAX-64-1TB-BLK-A,3999,2800,319,249,199,169
iPad Pro 12.9",IPAD-PRO-12,Tablets,Apple,M2 256GB WiFi,A,Space Gray,IPAD12-M2-256-WIFI-GRY-A,1099,750,99,79,59,49
ThinkPad X1 Carbon,THINKPAD-X1,Laptops,Lenovo,i7 16GB 512GB,A,Black,X1C-I7-16-512-BLK-A,1899,1200,159,129,99,79

Making the Request

Send the CSV file in the request body with Content-Type: text/csv:
curl -X POST "https://api-eu.flexportal.io/v1/products/csv" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Tenant-ID: YOUR_TENANT_ID" \
  -H "Content-Type: text/csv" \
  --data-binary @products.csv
Or with JavaScript:
const csvContent = await fs.readFile('products.csv', 'utf-8');

const response = await fetch('https://api-eu.flexportal.io/v1/products/csv', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer YOUR_API_KEY',
    'Tenant-ID': 'YOUR_TENANT_ID',
    'Content-Type': 'text/csv'
  },
  body: csvContent
});

const result = await response.json();
console.log(`Imported: ${result.created} created, ${result.updated} updated`);

Response

{
  "success": true,
  "message": "Import completed",
  "summary": {
    "totalRows": 50,
    "productsCreated": 12,
    "productsUpdated": 3,
    "variantsCreated": 45,
    "variantsUpdated": 5,
    "errors": []
  }
}

Error Handling

If some rows fail validation, the import continues and returns errors:
{
  "success": true,
  "message": "Import completed with errors",
  "summary": {
    "totalRows": 50,
    "productsCreated": 10,
    "variantsCreated": 40,
    "errors": [
      {
        "row": 15,
        "sku": "INVALID-SKU",
        "error": "Invalid List Price: must be a positive number"
      },
      {
        "row": 23,
        "sku": "DUPLICATE-SKU",
        "error": "Variant SKU already exists on different product"
      }
    ]
  }
}

Best Practices

Validate First

Test with a small sample before importing large catalogs

Consistent SKUs

Use a consistent SKU naming convention for easy management

Include All Pricing

Provide all contract length prices you want to offer

UTF-8 Encoding

Save CSV files with UTF-8 encoding for special characters

Limits

LimitValue
Maximum file size10 MB
Maximum rows10,000
Processing timeout60 seconds
For very large catalogs (10,000+ products), split into multiple files and import in batches.

Authorizations

Authorization
string
header
required

API key obtained from FlexPortal dashboard

Headers

Tenant-ID
string
required

Your tenant identifier

Body

multipart/form-data
csvFile
file
required

CSV file with product data

Response

Import completed

success
boolean
required
message
string
required
created
number
required

Products created

updated
number
required

Products updated

errors
object[]