read.bulk.products
List published products in a store; filter by category (ltree), SKU option, price range and facetable custom fields; sort; opt-in facets. projection selects a NAMED shape of the result (absent = the whole document; see the projections on the reference page). ⚠️ THE DEFAULT SHAPE IS THE WHOLE DOCUMENT and it is heavy — about 13 KB per product, so even limit=10 answers with 70–100 K characters. If you are an agent, or anything else that pays for what it reads, pass projection=feed: same published set, same page, roughly a fifth of the bytes. Served on the BULK face: the same published answer as the public read, charged to the caller’s own bulk budget instead of the shopper’s, and never to be reached by a browser.
- Surface:
GET /v1/read/bulk/products - Auth: ★ Bulk read (machine) — a TENANT credential is REQUIRED (no credential is
unauthorized), andstoremust be a store of THAT tenant (another tenant’s isforbidden). The ANSWER is the public read’s, byte for byte: this store’s published assortment, no drafts, no actor. What the credential buys is the BUDGET — this face has its ownRateLimit-*bucket, so walking a whole catalogue here takes nothing from the ceiling a shopper’s browser shares.
Params (JSON Schema)
Section titled “Params (JSON Schema)”{ "additionalProperties": { "type": "string" }, "properties": { "category": { "pattern": "^[A-Za-z0-9_]+(\\.[A-Za-z0-9_]+)*$", "type": "string" }, "collection": { "minLength": 1, "type": "string" }, "limit": { "default": 300, "maximum": 300, "minimum": 1, "type": "integer" }, "page": { "default": 1, "maximum": 9007199254740991, "minimum": 1, "type": "integer" }, "projection": { "description": "Named projection of the result. Declared: full, feed. Absent = full.", "type": "string" }, "status": { "enum": [ "draft", "active", "archived" ], "type": "string" }, "store": { "minLength": 1, "type": "string" } }, "required": [ "store" ], "type": "object"}Result (200)
Section titled “Result (200)”Open (unknown) — this capability does not declare an output schema.
Projections
Section titled “Projections”Pass projection=<name>. An absent projection means full — an existing caller is unaffected. A name that is not declared here is refused (unknown_projection), never served as the default.
projection=full
Section titled “projection=full”The whole product document, exactly as this read has always served it. This is what an absent projection means, so an existing caller is unaffected. ⚠️ It is BIG — about 13 KB per product, of which roughly three quarters is skus, so a single page of 10 is on the order of 70–100 K characters. A caller that renders a shelf wants it; a caller with a context window (an agent) almost never does, and should ask for feed.
Same shape as the result above.
projection=feed
Section titled “projection=feed”The product reduced to what a shopping channel puts in a listing: identity, title, description, handle, brand, category path, the primary image, and per sku its code, price (with the promotional and compare-at prices), status, name, GTIN and its own image. It is the same published set and the same page as full — about a fifth of the bytes — for a caller that walks the whole catalog to EMIT a document (a product feed, a marketplace export). Everything a listing never prints is gone: options, rich content, sku metadata and option values.
{ "additionalProperties": false, "properties": { "brand_id": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "categories": { "items": { "additionalProperties": false, "properties": { "category_id": { "type": "string" }, "is_primary": { "type": "boolean" }, "path": { "type": "string" } }, "required": [ "category_id", "path", "is_primary" ], "type": "object" }, "type": "array" }, "description": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "handle": { "type": "string" }, "media": { "items": { "additionalProperties": false, "properties": { "kind": { "type": "string" }, "url": { "type": "string" } }, "required": [ "kind" ], "type": "object" }, "type": "array" }, "metadata": {}, "product_id": { "type": "string" }, "skus": { "items": { "additionalProperties": false, "properties": { "amount": { "maximum": 9007199254740991, "minimum": -9007199254740991, "type": "integer" }, "code": { "type": "string" }, "compare_at_amount": { "anyOf": [ { "maximum": 9007199254740991, "minimum": -9007199254740991, "type": "integer" }, { "type": "null" } ] }, "currency": { "type": "string" }, "ean": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "id": { "type": "string" }, "media": { "items": { "additionalProperties": false, "properties": { "kind": { "type": "string" }, "url": { "type": "string" } }, "required": [ "kind" ], "type": "object" }, "type": "array" }, "name": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "promotional_price": { "additionalProperties": false, "properties": { "promotional_amount": { "maximum": 9007199254740991, "minimum": -9007199254740991, "type": "integer" }, "unit_amount": { "maximum": 9007199254740991, "minimum": -9007199254740991, "type": "integer" } }, "required": [ "unit_amount", "promotional_amount" ], "type": "object" }, "status": { "type": "string" } }, "required": [ "id", "code", "amount", "currency", "status", "name", "ean", "compare_at_amount", "media" ], "type": "object" }, "type": "array" }, "status": { "type": "string" }, "title": { "type": "string" } }, "required": [ "product_id", "title", "description", "handle", "status", "metadata", "brand_id", "categories", "media", "skus" ], "type": "object"}Errors
Section titled “Errors”invalid_params (400) · not_found (404) · unauthorized (401) · forbidden (403) · unknown_projection (400)