# GET /v1/catalogue/products

**Service:** Catalogue  
**Operation:** `CatalogueService_ListProducts`

Returns a paginated list of catalogue product summaries. The listing is lightweight: `rows` is not populated — retrieve a product with GetProduct to get its priced variant rows. Product kits are not included here; list them with ListKits. Products are returned alphabetically by name.

## Parameters

| Parameter | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `pageSize` | query | integer | no | Maximum number of products to return per page. 0 uses the server default; the server may cap the value. |
| `pageToken` | query | string | no | Opaque page token from a previous response, used to fetch the next page. |
| `name` | query | string | no | Optional: only return products whose name contains this text. A browse aid, not a precise lookup key. |
| `categoryName` | query | string | no | Optional: only return products in this category, by category name. |
| `fields` | query | string | no | Comma-separated response fields to include, using camelCase JSON names (e.g. `orderId,total.amountMicros`). Dot paths reach into nested objects and map transparently across arrays. Paths are relative to the resource, not the response envelope; envelope keys like `nextPageToken` are always preserved. Only 2xx JSON responses are filtered; error bodies pass through unmodified. Unknown names are silently ignored. Takes precedence over `excludeFields` when both are provided. |
| `excludeFields` | query | string | no | Comma-separated response fields to exclude, using camelCase JSON names. Dot paths and array-transparency work the same as `fields`. Paths are relative to the resource, not the response envelope. Only 2xx JSON responses are filtered; error bodies pass through unmodified. Ignored when `fields` is also provided. |

## Responses

| Status | Schema | Description |
| --- | --- | --- |
| 200 | `ListProductsResponse` | A successful response. |
| 400 | `Error` | The request was rejected because it was malformed — for example an unusable page token or an invalid filter value. The body is a validation-failure envelope: an overall type and message and one entry per problem (each with a stable code, a message, and a param pointing at the offending parameter). |
| 401 | `Error` | The request is missing a valid bearer token, or the token is invalid or expired. |
| 403 | `Error` | The token is valid but does not permit this action, or the resource belongs to a company the token cannot act for. The error type is permission_denied. |
| 429 | `Error` | The request was throttled (rate_limited) or exceeded a size limit (resource_exhausted). When throttled, the Retry-After header says how many seconds to wait before retrying; a resource_exhausted request will fail the same way if retried unchanged. |
| default | `Error` | Any other error. The body is the same error envelope every error uses: a short stable type identifying the kind of failure (for example "rate_limited" or "internal"), a human-readable message, and the request's idempotency key echoed back when one was supplied. |

## Returns

`ListProductsResponse`

| Field | Type | Description |
| --- | --- | --- |
| `products` | Product[] | The products in this page. |
| `products.productId` | string | The product's unique id. Use this to reference the product on a line item. |
| `products.name` | string | The product's display name. Not guaranteed unique; not a lookup key. |
| `products.categoryId` | string | The id of the category this product belongs to. |
| `products.categoryName` | string | The display name of the product's category. |
| `products.pricingStrategy` | PricingStrategy | How this product's quantity is interpreted for pricing. How a quantity is interpreted for pricing — whether it means a count of each, a length, or an area.  On a catalogue product this is the product's pricing basis. On an order or quote line, measurement-based strategies (lineal or square metres/feet) require the line's measurement details to be supplied.   - PRICING_STRATEGY_UNSPECIFIED: Unset, or a legacy product with no pricing strategy recorded.  - PRICING_STRATEGY_BASIC_QUANTITIES: Quantity is a simple per-each count.  - PRICING_STRATEGY_LINEAL_METRES: Quantity is a length in lineal metres.  - PRICING_STRATEGY_CUSTOM_FORMULA: Quantity is priced by a custom formula.  - PRICING_STRATEGY_SQUARE_METRES: Quantity is an area in square metres.  - PRICING_STRATEGY_LINEAL_FEET: Quantity is a length in lineal feet.  - PRICING_STRATEGY_SQUARE_FEET: Quantity is an area in square feet. One of: PRICING_STRATEGY_UNSPECIFIED, PRICING_STRATEGY_BASIC_QUANTITIES, PRICING_STRATEGY_LINEAL_METRES, PRICING_STRATEGY_CUSTOM_FORMULA, PRICING_STRATEGY_SQUARE_METRES, PRICING_STRATEGY_LINEAL_FEET, PRICING_STRATEGY_SQUARE_FEET. |
| `products.isTaxFree` | boolean | Whether this product is exempt from tax. |
| `products.rows` | ProductRow[] | The product's priced variant rows. |
| `products.rows.productRowId` | string | The variant row's unique id. Reference this on a catalogue line item. |
| `products.rows.colour` | string | The variant's own colour. Usually empty — colour is normally chosen per line from `colourOptions` rather than baked into the row. |
| `products.rows.thickness` | string | The variant's thickness. |
| `products.rows.attributes` | RowAttribute[] | Additional attribute pairs that identify this variant, e.g. [{name:"Size", value:"A4"}]. |
| `products.rows.markedUpPrice` | Money | The computed marked-up sell price, for reference. |
| `products.rows.prices` | RowPrice[] | The variant's price at each of your price levels. |
| `products.rows.colourOptions` | string[] | The colours a catalogue line built on this row may use, when the product's material offers a colour selection. A line's colour must be one of these. They come from the row's material, so every row sharing that material offers the same colours; the list is empty when the product has no colour choice. Colour is the per-line choice — the attributes above identify the variant. |
| `products.isInventoryTracked` | boolean | Whether this product participates in inventory tracking. When true, stock levels for its rows are available from the Inventory API. |
| `nextPageToken` | string | Token to pass as `pageToken` to fetch the next page; empty when there are no more results. |

---

Source: https://developer.factory.app/reference/catalogue/list-products · Factory Sales API v1
