# 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.

Source: https://x3dstudios.com/docs/api/list-orders  ·  Updated: 2026-10-16

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.

> **Two order stores, one list** — Website orders and print API orders are stored separately, and this endpoint reads both. An API order comes back with its X3D- code as id, type print_order, source api, and its uppercase status mapped onto the stages below: RECEIVED (and an unpaid PENDING_PAYMENT) as placed, ACCEPTED and PRINTING as queued, REJECTED as cancelled. The exact status is on GET /api/print/orders/{code}.

`GET /api/orders`

List the website orders belonging to the signed-in account.

Auth: session

## 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.

**From a signed-in page**

```javascript
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

**200**

```json
{
  "orders": [
    {
      "id": "2610014",
      "email": "ada@example.com",
      "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.

**Order object**

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | string | yes | 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. |
| `email` | string | yes | 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. |
| `type` | string (print_order \\| cart_order) | yes | Which flow created it. Store purchases are recorded as cart_order. |
| `status` | string (placed \\| queued \\| printed \\| packed \\| shipped \\| delivered \\| picked_up \\| cancelled) | yes | 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. |
| `source` | string | no | Where the order came from. api on every print API order; absent on website orders placed before it was recorded. |
| `fulfilment` | string (ship \\| pickup) | no | Present on orders entered by hand: pickup when the customer collects it. Absent means posted. |
| `total` | number | yes | Amount charged in USD, including postage. A print API order reports its estimatedCost, or 0 before it was priced. |
| `items` | string | no | 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. |
| `material` | string | no | Single-print orders only, print API orders included. The material key, e.g. pla. |
| `color` | string | no | Single-print orders only. The colour as ordered. |
| `quality` | string | no | Single-print orders only: standard or premium. |
| `dimensions` | string | no | Single-print orders only. A human-readable size string. |
| `quantity` | number | no | Single-print orders only. Units ordered. |
| `modelUrl` | string | no | gs:// URI of the model that was printed. Not fetchable directly by a browser. |
| `shippingAddress` | object | no | name, line1, line2 (optional), city, state, postal_code, country. Absent on orders where no address was collected. |
| `trackingNumber` | string | no | Set by an operator when the parcel is handed over. Absent until then. |
| `carrier` | string (ups \\| fedex \\| usps \\| other) | no | Which carrier the tracking number belongs to. other means we have no tracking URL we trust. |
| `createdAt` | string | yes | ISO 8601 timestamp. This is the sort key. |
| `updatedAt` | string | yes | 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

1. **Save 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.

2. **Poll 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.

```bash
curl https://x3dstudios.com/api/print/orders/X3D-K7M2QP \
  -H "Authorization: Bearer $X3D_API_KEY"
```

3. **Stop polling at a terminal status** — DELIVERED, CANCELLED and REJECTED do not change again. Drop those codes from the poll loop.

> **If you lose a code** — There is no lookup by address or file name. The code is in the confirmation email sent once the order is paid, in the URL of that order's status page, on /orders when you are signed in as the account that placed it, and in the MCP tool list_my_orders. If none of those has it, ask us at /contact.

- [Order status](https://x3dstudios.com/docs/api/order-status) — Read one order by its code, with the full list of status values.
- [Submit a print order](https://x3dstudios.com/docs/api/submit-order) — The endpoint that creates the code in the first place.
- [Orders in Studio](https://x3dstudios.com/docs/studio/orders) — What each fulfilment stage means and when we email you.

## Frequently asked

**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.
