# Place an order

> Places a single order.

Source: https://docs.stxapp.io/api/rest/orders/place-order/

Places a single order. The body is flat: the order's fields sit at the top level, not wrapped in a `user_order` key. Requires a `read_write` key.

```http
POST /api/v1/orders
```

Send it with your own demo key: [Try it](/quick-start/?op=orders_post#try-it).

:::tip[In the SDKs]
- TypeScript: [`STX.placeOrder()`](/sdks/typescript/reference/stx/#placeorder)
- Python: [`STX.place_order()`](/sdks/python/reference/stx/#place_order)
- C#: [`STXOrderService.ConfirmOrderAsync()`](/sdks/csharp/reference/trading/#stxorderservice)
:::

## Request body

| Field | Type | Required | Description |
|---|---|---|---|
| `action` | `buy` \| `sell` | **yes** | Which side of the book the order takes. |
| `cancel_on_disconnect` | boolean,null | no | Cancel this order if the `orders` channel stops heartbeating. See the cancel_on_disconnect guide. |
| `client_order_id` | string,null | no | Your own reference, echoed back unchanged. A free-form string, not a UUID; FIX clients routinely send ids like `my-order-001`. |
| `device_id` | string,null | no | Identifier for the device placing the order. |
| `expiration` | `good_till_start` \| `good_till_time` \| `null` | no | When the order should stop resting. |
| `expiration_time` | integer,null (int64) | no | The moment a `good_till_time` order expires. Required when `expiration` is `good_till_time`. UNIX microseconds. |
| `geo_location` | string,null | no | Location code from the STX geolocation check, covering this order. Inside a `POST /api/v1/orders/batched` leg it is ignored; send the batch's top-level `geo_location` instead. Read only in environments that enforce geo-fencing, and ignored elsewhere. Where it is read, omitting it is a 422 (`Placing Order(s) from this IP address … is disallowed.`) unless that environment accepts the network location the request arrives from in its place, which it can refuse for an address identified as a VPN, proxy or hosting provider. Do not rely on omitting it: send the code whenever you have one. A code that is sent is always checked, so a bad one is a 422 even where omitting it would have been accepted. |
| `market_id` | string (uuid) | **yes** | The market to place the order on. |
| `order_type` | `limit` \| `market` | **yes** | Whether the order takes a price or rests on the book. |
| `price` | string,null (decimal) | no | Limit price in dollars, as a string: `"0.42"` is 42 cents. A number is rejected outright rather than reinterpreted, because a bare `42` could mean 42 cents or 42 dollars and the wrong reading is off by 100x. Must be strictly less than the market's `max_price`; read that per market rather than assuming a fixed ceiling. Required for `limit` orders and unused by `market` orders, but validated whenever it is present: a malformed or sub-cent price is a 400 on a `market` order too, rather than being quietly dropped. An explicit `null` is accepted there, exactly as omitting the key is. Must be greater than zero and a whole number of cents: at most two decimal places, not counting trailing zeros. So "0.42" and "0.4200" are the same accepted value, and "0.001" is rejected. |
| `quantity` | string | **yes** | Number of contracts, greater than zero, as a decimal string: `"10"` and `"10.00"` are read the same. A number is rejected rather than converted, the same rule `price` follows, though for a different reason: a float arrives as a binary double, so the size that rests on the book would not always be the size that was sent. More than nine decimal places is rounded. Responses always return it as a string. |

## Responses

| Status | Description | Schema |
|---|---|---|
| `200` | Success | object |
| `400` | A parameter was missing or invalid. | Error |
| `401` | Missing, malformed or unrecognized signature, or a timestamp outside the 30-second window. Body: &#123;"error":"Missing or invalid API key credentials"&#125; | Error |
| `403` | This API key does not have write access. The account behind this key is not active, for example it is pending approval or suspended. Body: &#123;"error":"Your account is suspended. Contact support."&#125;, the message naming the account's status. | Error |
| `422` | The order was refused, for example a limit price at or above the market's `max_price`, insufficient funds or a failed location check. The body's `error` says why. | Error |

## Example

Request:

```bash
curl --request POST \
  --url 'https://demo.stxapp.io/api/v1/orders' \
  --header 'X-STX-ACCESS-KEY: <key-id>' \
  --header 'X-STX-ACCESS-TIMESTAMP: <unix-ms>' \
  --header 'X-STX-ACCESS-SIGNATURE: <base64-ed25519>' \
  --header 'Content-Type: application/json' \
  --data '{
  "action": "buy",
  "cancel_on_disconnect": null,
  "client_order_id": null,
  "device_id": null,
  "expiration": null,
  "expiration_time": null,
  "geo_location": null,
  "market_id": "a1b2c3d4-5e6f-4a7b-8c9d-0e1f2a3b4c5d",
  "order_type": "limit",
  "price": "0.42",
  "quantity": "10"
}'
```

Response `200`:

```json
{
  "order": {
    "accepted_at": null,
    "action": "buy",
    "amount": "0.6700",
    "avg_price": "0.6700",
    "cancellation_reason": null,
    "client_order_id": null,
    "delayed_until": null,
    "device_id": null,
    "expiration": null,
    "expiration_time": null,
    "expires_at": null,
    "filled": "2.00",
    "filled_amount": "0.6700",
    "filled_percentage": 0,
    "fix_order": false,
    "id": "a1b2c3d4-5e6f-4a7b-8c9d-0e1f2a3b4c5d",
    "inserted_at": 0,
    "ip_address": null,
    "market_id": "a1b2c3d4-5e6f-4a7b-8c9d-0e1f2a3b4c5d",
    "odds_type": null,
    "odds_value": null,
    "order_type": "limit",
    "placed_pre_start": false,
    "price": "0.4200000",
    "quantity": "2.00",
    "rejection_reason": null,
    "status": "accepted",
    "time": "2026-08-25T04:42:46.242093Z",
    "total_value": "0.6700"
  }
}
```

### Response fields

| Field | Type | Description |
|---|---|---|
| `accepted_at` | int64 | When the matching engine accepted the order. `null` while the order is still pending. UNIX microseconds. |
| `action` | string | The action of the order, either `buy` or `sell`. |
| `amount` | decimal | Order size in dollars, for an order entered by amount rather than by contract quantity. `null` for orders placed through this API, which always take a quantity. |
| `avg_price` | decimal | Volume-weighted average fill price. `null` until the order has its first fill. In dollars. |
| `cancellation_reason` | string | The cancellation reason, if the order was cancelled. |
| `client_order_id` | string | Your own identifier, echoed back unchanged, or `null` if you sent none. A free-form string, not a UUID, and set by REST and FIX callers alike. |
| `delayed_until` | int64 | The extended deadline for a delayed order. UNIX microseconds. |
| `device_id` | string | The device from which this order was placed. |
| `expiration` | string | The expiration condition for the order. |
| `expiration_time` | int64 | The expiration time for time-based expiration. UNIX microseconds. |
| `expires_at` | int64 | When the contracts this order trades expire, as UNIX microseconds: the market's expiration, copied onto the order when it is placed. `null` when the market has no event. Not the same as `expiration_time`. |
| `filled` | decimal | Contracts filled so far. Compare with `quantity` to get remaining size. |
| `filled_amount` | decimal | The portion of `amount` that has been filled. In dollars. |
| `filled_percentage` | integer | The percentage of the contracts on the order that have been filled. A percentage, not money. |
| `fix_order` | boolean | True if the order arrived over FIX rather than REST. |
| `id` | uuid | Unique identifier for the record. |
| `inserted_at` | int64 | Creation time, as UNIX microseconds. |
| `ip_address` | string | The IP address from which this order was placed. |
| `market_id` | uuid | The market this record relates to. |
| `odds_type` | string | Legacy. The odds format (`decimal` or `american`) an order was entered in on an STX app. `null` for orders placed through this API. |
| `odds_value` | string | Legacy. The odds an order was entered at on an STX app, as a string. `null` for orders placed through this API. |
| `order_type` | string | The type of order, either `limit` or `market`. |
| `placed_pre_start` | boolean | Whether the order was placed before the event started. |
| `price` | decimal | Limit price, below the market's `max_price`; read that per market rather than assuming a ceiling. Absent for market orders. In dollars. Carries up to 7 decimal places; parse money with a variable-scale decimal type, not a fixed-width one. |
| `quantity` | decimal | Order size in contracts. |
| `rejection_reason` | string | The rejection reason, if the order was rejected. |
| `status` | string | Order state, one of nine. See [Market and order status](/concepts/market-status/#order-statuses) for the full set and which are terminal. |
| `time` | date-time | The ISO-8601 timestamp of the time the order was placed. |
| `total_value` | decimal | Total premium across all fills on this order. In dollars. |
