For each product our own plant makes to any size (die-cut and kiss-cut stickers, vinyl and wall decals, magnets, posters), what an order of a custom size needs, from the rules the order itself applies: the SKU grammar (sku_template, a stem and {width}x{height} in decimal inches, case aside), the largest size (max_width_in × max_height_in, either way round), the smallest (min_area_sq_in, the least width × height the price book prices: below it one unit does not pay what it costs the plant to make and ship alone; null when every size is priced), the step (none, so a side is any decimal number of inches), the quantity a line may carry, the DPI the order grades artwork at (min_dpi recommended, refused below refuse_below_dpi on a live order) and the bleed, the finishes on offer (none yet) and the price book's list price for one unit (base_cents + width × height × cents_per_sq_in, rounded, before quantity breaks, any plan discount and the unit floor: POST /orders/quote is the price an order is charged). A size inside these bounds is one the book prices, and a test key's quote and order take it. A live quote or order also asks our plant for that exact SKU (plant_confirms_sku), and a size its catalog does not carry is refused 400 pricing_error; GET /products lists the sizes it carries now. Sticker sheets and rolls are not here: their SKUs name no printed area, so they are ordered at the sizes GET /products lists. One bounded document, not a page; it asks nothing of the plant, so it is never stale. Read-only.
- Credential
- An API key in
x-api-key - Scopes
products:read
Request
GET /api/v1/products/options
curl "https://portal.printedsimply.com/api/v1/products/options" \
-H "x-api-key: ps_test_YOUR_KEY"Response 200
Every configurable product, stickers first, then decals, magnets and posters.
data
productsobject[]requiredproducts[].productstringrequiredThe product, as
product_typeon POST /artwork/validate takes it.One of
sticker-die-cutsticker-kiss-cutdecal-vinyldecal-wallmagnetposterproducts[].namestringrequiredIts name: "Die-Cut Sticker".
products[].familystringrequiredIts family, as a catalog row's
familynames it.One of
stickersdecalsmagnetspostersproducts[].sku_templatestringrequiredHow to spell a SKU of it at a size: replace
{width}and{height}with the size in decimal inches (sticker-die-cut-{width}x{height}→sticker-die-cut-4x6). Case does not matter; a word after the size (a finish) changes neither the product nor its price.products[].example_skustringrequiredA SKU of it, spelled by
sku_template.products[].sizeobjectrequiredThe sizes the price book prices. Outside them the book prices nothing, and an order of the SKU is refused unless our plant's own catalog lists it.
products[].size.unitstringrequiredEvery size here, and in a SKU, is in inches.
Always
inproducts[].size.max_width_innumberrequiredThe widest side the book prices.
products[].size.max_height_innumberrequiredThe tallest side the book prices.
products[].size.either_orientationbooleanrequiredTrue:
{height}x{width}is priced wherever{width}x{height}is.products[].size.min_area_sq_innumber | nullrequiredThe least width × height, in square inches, the book prices, to a hundredth: a smaller print does not pay what one unit costs the plant alone. Null when every size is priced.
products[].size.step_innumber | nullrequiredNull: there is no step, and a side is any decimal number of inches.
products[].quantityobjectrequiredHow many units one order line may carry: the bounds
items[].quantityon POST /order is held to.products[].quantity.minimumintegerrequiredproducts[].quantity.maximumintegerrequiredproducts[].artworkobjectrequiredWhat the order grades an artwork file of this product against, at the size its SKU names.
products[].artwork.min_dpiintegerrequiredThe recommended resolution. Below it a line is warned (
below_recommended_resolution) and still prints.products[].artwork.refuse_below_dpinumberrequiredBelow this effective DPI a live order refuses the line (
low_resolution).products[].artwork.bleed_innumberrequiredThe margin the file should run past the trim, or keep clear around a die-cut outline, in inches.
products[].finishesstring[]requiredThe finishes offered as an option. Empty: none is offered yet, and a finish named in a SKU is not priced or made differently.
products[].price_bookobjectrequiredThe price book's list price for one unit:
base_cents+ width × height ×cents_per_sq_in, rounded to the cent. Quantity breaks (each catalog row'squantity_breaks), any plan discount and the unit floor apply after it; POST /orders/quote answers the price an order is charged.products[].price_book.base_centsintegerrequiredproducts[].price_book.cents_per_sq_innumberrequiredproducts[].plant_confirms_skubooleanrequiredAlways true: a live quote or order also asks our plant for the exact SKU, and a size its catalog does not carry is refused 400
pricing_error. A test key's takes any size within these bounds.
meta
pageintegertotalintegerlimitintegerThe page size actually applied, which is not necessarily the one asked for — the operation's own ceiling wins.
pagesintegertotaloverlimit, rounded up.next_cursorstring | nullOn a cursor-paged operation: send this back as
cursorfor the next page. Null on the last page. Absent on operations that do not page by cursor.has_morebooleanOn a list operation whose
metaisListMeta: whether another page follows this one.
{
"status": "success",
"data": {
"products": [
{
"product": "sticker-die-cut",
"name": "Die-Cut Sticker",
"family": "stickers",
"sku_template": "sticker-die-cut-{width}x{height}",
"example_sku": "sticker-die-cut-3x3",
"size": {
"unit": "in",
"max_width_in": 17,
"max_height_in": 17,
"either_orientation": true,
"min_area_sq_in": 1.55,
"step_in": null
},
"quantity": {
"minimum": 1,
"maximum": 10000
},
"artwork": {
"min_dpi": 300,
"refuse_below_dpi": 225,
"bleed_in": 0.125
},
"finishes": [],
"price_book": {
"base_cents": 177,
"cents_per_sq_in": 10
},
"plant_confirms_sku": true
},
{
"product": "sticker-kiss-cut",
"name": "Kiss-Cut Sticker",
"family": "stickers",
"sku_template": "sticker-kiss-cut-{width}x{height}",
"example_sku": "sticker-kiss-cut-3x3",
"size": {
"unit": "in",
"max_width_in": 17,
"max_height_in": 17,
"either_orientation": true,
"min_area_sq_in": 1.55,
"step_in": null
},
"quantity": {
"minimum": 1,
"maximum": 10000
},
"artwork": {
"min_dpi": 300,
"refuse_below_dpi": 225,
"bleed_in": 0.125
},
"finishes": [],
"price_book": {
"base_cents": 177,
"cents_per_sq_in": 10
},
"plant_confirms_sku": true
},
{
"product": "decal-vinyl",
"name": "Vinyl Decal",
"family": "decals",
"sku_template": "decal-vinyl-{width}x{height}",
"example_sku": "decal-vinyl-3x3",
"size": {
"unit": "in",
"max_width_in": 5,
"max_height_in": 5,
"either_orientation": true,
"min_area_sq_in": 1.55,
"step_in": null
},
"quantity": {
"minimum": 1,
"maximum": 10000
},
"artwork": {
"min_dpi": 150,
"refuse_below_dpi": 112.5,
"bleed_in": 0.25
},
"finishes": [],
"price_book": {
"base_cents": 177,
"cents_per_sq_in": 10
},
"plant_confirms_sku": true
},
{
"product": "decal-wall",
"name": "Wall Decal",
"family": "decals",
"sku_template": "wall-decal-{width}x{height}",
"example_sku": "wall-decal-24x36",
"size": {
"unit": "in",
"max_width_in": 96,
"max_height_in": 96,
"either_orientation": true,
"min_area_sq_in": 80.17,
"step_in": null
},
"quantity": {
"minimum": 1,
"maximum": 10000
},
"artwork": {
"min_dpi": 150,
"refuse_below_dpi": 112.5,
"bleed_in": 0.25
},
"finishes": [],
"price_book": {
"base_cents": 0,
"cents_per_sq_in": 3
},
"plant_confirms_sku": true
},
{
"product": "magnet",
"name": "Magnet",
"family": "magnets",
"sku_template": "magnet-{width}x{height}",
"example_sku": "magnet-3x3",
"size": {
"unit": "in",
"max_width_in": 12,
"max_height_in": 12,
"either_orientation": true,
"min_area_sq_in": 6.55,
"step_in": null
},
"quantity": {
"minimum": 1,
"maximum": 10000
},
"artwork": {
"min_dpi": 300,
"refuse_below_dpi": 225,
"bleed_in": 0.125
},
"finishes": [],
"price_book": {
"base_cents": 130,
"cents_per_sq_in": 10
},
"plant_confirms_sku": true
},
{
"product": "poster",
"name": "Poster",
"family": "posters",
"sku_template": "poster-{width}x{height}",
"example_sku": "poster-18x24",
"size": {
"unit": "in",
"max_width_in": 36,
"max_height_in": 48,
"either_orientation": true,
"min_area_sq_in": null,
"step_in": null
},
"quantity": {
"minimum": 1,
"maximum": 10000
},
"artwork": {
"min_dpi": 150,
"refuse_below_dpi": 112.5,
"bleed_in": 0.125
},
"finishes": [],
"price_book": {
"base_cents": 300,
"cents_per_sq_in": 2
},
"plant_confirms_sku": true
}
]
},
"request_id": "4e3753e1-48dd-4313-bfd8-48e4af4e116a"
}Errors
Missing, invalid or expired API key:
missing_api_key(none sent),invalid_api_key(unknown or revoked),key_expired(past the expiry set when it was created; create a new key).Rate limit exceeded. Two windows can produce this status and
error.codesays which:rate_limit_exceededfor the per-minute sliding window,daily_quota_exceededfor the plan's daily quota. Both carryRetry-After; the daily one also carriesX-Daily-LimitandX-Daily-Remaining. The per-minute limits are plan-based — see Rate Limits above, or call GET /v1/me for this credential's own figures.