Bulk update of service cases via API fails with 413 Payload Too Large error when processing more than 200 records

Our support operations team needs to bulk update service case statuses and assignments when we rotate support agents or close resolved cases in batches. We’re using the bulk update API endpoint, but when we try to update more than 200 cases at once, we get HTTP 413 Payload Too Large errors.

Current implementation:


PUT /api/service-cases/bulk-update
Body: [{case_id: 1, status: 'closed'}, ...]
// 500 case objects in array

We need to process 1,000+ cases weekly, and doing them in small batches is inefficient and error-prone. The API documentation doesn’t specify payload size limits. We’ve tried compressing the request body with gzip, but still hit the limit around 250 cases. Is there a configuration to increase the payload limit, or should we be using a different approach for large-scale case updates?

I’ll provide a complete solution covering API payload size limits, batch processing optimization, and error handling for large requests.

Understanding API Payload Size Limits: The 413 Payload Too Large error occurs because Adobe Experience Cloud enforces a 1MB limit on synchronous API requests at the gateway level. This isn’t a record count limit but a total payload size restriction. Your 200-case limit suggests each case object is approximately 5KB, which is typical when including all fields from GET responses.

Batch Processing Optimization:

Step 1: Minimize Payload Size First, reduce your payload by sending only the fields being updated:

PUT /api/service-cases/bulk-update
{
  "updates": [
    {"id": 1001, "status": "closed"},
    {"id": 1002, "status": "closed", "assigned_to": 456},
    {"id": 1003, "status": "resolved"}
  ]
}

This minimal format reduces each case object from ~5KB to ~50-100 bytes, allowing 800-1000 cases per request within the 1MB limit.

Step 2: Use Asynchronous Bulk Operations API For 1,000+ cases, switch to the async bulk operations endpoint designed for large-scale updates:

POST /api/v2/service-cases/bulk-operations
Content-Type: application/json

{
  "operation": "update",
  "idempotency_key": "weekly-closure-2025-09-22",
  "cases": [
    {"id": 1001, "status": "closed"},
    {"id": 1002, "status": "closed"},
    // ... up to 5000 cases
  ]
}

Response:
{
  "job_id": "job_abc123",
  "status": "queued",
  "total_records": 1000
}

The async API has higher limits (5,000 records or 10MB per job) and processes in the background.

Step 3: Poll Job Status Monitor the job completion:

GET /api/v2/service-cases/bulk-operations/job_abc123

Response:
{
  "job_id": "job_abc123",
  "status": "completed",
  "processed": 1000,
  "succeeded": 998,
  "failed": 2,
  "results_url": "/api/v2/bulk-operations/job_abc123/results"
}

Error Handling for Large Requests:

Implement Retry Logic with Exponential Backoff:

def submit_bulk_job(cases, max_retries=3):
    for attempt in range(max_retries):
        try:
            response = POST bulk_operations_endpoint
            if response.status == 202:  # Accepted
                return response.json()['job_id']
        except PayloadTooLarge:
            # Split into smaller batches
            batch_size = len(cases) // 2
            job_ids = []
            for batch in chunk(cases, batch_size):
                job_ids.append(submit_bulk_job(batch))
            return job_ids
        except TransientError:
            sleep(2 ** attempt)
    raise BulkUpdateFailed()

Handle Partial Failures: Retrieve and process failed records:

GET /api/v2/bulk-operations/job_abc123/results

Response:
{
  "succeeded": [
    {"id": 1001, "status": "closed"},
    // 998 successful updates
  ],
  "failed": [
    {
      "id": 1005,
      "error": "Case locked by another user",
      "error_code": "CASE_LOCKED"
    },
    {
      "id": 1023,
      "error": "Invalid status transition",
      "error_code": "INVALID_TRANSITION"
    }
  ]
}

Retry failed cases after resolving the errors (unlock cases, fix invalid transitions).

Optimized Batch Processing Strategy:

  1. Split Large Datasets: For 1,000+ cases, use 500-1000 per job to balance throughput and error handling
  2. Parallel Job Submission: Submit multiple jobs concurrently (max 5 active jobs per account)
  3. Idempotency Keys: Use unique keys per batch to prevent duplicate processing if jobs are retried
  4. Compression: Enable gzip compression on requests to reduce network transfer time

Complete Implementation:

def bulk_update_cases(cases, batch_size=800):
    job_ids = []

    # Split into optimal batches
    for i, batch in enumerate(chunk(cases, batch_size)):
        # Minimize payload - only updated fields
        minimal_batch = [
            {"id": c["id"], "status": c["status"]}
            for c in batch
        ]

        # Submit async job with idempotency
        job = {
            "operation": "update",
            "idempotency_key": f"batch_{i}_{date.today()}",
            "cases": minimal_batch
        }

        response = POST(
            "/api/v2/service-cases/bulk-operations",
            json=job,
            headers={"Content-Encoding": "gzip"}
        )

        job_ids.append(response.json()["job_id"])

    # Monitor all jobs
    return monitor_jobs(job_ids)

def monitor_jobs(job_ids):
    results = {"succeeded": 0, "failed": 0}

    while job_ids:
        for job_id in job_ids[:]:
            status = GET(f"/api/v2/bulk-operations/{job_id}")

            if status["status"] == "completed":
                results["succeeded"] += status["succeeded"]
                results["failed"] += status["failed"]

                # Handle failures
                if status["failed"] > 0:
                    retry_failed_cases(job_id)

                job_ids.remove(job_id)

        sleep(5)  # Poll every 5 seconds

    return results

Best Practices:

  1. Monitor payload sizes before submission to avoid 413 errors
  2. Use async API for any operation affecting 200+ records
  3. Implement idempotency to safely retry failed jobs
  4. Handle partial failures by retrieving and reprocessing failed records
  5. Set up monitoring for job completion times and failure rates
  6. Schedule large updates during off-peak hours for better performance

With this optimized approach, your 1,000+ weekly case updates will process reliably in 5-10 minutes with proper error handling and recovery.


This draft is based on general Adobe Experience Cloud knowledge. It has not been verified against your specific version and environment. Practitioners: verify the steps and share your experience below.

The 413 error indicates you’re hitting the API gateway’s request size limit, which is typically 1MB for Adobe Experience Cloud endpoints. The limit isn’t about the number of records but the total payload size. Each case object includes all the fields you’re updating plus metadata, so 200-250 cases probably exceeds 1MB. You need to reduce the payload size per request.

Instead of sending full case objects, send only the fields you’re actually updating. If you’re just changing status and assignment, your payload should be minimal like {“id”: 123, “status”: “closed”, “assigned_to”: 456}. Remove any read-only fields, timestamps, or nested objects. This can reduce your payload size by 70-80% and let you fit more cases per request. Also make sure you’re setting Content-Type: application/json header correctly.

Good point about minimizing the payload. We were including the entire case object from our GET response. I’ll strip it down to just ID and the fields we’re updating. But even with minimal payloads, we’ll still need multiple batches for 1,000+ cases. Is there a way to process these batches asynchronously so we don’t have to wait for each batch to complete?

Adobe Experience Cloud has an async bulk operations API that’s perfect for this. Instead of synchronous PUT requests, you POST a bulk operation job that processes in the background. You get a job ID back immediately, then poll for completion. This is designed for operations affecting 500+ records. The job API also handles retries automatically if individual case updates fail, giving you better error handling than manual batching.

The async approach is definitely the way to go for large batches. One thing to watch out for - make sure your bulk operation job doesn’t exceed the maximum job size, which I believe is 5,000 records or 10MB, whichever comes first. For 1,000 cases with minimal payloads, you should be fine with a single job. Also implement proper error handling to check the job status and retrieve any failed case updates from the results.

Don’t forget about idempotency when using the async API. If a job fails partway through or times out, you need to be able to retry without duplicating updates. Use the idempotency_key parameter in your job creation request to ensure duplicate job submissions don’t cause issues. This is especially important if you’re automating these bulk updates on a schedule.