# Bulk upload

Send many products at once, without the request body limit of a single upsert
(1 MB on Sandbox, 6 MB on Production).

1. **[Get a bulk upload URL](bulk_upload_url.md)** returns a signed URL.
2. **Upload your JSON file to that URL** with an HTTP `PUT`. The file is an
   array of products, each in the same format as a single upsert. See [Bulk file format](bulk_file_format.md).
3. **[Start a bulk upload](bulk_upload_start.md)** with the same URL.

Processing happens in the background. [Check bulk upload status](bulk_upload_status.md) follows it, and
[Cancel a bulk upload](bulk_upload_cancel.md) stops it.

## Endpoints

| | |
| --- | --- |
| `GET /api/bobsync/products-bulk` | Get a signed URL to upload the file to. |
| `POST /api/bobsync/products-bulk` | Queue the uploaded file for processing. |
| `GET /api/bobsync/products-bulk/status` | Check how a run is going, or how the last one finished. |
| `POST /api/bobsync/products-bulk/cancel` | Stop a running upload. |

## A bulk file is a set of changes, not a replacement catalogue

- Products in the file are created or updated.
- Products missing from the file are left alone. Their listings stay live.
- To remove a listing, call [Delete a product](../6_Products/product_delete.md). Leaving a product out of a
  later file does nothing.

A daily file containing only what changed is a good way to use these
endpoints.

## But a product's variants are replaced

Within each product you send, the `variants` array replaces that product's
variants. A variant we hold that is not in the array is deleted, along with
its part of the listing. Send a product's complete variant list every time,
even for a one-field change. The single-product upsert works the same way.

## Errors do not stop the run, with one exception

Each product is processed on its own. A failed product is recorded in the
status and the run continues.

The exception is an `id` that does not exist, most often an `id` from the
other environment. That stops the run part-way and leaves `is_running` set
for up to an hour, until the status record expires. See [Important concepts](../5_Important%20concepts/README.md).
