---
title: Using the Sync API | Shopware Community Hub
description: >-
  Learn how the Sync API is used for high-performance bulk operations and data
  synchronization.
canonical_url: 'https://hub.shopware.com/learn/unit/using-the-sync-api'
---

# Using the Sync API

<LearningObjectives>

- Understand the purpose of the Sync API and when it should be used instead of other APIs.
- Learn how to bulk operations work using the Sync API payload structure.
- Use common sync patterns to efficiently process and synchronize large amount of shop data.
  
</LearningObjectives>

# Using the Sync API

Welcome to the comprehensive guide to the Shopware Sync API! This powerful interface is your gateway to efficient bulk data operations, enabling you to handle massive datasets and complex synchronization tasks with remarkable performance.

## Understanding the Sync API's Power

The Sync API is specifically designed for high-performance bulk operations, making it the ideal choice when you need to process large amounts of data efficiently. Unlike the Admin and Store APIs which excel at single-record operations, the Sync API is built for scale:

- **Bulk Operations**: Process thousands of records in a single request
- **Data Synchronization**: Keep your Shopware data in sync with external systems
- **Mass Updates**: Update multiple entities simultaneously
- **ETL Processes**: Extract, transform, and load data efficiently
- **Migration Support**: Handle large-scale data migrations seamlessly
- **Performance Optimization**: Achieve maximum throughput for data operations

## When to Use the Sync API

The Sync API shines in specific scenarios where traditional APIs would be inefficient:

- **Product Catalog Imports**: Import thousands of products from external systems
- **Customer Data Synchronization**: Sync customer databases with CRM systems
- **Inventory Updates**: Bulk update stock levels across multiple products
- **Price Synchronization**: Update pricing from ERP or PIM systems
- **Category Management**: Bulk create or update product categories
- **Media Asset Management**: Import large numbers of product images

## Typical Use Case: ERP-to-Shopware Synchronization

In many real-world setups, an ERP System is the leading system for managing product data. In such scenarios, the Sync API is commonly used to:

- Regularly synchronize products from the ERP system to Shopware
- Update stock levels in bulk
- Apply price changes efficiently
- Keep Shopware aligned with the external source of truth

### Example: ERP-to-Shopware Synchronization

Let's see a small example of how a sync service can be implemented in PHP:

```php
<?php declare(strict_types=1);

use Symfony\Component\HttpFoundation\Response;
use Symfony\Contracts\HttpClient\HttpClientInterface;
use RuntimeException;
use Throwable;

class MyProductSyncService
{
    public function __construct(
        private readonly HttpClientInterface $client    
    ) {
    }
    
    public function syncProducts(string $baseUrl, string $bearerToken, array $products): array
    {
        if (true === empty($baseUrl)) {
            throw new RuntimeException('Base URL is required.');        
        }
        
        if (true === empty($bearerToken)) {
            throw new RuntimeException('Bearer token is required.');
        }
        
        if (true === empty($products)) {
            throw new RuntimeException('Products are required.');
        }
        
        try {
            $response = $this->client->request('POST', $baseUrl . '/api/_action/sync', [
              'headers' => [
                  'Accept'        => 'application/json',
                  'Content-Type'  => 'application/json',
                  'Authorization' => 'Bearer ' . $bearerToken,
              ],
              'json' => [
                  'sync-products-from-erp' => [
                      'entity'  => 'product',
                      'action'  => 'upsert',
                      'payload' => $products
                  ]
              ]
            ]);
            
            $statusCode = $response->getStatusCode();
            if ($statusCode < Response::HTTP_OK || $statusCode >= 300) {
                throw new RuntimeException(
                    sprintf('Unexpected response status code: %s.', $statusCode)
                );
            }
            
            return $response->toArray(); // To inspect response and errors; otherwise make this method return void
        } catch(Throwable $e) {
            throw new RuntimeException(
                sprintf('Failed to sync products: %s.', $e->getMessage())
            );
        }
    }
}
```

In this example, we sync products by sending a POST request to the `/api/_action/sync` endpoint with the following payload:

```txt
'sync-products-from-erp' => [
  'entity'  => 'product',
  'action'  => 'upsert',
  'payload' => $products
]
```

- The `sync-products-from-erp` is an operation name that you can choose freely. It's just a key used to identify the operation in the response.
- The action `upsert` means that we want to create or update existing records.
- The `payload` contains the product data to be synced.

## Core Concepts: Understanding Sync Operations

### The Sync Endpoint

The Sync API operates through a single, powerful endpoint:

```http
POST /api/_action/sync
```

This endpoint accepts a comprehensive payload that can handle multiple entity types and operations simultaneously.

### Operation Types

The Sync API supports two main operation types:

1. **Upsert Operations**: Create new entities or update existing ones
2. **Delete Operations**: Remove entities from the system

### Payload Structure

The sync payload follows a specific structure where each operation is defined as an object with three required properties:

- **`entity`**: The entity name in snake_case (e.g., `product`, `academy_product_note`)
- **`action`**: The operation type (`upsert` or `delete`)
- **`payload`**:
  - For `upsert`: An array of multiple records with their data
  - For `delete`: An array of IDs to delete

**Operation Keys:**

Each operation uses a unique key (e.g., `"write-product"`, `"operation-1"`). This key is just an identifier used in the response to track results.

```json
{
  "write-product": {
    "entity": "product",
    "action": "upsert",
    "payload": [ ... ]
  },
  "delete-product": {
    "entity": "product",
    "action": "delete",
    "payload": [ ... ]
  }
}
```

## Basic Sync Operations: Getting Started

### Simple Product Creation

Let's start with a basic example of creating multiple products using the `upsert` action:

```bash
curl -X POST "https://your-shop.com/api/_action/sync" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "create-products": {
      "entity": "product",
      "action": "upsert",
      "payload": [
        {
          "name": "Developer T-Shirt",
          "productNumber": "DEV-TSHIRT-001",
          "description": "Comfortable shirt for developers",
          "price": [
            {
              "currencyId": "CURRENCY_UUID",
              "net": 19.99,
              "gross": 23.99,
              "linked": false
            }
          ],
          "stock": 100,
          "taxId": "TAX_UUID"
        },
        {
          "name": "Programmer Hoodie",
          "productNumber": "DEV-HOODIE-001",
          "description": "Warm hoodie for coding sessions",
          "price": [
            {
              "currencyId": "CURRENCY_UUID",
              "net": 39.99,
              "gross": 47.99,
              "linked": false
            }
          ],
          "stock": 50,
          "taxId": "TAX_UUID"
        }
      ]
    }
  }'
```

### Bulk Product Updates

Update multiple products simultaneously by providing their IDs in the payload:

```bash
curl -X POST "https://your-shop.com/api/_action/sync" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "update-products": {
      "entity": "product",
      "action": "upsert",
      "payload": [
        {
          "id": "PRODUCT_UUID_1",
          "stock": 75,
          "price": [
            {
              "currencyId": "CURRENCY_UUID",
              "net": 24.99,
              "gross": 29.99,
              "linked": false
            }
          ]
        },
        {
          "id": "PRODUCT_UUID_2",
          "stock": 120,
          "active": true
        }
      ]
    }
  }'
```

## Advanced Sync Operations: Complex Scenarios

### Multi-Entity Operations

The Sync API excels at handling operations across multiple entity types in a single request:

```bash
curl -X POST "https://your-shop.com/api/_action/sync" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "create-categories": {
      "entity": "category",
      "action": "upsert",
      "payload": [
        {
          "name": "Developer Gear",
          "parentId": "PARENT_CATEGORY_UUID"
        }
      ]
    },
    "create-products": {
      "entity": "product",
      "action": "upsert",
      "payload": [
        {
          "name": "Code Editor Mug",
          "productNumber": "DEV-MUG-001",
          "categoryId": "NEW_CATEGORY_UUID",
          "price": [
            {
              "currencyId": "CURRENCY_UUID",
              "net": 9.99,
              "gross": 11.99,
              "linked": false
            }
          ],
          "stock": 200,
          "taxId": "TAX_UUID"
        }
      ]
    }
  }'
```

### Upsert Operations

Use upsert operations when you want to create new records or update existing ones. If an ID is provided, it updates; otherwise, it creates:

```bash
curl -X POST "https://your-shop.com/api/_action/sync" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "upsert-products": {
      "entity": "product",
      "action": "upsert",
      "payload": [
        {
          "productNumber": "DEV-TSHIRT-001",
          "name": "Updated Developer T-Shirt",
          "description": "Enhanced comfort and style",
          "price": [
            {
              "currencyId": "CURRENCY_UUID",
              "net": 22.99,
              "gross": 27.99,
              "linked": false
            }
          ],
          "stock": 150,
          "taxId": "TAX_UUID"
        }
      ]
    }
  }'
```

### Delete Operations

Remove multiple entities efficiently. Note that for delete operations, the payload is an array of IDs (strings):

```bash
curl -X POST "https://your-shop.com/api/_action/sync" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "delete-products": {
      "entity": "product",
      "action": "delete",
      "payload": [
        "PRODUCT_UUID_1",
        "PRODUCT_UUID_2"
      ]
    }
  }'
```

## Working with Custom Entities: ProductNotes Integration

The Sync API works seamlessly with custom entities from plugins like AcademyProductNotes:

### Bulk Product Notes Operations

Create multiple product notes in a single request:

```bash
curl -X POST "https://your-shop.com/api/_action/sync" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "create-notes": {
      "entity": "academy_product_note",
      "action": "upsert",
      "payload": [
        {
          "productId": "PRODUCT_UUID_1",
          "userName": "Admin User",
          "note": "Product requires inventory review",
          "solved": false
        },
        {
          "productId": "PRODUCT_UUID_2",
          "userName": "Quality Team",
          "note": "Quality check completed",
          "solved": true
        },
        {
          "productId": "PRODUCT_UUID_3",
          "userName": "Marketing Team",
          "note": "Ready for promotion campaign",
          "solved": false
        }
      ]
    }
  }'
```

### Mixed Operations with Custom Entities

Combine custom entity operations with core entities:

```bash
curl -X POST "https://your-shop.com/api/_action/sync" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "create-products": {
      "entity": "product",
      "action": "upsert",
      "payload": [
        {
          "name": "New Developer Product",
          "productNumber": "DEV-NEW-001",
          "price": [
            {
              "currencyId": "CURRENCY_UUID",
              "net": 29.99,
              "gross": 35.99,
              "linked": false
            }
          ],
          "stock": 100,
          "taxId": "TAX_UUID"
        }
      ]
    },
    "create-notes": {
      "entity": "academy_product_note",
      "action": "upsert",
      "payload": [
        {
          "productId": "NEW_PRODUCT_UUID",
          "userName": "Product Manager",
          "note": "Initial product setup completed",
          "solved": true
        }
      ]
    }
  }'
```

## Performance Optimization: Maximizing Sync Efficiency

### Batch Size Considerations

Optimal batch sizes depend on your data complexity and server resources:

```bash
# For simple entities (like product notes)
curl -X POST "https://your-shop.com/api/_action/sync" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "upsert-notes": {
      "entity": "academy_product_note",
      "action": "upsert",
      "payload": [
        // Process 100-500 notes per batch for optimal performance
      ]
    }
  }'

# For complex entities (like products with associations)
curl -X POST "https://your-shop.com/api/_action/sync" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "upsert-products": {
      "entity": "product",
      "action": "upsert",
      "payload": [
        // Process 50-200 products per batch for complex entities
      ]
    }
  }'
```

### Parallel Processing

Implement parallel processing for large datasets:

```bash
#!/bin/bash
# Process large datasets in parallel batches

# Split your data into chunks and process simultaneously
for i in {0..9}; do
  curl -X POST "https://your-shop.com/api/_action/sync" \
    -H "Authorization: Bearer $TOKEN" \
    -H "Content-Type: application/json" \
    -d @batch_$i.json &
done

# Wait for all processes to complete
wait
echo "All batches processed successfully"
```

## Error Handling and Validation

### Understanding Sync Responses

The Sync API provides detailed feedback on your operations, using the operation keys you provided:

```json
{
  "data": {
    "upsert-products": [
      {
        "entities": {
          "PRODUCT_UUID_1": {
            "id": "PRODUCT_UUID_1",
            "name": "Developer T-Shirt"
          }
        },
        "errors": []
      }
    ]
  }
}
```

### Handling Sync Failures

A Sync request is executed **transactionally**. This means if a write operation inside the request throws an exception, the entire request is rolled back and no data is written.

To resolve sync failures:

1. Inspect the error response to identify which operation or payload item caused the error.
2. Fix the invalid data.
3. Retry the request, often by sending smaller batches to isolate problematic records.

<Callout title="Note" type="info">

Some delete scenarios may return `notFound` entries without turning the whole request into a hard failure.

These entries indicate that the referenced IDs did not exist and can usually be ignored or cleaned up before retrying.

</Callout>

**Implement robust error handling for sync failures:**

```bash
curl -X POST "https://your-shop.com/api/_action/sync" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "upsert-products": {
      "entity": "product",
      "action": "upsert",
      "payload": [
        {
          "id": "PRODUCT_UUID_1",
          "name": "Valid Product",
          "productNumber": "VALID-001"
        },
        {
          "id": "PRODUCT_UUID_2",
          "name": "Invalid Product",
          "productNumber": "" // This will cause an error
        }
      ]
    }
  }'
```

If the request fails, the error response helps you identify the failing operation and payload item (e.g., via operation key and path/pointer), allowing you to fix the data and retry – often by sending smaller batches to isolate problematic records.

## Real-World Implementation Examples

### ETL Pipeline for Product Import

Build a complete ETL pipeline for importing products from external systems:

```bash
#!/bin/bash
# Complete ETL pipeline example

echo "Starting product import ETL process..."

# Step 1: Extract data from external source
echo "Extracting product data..."
EXTRACTED_DATA=$(curl -s "https://external-api.com/products")

# Step 2: Transform data to Shopware format
echo "Transforming data..."
TRANSFORMED_DATA=$(echo "$EXTRACTED_DATA" | jq -r '
  .products[] | {
    name: .title,
    productNumber: .sku,
    description: .description,
    price: [{
      currencyId: "CURRENCY_UUID",
      net: (.price * 0.84),
      gross: .price,
      linked: false
    }],
    stock: .inventory,
    taxId: "TAX_UUID"
  }
' | jq -s '{
  "import-products": {
    "entity": "product",
    "action": "upsert",
    "payload": .
  }
}')

# Step 3: Loading data into Shopware
echo "Loading data into Shopware..."
RESPONSE=$(curl -s -X POST "https://your-shop.com/api/_action/sync" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d "$TRANSFORMED_DATA")

echo "Import completed. Response: $RESPONSE"
```

### Customer Data Synchronization

Sync customer data between CRM and Shopware:

```bash
#!/bin/bash
# Customer synchronization script

echo "Starting customer synchronization..."

# Get customers from CRM system
CRM_CUSTOMERS=$(curl -s "https://crm-api.com/customers" \
  -H "Authorization: Bearer $CRM_TOKEN")

# Transform and sync to Shopware
echo "$CRM_CUSTOMERS" | jq -r '
  .customers[] | {
    email: .email,
    firstName: .first_name,
    lastName: .last_name,
    phoneNumber: .phone,
    active: true
  }
' | jq -s '{
  "sync-customers": {
    "entity": "customer",
    "action": "upsert",
    "payload": .
  }
}' | \
curl -X POST "https://your-shop.com/api/_action/sync" \
  -H "Authorization: Bearer $SHOPWARE_TOKEN" \
  -H "Content-Type: application/json" \
  -d @-

echo "Customer synchronization completed"
```

### Inventory Update Automation

Automate inventory updates from warehouse management systems:

```bash
#!/bin/bash
# Automated inventory update

echo "Updating inventory levels..."

# Get current inventory from WMS
INVENTORY_DATA=$(curl -s "https://wms-api.com/inventory" \
  -H "Authorization: Bearer $WMS_TOKEN")

# Update Shopware inventory
echo "$INVENTORY_DATA" | jq -r '
  .inventory[] | {
    id: .product_id,
    stock: .available_quantity
  }
' | jq -s '{
  "update-stock": {
    "entity": "product",
    "action": "upsert",
    "payload": .
  }
}' | \
curl -X POST "https://your-shop.com/api/_action/sync" \
  -H "Authorization: Bearer $SHOPWARE_TOKEN" \
  -H "Content-Type: application/json" \
  -d @-

echo "Inventory update completed"
```

## Best Practices for Production Use

### Data Validation

Always validate your data before sending it to the Sync API:

```bash
#!/bin/bash
# Data validation example

validate_product_data() {
  local product_data="$1"
  
  # Check required fields
  if [[ -z "$(echo "$product_data" | jq -r '.name')" ]]; then
    echo "Error: Product name is required"
    return 1
  fi
  
  if [[ -z "$(echo "$product_data" | jq -r '.productNumber')" ]]; then
    echo "Error: Product number is required"
    return 1
  fi
  
  echo "Product data validation passed"
  return 0
}

# Validate before sync
if validate_product_data "$PRODUCT_DATA"; then
  curl -X POST "https://your-shop.com/api/_action/sync" \
    -H "Authorization: Bearer $TOKEN" \
    -H "Content-Type: application/json" \
    -d "$PRODUCT_DATA"
else
  echo "Validation failed, skipping sync"
  exit 1
fi
```

## Common Challenges and Solutions

### Rate Limiting and Throttling

Shopware's Sync API is protected by existing rate limiters to ensure system stability. If you exceed the allowed number of requests, the API will respond with HTTP 429 (Too Many Requests). Always implement retry logic with exponential backoff in your integration scripts to gracefully handle rate limiting and avoid service disruptions.

## Next Steps and Advanced Topics

With the Sync API fundamentals mastered, explore:

- **Webhook Integration**: Real-time notifications for sync operations
- **Scheduled Synchronization**: Automated data sync using cron jobs
- **Incremental Updates**: Sync only changed data for efficiency
- **Data Transformation**: Advanced data mapping and transformation
- **Conflict Resolution**: Handle data conflicts between systems
- **Audit Logging**: Track all sync operations for compliance

## Resources and Tools

- **API Documentation**: [https://shopware.stoplight.io/docs/admin-api/faf8f8e4e13a0-bulk-payloads](https://shopware.stoplight.io/docs/admin-api/faf8f8e4e13a0-bulk-payloads)
- **Testing Tools**: Use Postman or custom scripts for testing sync operations
- **Data Validation**: Implement JSON schema validation for your payloads
- **Monitoring**: Set up comprehensive logging and alerting for sync operations

## Summary

Great! In this learning unit, you learned:

- What the Sync API is responsible for and how it differs from the Admin API and Store API.
- When to use the Sync API for bulk data processing and integration scenarios.
- How sync operations are structured using entities, actions, and payloads.
- How to use upsert and delete operations efficiently write large datasets.
- How multiple operations and entities can be processed within a single sync request.
- Which best practices to follow regarding validation, error handling, and performance.

With this knowledge, you can safely use the Sync API to synchronize large datasets between Shopware and external systems without relying on inefficient single-entity API calls.
