Limits and Errors

Before you retry anything. Rate limits, error codes, and how to retry safely.

Updated on
Limits and Errors

In short

  • Limits are per account and per endpoint over a rolling hour, shared by every key on the account: 2,000 reads, 1,000 product creates, 500 of the other writes, and 1,000 new orders per 24 hours. A call that fails with 400 still counts.
  • Over a limit you get 429 { "error": "Rate limit exceeded" } with no Retry-After header. Wait at least a minute, then back off further.
  • Every error is JSON with an error message, sometimes an errors array naming the items that failed, and on 402 a credits object.
  • Reads are always safe to retry. Writes are safe when they carry an idempotent key: external_order_number on orders, the SKU on products and bundles. A 409 or an “already exists” 400 on a retry means the first attempt worked.
  • The one call to never retry blind is POST /api-bulk-list without explicit SKUs, because the generated SKUs get a number added and you create duplicate drafts. Check GET /api-bulk-list first.

Rate limits

Endpoints Limit
GET /api-get-pricing, GET /api-get-product, GET /api-get-order, GET /api-get-templates, GET /api-get-bundle, GET /api-bulk-list 2,000 an hour
POST /api-create-product 1,000 an hour
PUT /api-update-product, DELETE /api-delete-product, POST /api-bulk-list, POST /api-create-bundle, DELETE /api-delete-bundle 500 an hour
POST /api-create-order 1,000 new orders per 24 hours
  • Each limit is a rolling window per endpoint, so hitting the product limit doesn’t stop you reading orders.
  • Limits are per account, not per key. Extra keys don’t raise them.
  • The limit is counted before the body is checked, so invalid requests that come back 400 use it up like any other. Validate on your side first.
  • Budget reads for what matters: for many orders, use the list form of GET /api-get-order (25 per page) rather than one read per order, and poll every 30 to 60 minutes, which is as fast as orders move.

Error format

{ "error": "SKU must be 50 characters or less." }

{ "error": "1 of 2 images failed validation. Nothing was created.",
  "errors": [ { "index": 1, "image_url": "https://example.com/oak-leaf.png", "error": "Artwork is 2:3 (1024x1536px) but template \"Botanical\" is A-series. Use a 2:3 template or resize the file." } ] }

{ "error": "Not enough credits: this request needs 12 and you have 4. Top up or send fewer images.",
  "credits": { "cost": 12, "available": 4 } }

Successful responses carry "success": true. Keys look like psk_live_ plus 32 characters, and they don’t expire; a revoked key returns 401 immediately.

Status codes

Code Meaning What to do
400 The request is invalid. The message names the field or item. Fix it and resend. Don’t resend the same body.
401 Missing or invalid API key (no key, or the wrong prefix) or Invalid API key (unknown or revoked). Don’t retry. Check the header, then ask the seller to check Tools → Developer (see Get Access).
402 Not enough credits for the whole POST /api-bulk-list request; credits shows the cost and what’s available. Nothing was created. Send fewer images, use "generate": false, or wait for the seller’s allowance to reset or upgrade.
404 Not found: a product by SKU, a bundle, an order, or pricing for a country. For a product or bundle, create it; for an order, check the id or external number; for pricing, check the country code.
405 Method not allowed. Use the method on the reference: GET for reads, POST to create, PUT to update, DELETE to delete (POST is accepted as an alias for the two deletes).
409 Already exists: a product SKU, a bundle SKU, a Bulk List SKU already used by a product or draft, or a product that other records still use on delete. On a retry, treat it as done and read the record back. On a first attempt, pick a different SKU or read what’s there.
429 Rate limit exceeded, with no Retry-After header. Wait at least 60 seconds, then back off further (see below).
500 Our error; the wording varies by endpoint. For Bulk List, a 500 with success: false and "No draft could be created. See drafts[].error." means every image failed individually. Retry a read after a few seconds. For a write, read the record first to see whether it went through, then retry if it didn’t.

The 400s you will meet most

  • Orders: Missing customer information: zip. All of these are required: name, address1, city, zip, country · Country must be a 2-letter ISO code (e.g. "GB", "US", "AU"). Received: "UK" · State/province is required for United States. Please provide a "state" field. · Shipping option "priority" is only available for United Kingdom. Use "standard" for other countries. · Product[0]: Missing required field: paper_type. Accepted values: Matte, Satin, Gloss · Product[1]: Frame orders require a "variant" field. Accepted values: Black, White, Oak · Product[0]: Provide either "sku" (to reference a saved product) or "image.artwork_url" (to provide artwork directly), not both. · An order with external_order_number "SHOP-1001" already exists.
  • Products: Missing required field: name · SKU must be 50 characters or less. · Too many variants. Maximum 22 variants per request. · variants[1]: Invalid size "a4". Accepted values: ... (sizes are case-sensitive) · variants[0]: Duplicate size "A4". ... · variants[0]: Invalid image_url: <reason> · the account has reached 25,000 products.
  • Pricing: Missing required query parameter: country · Unknown country: XX.
  • Bulk List and bundles: a 400 with an errors array, one entry per failing item with its index and reason, and nothing created. Fix the items named and resend the whole request.

Retrying safely

Call Safe to retry? Why
All GET calls Yes Read-only.
POST /api-create-order Yes, with the same external_order_number A duplicate is refused with 400 already exists, so a retry either places the order or tells you it was placed; look it up with GET /api-get-order. Without an external number a retry can create a second real order, so always send one.
POST /api-create-product Yes A repeat with the same SKU is a 409, and the first attempt stands. Read it back with GET /api-get-product.
PUT /api-update-product Yes Sending the same change twice leaves the product in the same state.
DELETE /api-delete-product, DELETE /api-delete-bundle Yes A repeat is a 404, meaning it is already gone.
POST /api-create-bundle Yes A repeat with the same SKU is a 409.
POST /api-bulk-list with explicit items[].sku Yes A repeat is refused with 409 and nothing is created.
POST /api-bulk-list without SKUs No, not blind SKUs default to file names, and a name already in use gets a number added, so a repeat creates duplicate drafts and spends credits. List the newest drafts with GET /api-bulk-list first and resend only the images that are missing.

Timeouts

A timeout or a dropped connection on a write means you don’t know whether it happened. Don’t guess: read first. GET /api-get-order?external_order_number=..., GET /api-get-product?sku=..., GET /api-get-bundle?sku=... and the list form of GET /api-bulk-list each tell you in one call, and then you retry only what is missing. Give every order an external number and every product and bundle a SKU you chose, and this is always possible.

A backoff that works

  • On 429: wait 60 seconds, then 2, 4 and 8 minutes between further attempts. Since the window is a rolling hour, spreading calls out beats bursting.
  • On 500 for a read: retry after a few seconds, up to three times, then stop and report it.
  • On 400, 401, 404, 405 and 409: don’t retry the same request. Each one is telling you something to change or something already done.
  • On a timeout: read, then retry the write if needed.

Other limits

What Limit
Prints per order 50, after bundles are split; one line per print
Sizes per product call 22 (create or update); use further update calls for more
Images per Bulk List call 1 to 10
Products per bundle 1 to 10, making 2 to 10 prints counting quantities
Products per account 25,000
Drafts in the Listing Hub 500 (5,000 on the Max plan)
API keys 10 active per account
Files 50 MB, JPEG or PNG (PDF
CoFounder
Updated on