---
title: "What you can order, and what it costs you"
method: GET
path: "/catalog"
tags: ["catalog"]
---

# What you can order, and what it costs you

`GET /catalog`

## Request

- Base URL: `https://0.0.0.0:3000/api/v1`
- URL: `https://0.0.0.0:3000/api/v1/catalog`
- Auth: API key in header `x-api-key`

Replaces two legacy endpoints at once: `GET /api/customer/metadata` (what can I order?) and `GET /api/v3/customer/orders/pricing` (what does it cost me?). They were separate, so every integration joined them client-side and each did it slightly differently.

Prices are THIS KEY'S prices — computed against the owner's tier with the same function that charges them at assignment. A price list that differs from the invoice is worse than no price list.

When OpCreative ships, `shipping_first_item` is added once per parcel and `shipping_add_on` for every extra unit in it — also your rates. `null` means no shipping charge.

`price_customer_ship` is what you pay when YOU ship it: higher, and no postage is added. `price_updated_at` is when the SKU's price or postage last changed, so a periodic pull can take only what moved.

`description` is the product information as plain text. `description_html` is the same text with the formatting set in the dashboard (bold, italic, underline, size, colour, alignment) as HTML, or `null` when it has none. It is sanitised down to `p`, `br`, `strong`, `em`, `u` and `span`, and its colours are our theme's CSS variables (`var(--red-900)`) — define them, or let the text inherit yours.

`add_on` is the extra an order can take with `add_on: true` — `{ label, price }`, the price per unit, on top of every SKU below — or `null` when the product offers none.

`thumbnail` is the product's cover and `images` the rest of its gallery, a list of `{ url, label }`. Both are stored on OpCreative, so the links do not go dead.

`private` is `true` on a product made only for chosen sellers, your account among them: other sellers do not see it here, and only those sellers can order it. Everything else is the public catalogue.

`kind` is what you are buying: `CATALOGUE` (our item, made and shipped by us), `FULFILMENT` (your own goods, stored and shipped by us) or `CONTRACT_WORK` (your own blank, decorated by us, where the SKU price is the labour). A `FULFILMENT` product bills `service_fee` once per order plus `service_fee_add_on` for every extra SKU line in it (quantity does not count), whoever ships; both are `null` on every other kind. An order on a SKU that has no price yet is refused with `price-missing`.

`skus[].id` is what `POST /orders` wants as `product_variant_id`.

## Query parameters

- `cursor` integer
- `limit` integer

## Response `200`

A page of products, each with its orderable SKUs.

## Other responses

- `401`: No key, or a key that is unknown, revoked or expired. One message for all four — telling you which would confirm to a guesser that a key exists.
- `429`: Over the per-key limit. `Retry-After` says how many seconds to wait.
- `500`: Our fault. `error.request_id` matches the X-Request-Id header — quote it to support.

---

[API](https://skmtc.dev/opcreative/apis/opcreative-api.md) · [All operations](https://skmtc.dev/opcreative/apis/opcreative-api/llms.txt) · [OpenAPI document](https://skmtc.dev/opcreative/apis/opcreative-api/revisions/105bb30aa87e?raw)
