List orders
GET /api/orders returns every order on your X3D account, website and print API alike, newest first, authenticated by your signed-in session rather than an API key.
There is no REST endpoint that lists orders with an API key. The submit call hands you one order code and that code is the handle you keep — store it the moment you get it. The listing that exists, GET /api/orders, is authenticated by a signed-in browser session and returns every order on the account: website orders and orders placed with the account's API key. It is what /orders and its statement are built from. With a key and no browser, the MCP tool list_my_orders gives the same view, capped at the 20 most recent.
/api/ordersSigned inList the website orders belonging to the signed-in account.
Request#
No path parameters, no query parameters, no body. The account is taken from the session cookie, so there is nothing to pass and no way to ask for somebody else's orders. Call it from a signed-in page in the browser; an API key will not authenticate it.
const res = await fetch("/api/orders", { credentials: "include" });
if (res.status === 401) {
window.location.href = "/auth/login";
} else {
const { orders } = await res.json();
console.log(orders.length, orders[0]?.id, orders[0]?.status);
}Response#
{
"orders": [
{
"id": "2610014",
"email": "[email protected]",
"type": "cart_order",
"status": "shipped",
"total": 24.87,
"items": "2x Hinge bracket — PLA, Black; 1x Cable clip — PETG, Cyan",
"shippingAddress": {
"name": "Ada Lovelace",
"line1": "1100 Congress Ave",
"city": "Austin",
"state": "TX",
"postal_code": "78701",
"country": "US"
},
"trackingNumber": "9400111899223197428490",
"carrier": "usps",
"createdAt": "2026-09-01T18:33:00.000Z",
"updatedAt": "2026-09-04T11:02:11.412Z"
}
]
}The documents are returned exactly as they are stored — nothing is projected away, and optional fields are absent rather than null when they were never set.
idstringrequired- Order id and document key. A print API order's id is its X3D- code. New website orders are numbered by month: two digits of year, two of month, then a count within that month, so 2610014 is the 14th order paid in October 2026 (Central time). A number is given only when an order is paid, so the numbers run without gaps from 2610019 on; before 13 October 2026 a number was taken when the checkout opened, which left gaps where a checkout was never paid. The first numbered orders, 10001 to 10005, keep their numbers. Orders placed before numbering keep their original id: CART- for a cart checkout, ORD- for a single print order, STORE- for a store purchase, each followed by a UUID.
emailstringrequired- The order's email, lowercased. For a print API order it is the order's main contact, which may not be the account's own address.
typestringrequired- Which flow created it. Store purchases are recorded as cart_order.
print_ordercart_order statusstringrequired- Fulfilment stage. Every order is created as placed. An order the customer collects goes placed, queued, printed, packed, picked_up, with no shipped or delivered. cancelled is terminal and off the pipeline, not a stage of its own. A print API order never reaches packed or picked_up. See /docs/studio/orders for what each one means.
placedqueuedprintedpackedshippeddeliveredpicked_upcancelled sourcestringoptional- Where the order came from. api on every print API order; absent on website orders placed before it was recorded.
fulfilmentstringoptional- Present on orders entered by hand: pickup when the customer collects it. Absent means posted.
shippickup totalnumberrequired- Amount charged in USD, including postage. A print API order reports its estimatedCost, or 0 before it was priced.
itemsstringoptional- One-line summary of what was ordered, e.g. "2x Hinge bracket — PLA, Black". With no manifest saved it falls back to a bare count for a cart ("2 items"), or the product title for a single store purchase. Truncated at 900 characters. On a print API order it is the file name.
materialstringoptional- Single-print orders only, print API orders included. The material key, e.g. pla.
colorstringoptional- Single-print orders only. The colour as ordered.
qualitystringoptional- Single-print orders only: standard or premium.
dimensionsstringoptional- Single-print orders only. A human-readable size string.
quantitynumberoptional- Single-print orders only. Units ordered.
modelUrlstringoptional- gs:// URI of the model that was printed. Not fetchable directly by a browser.
shippingAddressobjectoptional- name, line1, line2 (optional), city, state, postal_code, country. Absent on orders where no address was collected.
trackingNumberstringoptional- Set by an operator when the parcel is handed over. Absent until then.
carrierstringoptional- Which carrier the tracking number belongs to. other means we have no tracking URL we trust.
upsfedexuspsother createdAtstringrequired- ISO 8601 timestamp. This is the sort key.
updatedAtstringrequired- ISO 8601 timestamp of the last status or tracking write.
Ordering and limits#
- At most 50 website orders and 100 print API orders come back. The limits are fixed in the queries — there is no cursor, offset or page parameter.
- The two lists are merged and sorted newest first by createdAt, in the application rather than in the database.
- The 100 API orders are the newest 100. The website sort happens after its limit, so an account with more than 50 website orders gets an arbitrary 50 of them sorted, not the newest 50. Keep your own record if you need complete history.
- Website orders are matched on the order's email, and print API orders on the account that placed them, both lowercased and trimmed. There is no parameter that could reach another account's data.
- An account with no orders gets 200 and an empty array, as does a deployment with no order database configured.
| Status | Body | When |
|---|---|---|
| 200 | { "orders": [...] } | Signed in. The array may be empty. |
| 401 | { "error": "unauthorized" } | No session, or a session with no email. Note the lowercase word. |
Tracking orders you submitted with a key#
- 1Save the code on submit
The 201 from POST /api/print/orders carries code, statusUrl and apiStatusUrl. Write the code to your own database against your own order id before you do anything else.
- 2Poll each code you are still waiting on
GET /api/print/orders/{code} with the same key returns status, paid and tracking. Poll on a human timescale — prints take hours.
bashcurl https://x3dstudios.com/api/print/orders/X3D-K7M2QP \ -H "Authorization: Bearer $X3D_API_KEY" - 3Stop polling at a terminal status
DELIVERED, CANCELLED and REJECTED do not change again. Drop those codes from the poll loop.
Questions
Is there an API-key endpoint that lists my orders?
Not on the REST API, which has exactly two order endpoints: POST /api/print/orders to submit, and GET /api/print/orders/{code} to read one order back. The MCP server's list_my_orders tool does take the key: it returns the account's 20 most recent orders from every store, print API orders included. See /docs/api/mcp.
Do orders I submitted with my API key appear in GET /api/orders?
Yes, since 13 September 2026. The response reads both order stores, so an X3D- order placed with the account's key sits in the same list as its website orders, mapped onto the same lowercase stages. Read it in full, with the exact status, through GET /api/print/orders/{code}.
Can I page past the 50 orders it returns?
No. There is no cursor, offset or page parameter, and the limit is fixed in the query. If you need a full history, keep your own record as you create orders.