# Catalog offers

Catalog offers: list, retrieve, create, update, and delete. An offer describes a specific variant of a parent product — for example, a T-shirt size — and is stored in a separate offer catalog. The parent product is created in [Catalog SKUs](/docs/entities/catalog-skus), and the offer's sale prices are set in [Catalog prices](/docs/entities/catalog-prices).

Bitrix24 API: `catalog.product.offer.*`
Scope: `catalog`

## Operations

- [Create an offer](./catalog-offers/create.md) — `POST /v1/catalog-offers`
- [List offers](./catalog-offers/list.md) — `GET /v1/catalog-offers`
- [Get an offer](./catalog-offers/get.md) — `GET /v1/catalog-offers/:id`
- [Update an offer](./catalog-offers/update.md) — `PATCH /v1/catalog-offers/:id`
- [Delete an offer](./catalog-offers/delete.md) — `DELETE /v1/catalog-offers/:id`
- [Search offers](./catalog-offers/search.md) — `POST /v1/catalog-offers/search`
- [Offer fields](./catalog-offers/fields.md) — `GET /v1/catalog-offers/fields`
- [Aggregate offers](./catalog-offers/aggregate.md) — `POST /v1/catalog-offers/aggregate`

## Key fields

| Field | Description |
|------|---------|
| `name` | Offer name |
| `iblockId` | Offer catalog ID — the catalog that has `productIblockId` filled in. Required on create and in the list, search, and aggregation filters. List: [`GET /v1/catalogs`](/docs/entities/catalogs) |
| `parentId` | Link to the parent product. On create, it is passed as `{ "value": "<ID>" }`; the response returns it as an object `{ value, valueId }`. The parent product ID comes from [`GET /v1/catalog-skus`](/docs/entities/catalog-skus/list) |
| `type` | Record type, computed by Bitrix24: `4` — the offer is linked to a parent product, `5` — a free offer without `parentId` |
| `active` | Whether the offer is active |
| `measure` | Unit of measure ID. List: [`GET /v1/catalog-measures`](/docs/entities/catalog-measures) |
| `purchasingPrice` | Purchase price. Currency is in `purchasingCurrency` |
| `quantity` | Stock balance |

Full list of fields — [`GET /v1/catalog-offers/fields`](./catalog-offers/fields.md).

## What to know before you start

1. **Offers have their own catalog.** Each product catalog has a separate offer catalog: in [`GET /v1/catalogs`](/docs/entities/catalogs) it has `productIblockId` — the product catalog ID — and `skuPropertyId` filled in. Its `iblockId` is passed when creating an offer and in the list, search, and aggregation filters. With the `iblockId` of the product catalog, the list and offer creation return `422 BITRIX_ERROR` with the message `productType is not allowed for this catalog`.
2. **List, search, and aggregation require a catalog filter.** Without `filter[iblockId]`, the request is rejected with `400 MISSING_REQUIRED_FILTER` before Bitrix24 is called.
3. **The link to the parent product is set on create.** The `parentId` field accepts `{ "value": "<parent product ID>" }`. Without it, a free offer with `type: 5` is created. The link cannot be changed: `PATCH` with `parentId` is rejected with `400 READONLY_FIELD`. To select all offers of one parent product, use `filter[parentId]=<ID>`.
4. **The `type` filter accepts one exact value** — `4` or `5`. Other numbers, arrays, and operators are rejected with `400 UNSUPPORTED_FILTER`.
5. **Field selection must include `iblockId` and `id`.** If a list or search request passes `select`, both fields are required in it. Without `select`, the list returns a reduced set of fields, while a single record by `id` returns the full set, together with catalog properties of the form `propertyNNN`.
6. **The minimum for creation is `name` and `iblockId`.** If either is missing, the request is rejected with `400 MISSING_REQUIRED_FIELDS`.
7. **Deleting a parent product deletes its offers.** After `DELETE /v1/catalog-skus/:id`, requests for that product's offers return `404 ENTITY_NOT_FOUND`.
8. **`DELETE` does not check that the `id` belongs to an offer.** With the `id` of a regular product, the call returns `204` and deletes that product. Before deleting, check the record with [`GET /v1/catalog-offers/:id`](./catalog-offers/get.md): for the `id` of a regular product, it returns `404 ENTITY_NOT_FOUND`.

## Typical scenario

1. Find the offer catalog and its `iblockId`: [`GET /v1/catalogs`](/docs/entities/catalogs) — the record with `productIblockId` filled in.
2. Create or find a parent product: [`POST /v1/catalog-skus`](/docs/entities/catalog-skus/create) / [`GET /v1/catalog-skus`](/docs/entities/catalog-skus/list).
3. Create offers of the parent product: [`POST /v1/catalog-offers`](./catalog-offers/create.md) with `parentId`.
4. View the offers of the parent product: [`GET /v1/catalog-offers?filter[iblockId]=27&filter[parentId]=7189`](./catalog-offers/list.md).
5. Set the offer price — the offer `id` is passed in `productId`: [`POST /v1/catalog-prices`](/docs/entities/catalog-prices/create).

## Limits

| Limit | Value |
|-------|----------|
| Maximum records per request | 5000 (`limit ≤ 5000`) |
| Auto-pagination | enabled when `limit > 50` |
| Batch requests | up to 50 operations in [`POST /v1/batch`](/docs/batch) |
| Rate limit | shared across the Vibecode API — see [Limits and optimization](/docs/optimization) |

## See also

- [Catalog SKUs](/docs/entities/catalog-skus)
- [Catalog products](/docs/entities/catalog-products)
- [Catalog prices](/docs/entities/catalog-prices)
- [Catalogs](/docs/entities/catalogs)
- [Catalog measures](/docs/entities/catalog-measures)
- [Catalog product properties](/docs/entities/catalog-product-properties)
- [Filtering syntax](/docs/filtering)
- [API reference](/docs/api-reference)
