> ## Documentation Index
> Fetch the complete documentation index at: https://docs.okasie.be/llms.txt
> Use this file to discover all available pages before exploring further.

# Bulk Create/Update Listings

> Create or replace up to 100 listings in one request

Runs [Create/Update Listing](https://docs.okasie.be/api-reference/listings/upsert.md) for up to 100 cars in one request. Every item follows exactly the same rules as the single PUT (full replacement, required fields, images, errors), and every item succeeds or fails on its own.

> [!NOTE]
>
> Requires the `write:listings` scope. The whole request counts as **one** request for the [rate limit](https://docs.okasie.be/rate-limiting.md).

## Request body

Either a JSON array of items, or an object with the array in `items`. Between 1 and 100 items.

**items** `object[]` *required*

The listings. Each item is the body of [Create/Update Listing](https://docs.okasie.be/api-reference/listings/upsert.md) plus its reference.

**item properties**

**externalReference** `string` *required*

Your reference for this car, the same value you would put in the path of the single PUT. `external_reference` is accepted as another name.

**...** `any`

Every field of [Create/Update Listing](https://docs.okasie.be/api-reference/listings/upsert.md).

Items are processed one after the other, in the order sent. Photos are downloaded during the request, so a request with many new photos can take long: send fewer items per request when they carry photos, and use a client timeout of at least a few minutes.

## Response

When the request itself is valid the response is **always HTTP 200**, also when some or all items failed. Check `errorCount` and `errors`. The response carries the [rate limit headers](https://docs.okasie.be/rate-limiting.md).

**data.successCount** `integer`

Number of items saved.

**data.errorCount** `integer`

Number of items refused.

**data.results** `object[]`

One entry per saved item.

**properties**

**index** `integer`

Position of the item in your array, starting at 0.

**externalReference** `string`

Your reference.

**listingId** `string`

Okasie listing UUID.

**status** `string`

The stored status.

**listingUrl** `string | null`

The public page when `status` is `active`; `null` otherwise.

**created** `boolean`

`true` when this request created the listing.

**warnings** `object`

Only present when photos were skipped: `skippedImages[]` with `source` and `reason`, as for the single PUT.

**data.errors** `object[]`

One entry per refused item.

**properties**

**index** `integer`

Position of the item in your array, starting at 0.

**externalReference** `string`

Your reference; absent when the item had none.

**code** `string`

Error code, the same codes as the single PUT (for example `VALIDATION_FAILED`, `INVALID_LOCATION`, `PROFILE_FORBIDDEN`, `LISTING_LIMIT_REACHED`).

**message** `string`

Description of the error.

**status** `integer`

The HTTP status the single PUT would have returned.

**issues** `object[]`

Only for `VALIDATION_FAILED`: `path`, `message` and `code` per problem.

```bash cURL theme={null}
curl -X POST "https://www.okasie.be/api/external/v1/listings/bulk-upsert" \
  -H "Authorization: Bearer YOUR_API_SECRET" \
  -H "Content-Type: application/json" \
  -d '{
    "items": [
      {
        "externalReference": "DV-123456",
        "dealerLocationCode": "OK-00148",
        "status": "active",
        "title": "Volkswagen Golf 1.5 TSI Life",
        "price": 21950,
        "brand": "Volkswagen",
        "model": "Golf",
        "year": 2022,
        "mileage": 38500,
        "fuelType": "Benzine",
        "postalCode": "9000",
        "city": "Gent",
        "images": ["https://images.example.com/dv/123456/1.jpg"]
      },
      {
        "externalReference": "DV-123457",
        "dealerLocationCode": "OK-00148",
        "status": "active",
        "title": "Renault Trafic 2.0 dCi",
        "price": 26990,
        "doors": 0,
        "postalCode": "9000",
        "city": "Gent"
      }
    ]
  }'
```

```json 200 partial success theme={null}
{
  "data": {
    "successCount": 1,
    "errorCount": 1,
    "results": [
      {
        "index": 0,
        "externalReference": "DV-123456",
        "listingId": "0c52cae7-bcaa-4b37-be0d-1b78c92c5225",
        "status": "active",
        "listingUrl": "https://www.okasie.be/aanbod/volkswagen-golf-1-5-tsi-life--0c52cae7-bcaa-4b37-be0d-1b78c92c5225",
        "created": true
      }
    ],
    "errors": [
      {
        "index": 1,
        "externalReference": "DV-123457",
        "message": "Validation failed",
        "code": "VALIDATION_FAILED",
        "status": 422,
        "issues": [
          {"path": "doors", "message": "Number must be greater than or equal to 1", "code": "too_small"}
        ]
      }
    ]
  }
}
```

```json 400 too many items theme={null}
{
  "error": {
    "code": "TOO_MANY_ITEMS",
    "message": "Maximum 100 items per request",
    "maxItems": 100
  }
}
```

## Errors for the whole request

These refuse the request before any item is processed.

| Status | Code | When |
| - | - | - |
| 401 | `UNAUTHORIZED` | Missing or unknown API key, or the key lacks `write:listings` |
| 429 | `RATE_LIMITED` | Rate limit reached; the body has `retryAt` |
| 400 | `INVALID_JSON` | The body is not valid JSON |
| 400 | `INVALID_BODY` | The body is neither an array nor an object with an `items` array |
| 400 | `EMPTY_PAYLOAD` | The array is empty |
| 400 | `TOO_MANY_ITEMS` | More than 100 items; the body has `maxItems` |

## Errors per item

Besides every error code of [Create/Update Listing](https://docs.okasie.be/api-reference/listings/upsert.md#errors), an item can fail with `INVALID_REFERENCE` (status 400) when it has no `externalReference`.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.