Generating Product Feeds in CSV/XML Format for Marketplaces
Each marketplace dictates its own export format. Ozon, Wildberries, Avito, AliExpress, Amazon, Rozetka — each has its own fields, encodings, data transmission methods, and restrictions. We develop universal feed generators with adapters for specific platforms.
Typical Formats by Marketplace
| Marketplace | Format | Transmission Method |
|---|---|---|
| Ozon | Excel / CSV / API | Seller API v3 |
| Wildberries | Excel / API | Supplier API |
| Avito | XML (avito-format) | Feed URL |
| AliExpress | CSV | Seller Center |
| Amazon | CSV (Flat File) | Seller Central / MWS |
| Rozetka | YML / XML | Feed URL |
| Lamoda | CSV | SFTP |
| Leroy Merlin | XML | SFTP / API |
Universal Generator with Adapters
Architecture uses Strategy pattern: core generator is unified, adapters for each marketplace are separate classes.
interface MarketplaceFeedAdapter
{
public function getHeaders(): array;
public function transform(Product $product): array;
public function getFormat(): string; // 'csv' | 'xml' | 'xlsx'
public function getDelimiter(): string;
}
class OzonFeedAdapter implements MarketplaceFeedAdapter
{
public function getHeaders(): array
{
return [
'SKU', 'Product Name', 'Description', 'Price',
'Old Price', 'VAT', 'Quantity', 'Weight, g', 'Width, mm',
'Height, mm', 'Depth, mm', 'Images', 'Category',
'Brand', 'Barcode',
];
}
public function transform(Product $product): array
{
return [
$product->sku,
$product->name,
strip_tags($product->description),
$product->price,
$product->compare_price ?? '',
'20', // VAT 20%
$product->stock,
$product->weight_grams ?? '',
$product->width_mm ?? '',
$product->height_mm ?? '',
$product->depth_mm ?? '',
$product->images->pluck('cdn_url')->implode('; '),
$product->category?->ozon_category ?? '',
$product->brand?->name ?? '',
$product->barcode ?? '',
];
}
public function getFormat(): string { return 'csv'; }
public function getDelimiter(): string { return ';'; }
}
class AvitoCatalogAdapter implements MarketplaceFeedAdapter
{
// Avito requires XML with specific structure
public function getFormat(): string { return 'xml'; }
public function getDelimiter(): string { return ''; }
// ...
}
class UniversalFeedGenerator
{
public function generate(MarketplaceFeedAdapter $adapter, string $outputPath): void
{
if ($adapter->getFormat() === 'csv') {
$this->generateCsv($adapter, $outputPath);
} elseif ($adapter->getFormat() === 'xml') {
$this->generateXml($adapter, $outputPath);
}
}
private function generateCsv(MarketplaceFeedAdapter $adapter, string $path): void
{
$fp = fopen($path, 'w');
// UTF-8 BOM for correct opening in Excel
fwrite($fp, "\xEF\xBB\xBF");
fputcsv($fp, $adapter->getHeaders(), $adapter->getDelimiter());
Product::with(['images', 'brand', 'category'])
->where('is_active', true)
->chunk(500, function ($products) use ($fp, $adapter) {
foreach ($products as $product) {
fputcsv($fp, $adapter->transform($product), $adapter->getDelimiter());
}
});
fclose($fp);
}
}
Avito XML Feed
Avito requires specific XML format with <Ad> elements:
<?xml version="1.0" encoding="UTF-8"?>
<Ads formatVersion="3" target="Avito.ru">
<Ad>
<Id>SKU-12345</Id>
<AllowEmail>No</AllowEmail>
<Title>Nike Air Max 270 Sneakers</Title>
<Description>Product description...</Description>
<Category>Clothing, shoes, accessories</Category>
<GoodsType>Sneakers</GoodsType>
<Condition>New</Condition>
<Price>4990</Price>
<Images>
<Image url="https://cdn.example.com/product1.jpg"/>
<Image url="https://cdn.example.com/product2.jpg"/>
</Images>
<ContactPhone>+79001234567</ContactPhone>
</Ad>
</Ads>
Validation Before Submission
Mandatory checks before export:
class FeedValidator
{
public function validate(array $row, MarketplaceFeedAdapter $adapter): array
{
$errors = [];
if (empty($row[0])) {
$errors[] = 'Empty SKU';
}
if (empty($row[1]) || mb_strlen($row[1]) > 255) {
$errors[] = 'Incorrect product name';
}
if (!is_numeric($row[3]) || $row[3] <= 0) {
$errors[] = 'Incorrect price';
}
return $errors;
}
}
Feed Schedule and Delivery
- Generation by schedule via Laravel Scheduler
- Delivery by URL (direct link to file in
storage/public/feeds/) - SFTP delivery — uses
league/flysystem-sftp-v3 - Email notification on generation errors — via Laravel Notifications
Timeline
Generator with one adapter — 1–2 working days. Each additional marketplace — 0.5–1 working day (with unified data structure in catalog).







