Developers › Docs › API reference › Import products
Import products
Creates products in bulk from a CSV or Excel file. This is the same importer as Create → Upload CSV in FLUF, including AI for missing fields and photo URLs in cells. Runs in the background and returns an import id straight away. Track it with GET /products/import/{id}.
curl -X POST "https://fluf.io/wp-json/fc/api/v1/products/import" \
-H "Authorization: Bearer fluf_pat_your_token" \
-F "[email protected]"import { openAsBlob } from "node:fs";
const form = new FormData();
form.append("file", await openAsBlob("stock.csv"), "stock.csv");
const res = await fetch("https://fluf.io/wp-json/fc/api/v1/products/import", {
method: "POST",
headers: {
"Authorization": "Bearer fluf_pat_your_token",
},
body: form,
});
const data = await res.json();import requests
res = requests.post(
"https://fluf.io/wp-json/fc/api/v1/products/import",
headers={
"Authorization": "Bearer fluf_pat_your_token",
},
files={"file": open("stock.csv", "rb")},
)
data = res.json()<?php
// composer require guzzlehttp/guzzle
$client = new GuzzleHttp\Client();
$res = $client->request('POST', 'https://fluf.io/wp-json/fc/api/v1/products/import', [
'headers' => [
'Authorization' => 'Bearer fluf_pat_your_token',
],
'multipart' => [
['name' => 'file', 'contents' => fopen('stock.csv', 'r')],
],
]);
$data = json_decode((string) $res->getBody(), true);{
"import_id": "batch_66f0c1a2b3c4d5.12345678",
"status": "pending",
"total_rows": 120,
"mapping": { "title": "Item name", "price": "Price", "images": "Photo URLs" }
}Body parameters
Send a file as multipart, or the CSV text as JSON: {"csv": "title,price,photos\n…"} (up to 20 MB).
| Field | Notes |
|---|---|
file | Multipart upload: .csv, .txt, .xlsx or .xls. Max 5,000 rows |
csv | Alternative to file: the CSV as a string |
mapping | Optional. {fluf_field: "Column header"}. Omit it and FLUF matches columns itself; the response shows what it chose |
ai | Default true. With false, rows are imported exactly as given |
Columns
FLUF fields you can map: images, title, sku, description, category, department, brand, size, colours, condition, price, cost, stock, weight.
- Every row needs at least one photo URL. A row without one fails with the reason shown in its import status.
- An
imagescell can hold several URLs separated by|or;. - Columns you don't map are kept on each product as custom fields rather than thrown away.
Re-importing
A row that matches a product you already have (same SKU or, with no SKU, the exact same title) updates that product rather than creating a duplicate. That makes it safe to import the same file again.
Something missing? Email [email protected] — we prioritise by what people actually ask for.
