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

# Imports vs bulk

> Choosing between async CSV imports and synchronous JSON bulk endpoints.

The Public API offers two ways to load many records at once. Picking the right one keeps your integration simple and your latency low.

## Side by side

|                         | `POST /v1/imports/*`                                     | `POST /v1/<entity>/bulk`                   |
| ----------------------- | -------------------------------------------------------- | ------------------------------------------ |
| Transport               | Multipart CSV                                            | JSON                                       |
| Mode                    | **Async** - returns a `job_id`                           | **Sync** - returns results in the response |
| Typical size            | Hundreds of thousands to millions of rows                | Hundreds to a few thousand rows            |
| Per-row error reporting | After the job completes                                  | Immediately, in `BulkResponse.results`     |
| Retry semantics         | Retry the upload; fix CSV and re-submit if the job fails | Idempotent per item                        |
| Rate limit footprint    | One request per file                                     | One request per batch                      |

## Use `imports/*` when...

* You are doing a **full-catalog refresh** from your ERP or PIM.
* The data already lives in a CSV (or your pipeline can produce one cheaply).
* Wall-clock latency does not matter - you can poll status.
* You want minimal request overhead per row.

Endpoints in this family:

* [`POST /v1/imports/products`](/api-reference/imports/products)
* [`POST /v1/imports/transactions`](/api-reference/imports/transactions)
* [`POST /v1/imports/competitor_prices`](/api-reference/imports/competitor-prices)
* [`POST /v1/imports/product_store`](/api-reference/imports/product-store)

## Use `/bulk` when...

* You are syncing **deltas** from your source system on an event-driven schedule.
* You want a single round trip and an immediate per-row outcome.
* You can shape the payload as JSON (the records do not naturally come from a file).
* Batch size is in the **hundreds to low thousands**.

Endpoints in this family:

* [`POST /v1/products/bulk`](/api-reference/products/bulk-upsert)
* [`POST /v1/product_store/bulk`](/api-reference/product-store/bulk-upsert)
* [`POST /v1/transactions/bulk`](/api-reference/transactions/bulk-upsert)
* [`POST /v1/competitor_prices/bulk`](/api-reference/competitor-prices/bulk-upsert)

## Use single-record `POST /v1/<entity>/{entity_id}` when...

* You are reacting to a **single change** in your source system (one product updated, one transaction posted).
* You want strong per-call status semantics (`201` vs `422` directly).
* You need the full response body for the row you just wrote.

## A pragmatic pattern

Most production integrations end up using two of the three:

1. **Daily** full-catalog `imports/products` to absorb all upstream changes.
2. **Event-driven** `products/bulk` or `POST /v1/products/{entity_id}` for low-latency updates between full syncs.

This keeps Retailgrid eventually consistent on cheap infrastructure, with a fast path for the small slice of changes that actually need it.
