Skip to main content

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.

Read this once and you will be able to use any endpoint in the API without surprises.

Identifiers

Two kinds of ids show up in the API:
  • product_id / sku / competitor_name / etc. - your stable business identifier. You provide it. Use it for upserts.
  • id / entity_id - Retailgrid’s internal row id, returned in responses. Use it in path parameters when patching or deleting (PATCH /v1/products/{entity_id}).
entity_id is a UUID for products, product variants, and transactions. For competitor prices it is an integer - the only place this varies in the API.

Bulk endpoints

Every entity has a POST /v1/<entity>/bulk endpoint that accepts an items array and returns per-item results. Request:
{
  "items": [
    { "product_id": "SKU-001", "product_name": "Item one" },
    { "product_id": "SKU-002", "product_name": "Item two" }
  ]
}
Response (BulkResponse):
{
  "results": [
    { "index": 0, "ok": true,  "id": "1c2d..." },
    { "index": 1, "ok": false, "code": "validation_error", "message": "product_name too long" }
  ]
}
A bulk request is never all-or-nothing. Successful rows are written even if other rows fail. Always inspect each BulkItemResult.ok before declaring victory. Recommended batch size: 500 to 2,000 items per request depending on row width. Larger payloads will work but increase latency and the chance of a transport-level retry.

Async jobs

The POST /v1/imports/* endpoints accept a multipart CSV and return immediately with a JobHandle:
{ "job_id": "0b3c1f4a-9c1d-4f2a-8a3b-2c3d4e5f6a7b" }
Poll status with GET /v1/jobs/{job_id}:
{
  "job_id": "0b3c1f4a-9c1d-4f2a-8a3b-2c3d4e5f6a7b",
  "job_type": "import_products",
  "status": "running",
  "progress": 47,
  "created_at": "2026-05-04T10:00:00Z",
  "started_at": "2026-05-04T10:00:01Z"
}
status is one of queued, running, completed, failed. A reasonable polling cadence is every 5-10 seconds for active jobs and every 30-60 seconds for queued jobs.

Sync vs async

You want to…UseEndpoint family
Push a few records right nowSync, JSONPOST /v1/<entity>/{entity_id} or /bulk
Refresh a full catalog from a CSVAsync, multipartPOST /v1/imports/<entity> then poll /v1/jobs/{id}
See Imports vs bulk for the full decision tree.

Decimals

Money, quantities, and rates are returned as strings, not floats. This avoids the rounding surprises you would get with IEEE-754:
{ "regular_price": "19.99", "tax_rate": "0.20" }
You can submit them as either numbers or strings on the way in, but expect strings in responses. Parse with a decimal library on the client side.

Timestamps

All timestamps are ISO 8601 in UTC:
2026-05-04T10:00:00Z
Date-only fields use YYYY-MM-DD. The API does not perform timezone conversion - send UTC, get UTC.

Idempotency

POST /v1/<entity>/{entity_id} is an upsert keyed on the path’s entity_id, which makes it safe to retry. PATCH is also idempotent at the field level. DELETE is idempotent (deleting an already-deleted row returns 204). The /bulk endpoints are idempotent per item, keyed on the business identifier inside each item (product_id, sku, etc.). There is no Idempotency-Key header today - we will add one before we recommend this API for payment-adjacent flows.

Pagination

The current write-focused endpoints do not paginate (there are no list endpoints in v1). When read endpoints land they will use cursor-based pagination; they will be added under a new section in this reference.

Rate limits

Rate limits are applied per API key. Today’s limits are generous enough that well-behaved sync pipelines do not hit them; if you do, you will receive 429 Too Many Requests with a Retry-After header. Specific numbers will be published here once we have stable production data.