Alpaca Broker API — Trading on Behalf of Accounts
Place, modify, cancel, and track orders for an end-user account, and read positions & buying power. The defining feature of Broker API trading: account_id is in the path — you act for a user account, not your own.
Read alpaca-broker-integration first. Broker API + HTTP Basic auth. (The standalone Trading API uses /v2/orders with no account in the path; everything else here transfers.)
Reference
- Guides:
https://docs.alpaca.markets/docs/orders-at-alpaca, https://docs.alpaca.markets/docs/fractional-trading
- API ref:
https://docs.alpaca.markets/reference/postorder
- Live schema:
alpaca-docs MCP → get-endpoint title "Broker API" path /v1/trading/accounts/{account_id}/orders
1. Endpoints
| Method |
Path |
Purpose |
| POST |
/v1/trading/accounts/{id}/orders |
Create order |
| GET |
/v1/trading/accounts/{id}/orders |
List orders (filter by status, symbols, after…) |
| GET |
/v1/trading/accounts/{id}/orders/{order_id} |
Get order by ID |
| GET |
/v1/trading/accounts/{id}/orders:by_client_order_id?client_order_id=… |
Get by your client ID |
| PATCH |
/v1/trading/accounts/{id}/orders/{order_id} |
Replace (modify) order |
| DELETE |
/v1/trading/accounts/{id}/orders/{order_id} |
Cancel one order (204) |
| DELETE |
/v1/trading/accounts/{id}/orders |
Cancel all (207 Multi-Status) |
| POST |
/v1/trading/accounts/{id}/orders/estimation |
Cost-estimate an order |
| GET / DELETE |
/v1/trading/accounts/{id}/positions[/{symbol_or_asset_id}] |
List / close positions |
| GET |
/v1/trading/accounts/{id}/account |
Trading-account details (buying power etc.) |
2. Create-order request
Schema-required: type and time_in_force. Conditionally required: symbol, side, and exactly one of qty/notional.
// notional market buy (dollar-based, fractional)
{ "symbol": "AAPL", "notional": "25.00", "side": "buy", "type": "market", "time_in_force": "day",
"client_order_id": "your-own-uuid" }
// limit qty sell
{ "symbol": "AAPL", "qty": "3", "side": "sell", "type": "limit", "limit_price": "190.00", "time_in_force": "gtc" }
| Field |
Values / notes |
symbol |
required (except mleg multi-leg options) |
qty |
decimal string, up to 9 dp. Fractional only for market+day |
notional |
decimal string, up to 9 dp. Mutually exclusive with qty |
side |
buy, sell (plus advanced: sell_short, …) |
type |
market, limit, stop, stop_limit, trailing_stop |
time_in_force |
day, gtc, opg, cls, ioc, fok |
limit_price / stop_price |
required for limit/stop variants |
trail_price / trail_percent |
one required for trailing_stop |
extended_hours |
bool; only with type=limit and TIF day/gtc |
client_order_id |
≤128 chars; your idempotency key (auto-generated if omitted) |
order_class |
simple (default), bracket, oco, oto, mleg |
take_profit / stop_loss |
{limit_price} / {stop_price, limit_price?} for bracket/oco/oto |
position_intent |
buy_to_open, sell_to_close, … |
qty XOR notional (verbatim rule): pass one or the other — supplying both → 400. In the response, whichever you didn't use comes back null.
3. Fractional / notional rules
- On by default for all accounts (live + paper).
- Asset must have
fractionable: true (check the Assets API — see alpaca-broker-market-data), else requested asset is not fractionable.
- TIF must be
day for fractional/notional.
- Notional is limited to
market and limit (day); only limit for extended hours. Fractional qty additionally allows stop/stop_limit per the guide.
- No shorting fractional — all fractional sells are marked long.
- Precision: up to 9 decimal places for both
qty and notional.
4. Order status lifecycle
OrderStatus (the order object's status): new, partially_filled, filled, done_for_day, canceled, expired, replaced, pending_cancel, pending_replace, accepted, pending_new, accepted_for_bidding, stopped, rejected, suspended, calculated.
Order status ≠ trade-event event. The order object's status is the enum above. The SSE trade-update stream reports a richer event enum that adds operational events not present as a status — including held (multi-leg secondary legs awaiting trigger), trade_bust, trade_correct, restated, order_cancel_rejected, order_replace_rejected. So held exists as a trade event but never as an order status. See alpaca-broker-sse-events.
Terminal: filled, canceled, expired, rejected (and replaced for the original order). Everything else is in-flight.
Early-state distinctions (these trip people up):
accepted — received by Alpaca, not yet routed to a venue (common outside market hours).
new — received and routed to exchanges; the usual initial live state.
pending_new — routed but not yet accepted for execution (rare).
So the typical opening sequence is accepted → pending_new → new, then fills. Lesson: treat new/accepted/pending_new as "exists but not done." Persist the order on submit, then update on fill/cancel/reject events — don't block the user waiting for a terminal state synchronously.
5. Positions & trading account
Position key fields: symbol, asset_id, qty, qty_available (free of open orders), side (long/short), avg_entry_price, market_value, cost_basis, unrealized_pl, unrealized_plpc, current_price, change_today.
TradeAccount key fields:
buying_power (with margin multiplier 1–4), cash, cash_withdrawable, equity, last_equity.
- Blockers:
trading_blocked, account_blocked, transfers_blocked, trade_suspended_by_user.
multiplier, regt_buying_power, non_marginable_buying_power, long_market_value, initial_margin, maintenance_margin, sma.
Lesson — check buying power before notional orders. For a "spend $X" UX, read buying_power/cash first and reject/notify on insufficient funds, rather than letting Alpaca reject the order. (Cache it per account within a batch run to avoid re-fetching.)
PDT/day-trade fields are deprecated (since 2026-04-27, sunset 2026-07-06) following FINRA's intraday-margin rule change: daytrade_count, pattern_day_trader, daytrading_buying_power, bod_dtbp, plus config dtbp_check/pdt_check. They still exist in the schema today but stop relying on them.
6. Documented gotchas
- Wash-trade rejection (403): if a user's two orders could self-cross (opposite sides, crossable prices), Alpaca rejects. Opposing market/stop pairs are always rejected; opposing limits rejected when buy-limit ≥ sell-limit. Use
bracket/oco/trailing_stop for simultaneous take-profit + stop-loss — they're exempt.
- Bracket constraints: requires both
take_profit.limit_price and stop_loss.stop_price; TP must be above SL for a buy; no extended hours; TIF day/gtc; child legs activate only after the entry fully fills; canceling one cancels the group.
- Notional orders can't be replaced — cancel and resubmit (IPO-class notional is the exception). Fractional
qty can't be changed on replace ("full shares only").
- Replace ≠ guaranteed: a
200 from PATCH can still be rejected if the original fills first; watch the trade-updates stream. Can't replace while accepted/pending_new/pending_cancel/pending_replace.
- Cancel semantics: single cancel →
204, or 422 if no longer cancelable; cancel-all → 207 per-order results; close-all positions → 207. Close-single accepts mutually-exclusive qty or percentage.
7. Idempotency & recurring-invest lessons
- Always set
client_order_id from your own transaction record. It's your dedup key and lets you look the order up (orders:by_client_order_id) if the create response is lost. Note it dedups lookup, not necessarily replay — combine it with a local "already-submitted?" guard.
- Recurring/scheduled buys (lesson): the robust pattern is — fetch pending invest instructions from your DB → check buying power → place a
notional market/day order per instruction → record the returned order → mark the instruction done only after a successful create. On insufficient funds, cancel the instruction and notify, don't silently skip. Schedule the batch shortly before market open and respect the market clock (alpaca-broker-market-data).
- Track fills via the trade events SSE stream, not by polling each order — see
alpaca-broker-sse-events.
Related skills: prices/assets/clock → alpaca-broker-market-data; fills in real time → alpaca-broker-sse-events; rate limits on bulk placement → alpaca-broker-rate-limits-resilience; money formatting → alpaca-broker-money-precision.
1---2name: alpaca-broker-trading-orders3description: Place and manage orders on behalf of accounts via the Alpaca Broker API — order creation (qty vs notional, fractional shares, order types/TIF/classes), order status lifecycle, replace/cancel, positions, and trading-account buying power. Use when building trading, recurring-invest, or portfolio flows on Alpaca in any language.4---56# Alpaca Broker API — Trading on Behalf of Accounts78Place, modify, cancel, and track orders for an end-user account, and read positions & buying power. The defining feature of Broker API trading: **`account_id` is in the path** — you act *for* a user account, not your own.910> Read `alpaca-broker-integration` first. Broker API + HTTP Basic auth. (The standalone Trading API uses `/v2/orders` with no account in the path; everything else here transfers.)1112## Reference13- Guides: `https://docs.alpaca.markets/docs/orders-at-alpaca`, `https://docs.alpaca.markets/docs/fractional-trading`14- API ref: `https://docs.alpaca.markets/reference/postorder`15- Live schema: `alpaca-docs` MCP → `get-endpoint` title `"Broker API"` path `/v1/trading/accounts/{account_id}/orders`1617## 1. Endpoints1819| Method | Path | Purpose |20|--------|------|---------|21| POST | `/v1/trading/accounts/{id}/orders` | Create order |22| GET | `/v1/trading/accounts/{id}/orders` | List orders (filter by `status`, `symbols`, `after`…) |23| GET | `/v1/trading/accounts/{id}/orders/{order_id}` | Get order by ID |24| GET | `/v1/trading/accounts/{id}/orders:by_client_order_id?client_order_id=…` | Get by your client ID |25| PATCH | `/v1/trading/accounts/{id}/orders/{order_id}` | Replace (modify) order |26| DELETE | `/v1/trading/accounts/{id}/orders/{order_id}` | Cancel one order (204) |27| DELETE | `/v1/trading/accounts/{id}/orders` | Cancel all (207 Multi-Status) |28| POST | `/v1/trading/accounts/{id}/orders/estimation` | Cost-estimate an order |29| GET / DELETE | `/v1/trading/accounts/{id}/positions[/{symbol_or_asset_id}]` | List / close positions |30| GET | `/v1/trading/accounts/{id}/account` | Trading-account details (buying power etc.) |3132## 2. Create-order request3334Schema-required: `type` and `time_in_force`. Conditionally required: `symbol`, `side`, and exactly one of `qty`/`notional`.3536```json37// notional market buy (dollar-based, fractional)38{ "symbol": "AAPL", "notional": "25.00", "side": "buy", "type": "market", "time_in_force": "day",39 "client_order_id": "your-own-uuid" }4041// limit qty sell42{ "symbol": "AAPL", "qty": "3", "side": "sell", "type": "limit", "limit_price": "190.00", "time_in_force": "gtc" }43```4445| Field | Values / notes |46|-------|----------------|47| `symbol` | required (except `mleg` multi-leg options) |48| `qty` | decimal **string**, up to 9 dp. Fractional only for `market`+`day` |49| `notional` | decimal **string**, up to 9 dp. **Mutually exclusive with `qty`** |50| `side` | `buy`, `sell` (plus advanced: `sell_short`, …) |51| `type` | `market`, `limit`, `stop`, `stop_limit`, `trailing_stop` |52| `time_in_force` | `day`, `gtc`, `opg`, `cls`, `ioc`, `fok` |53| `limit_price` / `stop_price` | required for limit/stop variants |54| `trail_price` / `trail_percent` | one required for `trailing_stop` |55| `extended_hours` | bool; only with `type=limit` and TIF `day`/`gtc` |56| `client_order_id` | ≤128 chars; **your idempotency key** (auto-generated if omitted) |57| `order_class` | `simple` (default), `bracket`, `oco`, `oto`, `mleg` |58| `take_profit` / `stop_loss` | `{limit_price}` / `{stop_price, limit_price?}` for bracket/oco/oto |59| `position_intent` | `buy_to_open`, `sell_to_close`, … |6061**qty XOR notional (verbatim rule):** pass one or the other — supplying both → `400`. In the response, whichever you didn't use comes back `null`.6263## 3. Fractional / notional rules6465- **On by default** for all accounts (live + paper).66- Asset must have **`fractionable: true`** (check the Assets API — see `alpaca-broker-market-data`), else `requested asset is not fractionable`.67- **TIF must be `day`** for fractional/notional.68- **Notional** is limited to `market` and `limit` (day); only `limit` for extended hours. Fractional `qty` additionally allows `stop`/`stop_limit` per the guide.69- **No shorting fractional** — all fractional sells are marked long.70- Precision: up to **9 decimal places** for both `qty` and `notional`.7172## 4. Order status lifecycle7374`OrderStatus` (the order object's `status`): `new`, `partially_filled`, `filled`, `done_for_day`, `canceled`, `expired`, `replaced`, `pending_cancel`, `pending_replace`, `accepted`, `pending_new`, `accepted_for_bidding`, `stopped`, `rejected`, `suspended`, `calculated`.7576> **Order `status` ≠ trade-event `event`.** The order object's `status` is the enum above. The **SSE trade-update stream** reports a *richer* `event` enum that adds operational events not present as a status — including `held` (multi-leg secondary legs awaiting trigger), `trade_bust`, `trade_correct`, `restated`, `order_cancel_rejected`, `order_replace_rejected`. So `held` exists as a trade *event* but never as an order *status*. See `alpaca-broker-sse-events`.7778**Terminal:** `filled`, `canceled`, `expired`, `rejected` (and `replaced` for the original order). **Everything else is in-flight.**7980**Early-state distinctions (these trip people up):**81- `accepted` — received by Alpaca, not yet routed to a venue (common outside market hours).82- `new` — received **and routed to exchanges**; the usual initial live state.83- `pending_new` — routed but not yet accepted for execution (rare).8485So the typical opening sequence is `accepted → pending_new → new`, then fills. **Lesson:** treat `new`/`accepted`/`pending_new` as "exists but not done." Persist the order on submit, then update on fill/cancel/reject events — don't block the user waiting for a terminal state synchronously.8687## 5. Positions & trading account8889**`Position`** key fields: `symbol`, `asset_id`, `qty`, `qty_available` (free of open orders), `side` (`long`/`short`), `avg_entry_price`, `market_value`, `cost_basis`, `unrealized_pl`, `unrealized_plpc`, `current_price`, `change_today`.9091**`TradeAccount`** key fields:92- `buying_power` (with margin `multiplier` 1–4), `cash`, `cash_withdrawable`, `equity`, `last_equity`.93- Blockers: `trading_blocked`, `account_blocked`, `transfers_blocked`, `trade_suspended_by_user`.94- `multiplier`, `regt_buying_power`, `non_marginable_buying_power`, `long_market_value`, `initial_margin`, `maintenance_margin`, `sma`.9596**Lesson — check buying power before notional orders.** For a "spend $X" UX, read `buying_power`/`cash` first and reject/notify on insufficient funds, rather than letting Alpaca reject the order. (Cache it per account within a batch run to avoid re-fetching.)9798> **PDT/day-trade fields are deprecated** (since 2026-04-27, sunset 2026-07-06) following FINRA's intraday-margin rule change: `daytrade_count`, `pattern_day_trader`, `daytrading_buying_power`, `bod_dtbp`, plus config `dtbp_check`/`pdt_check`. They still exist in the schema today but stop relying on them.99100## 6. Documented gotchas101102- **Wash-trade rejection (403):** if a user's two orders could self-cross (opposite sides, crossable prices), Alpaca rejects. Opposing market/stop pairs are always rejected; opposing limits rejected when buy-limit ≥ sell-limit. **Use `bracket`/`oco`/`trailing_stop` for simultaneous take-profit + stop-loss** — they're exempt.103- **Bracket constraints:** requires both `take_profit.limit_price` and `stop_loss.stop_price`; TP must be above SL for a buy; no extended hours; TIF `day`/`gtc`; child legs activate only after the entry fully fills; canceling one cancels the group.104- **Notional orders can't be replaced** — cancel and resubmit (IPO-class notional is the exception). Fractional `qty` can't be changed on replace ("full shares only").105- **Replace ≠ guaranteed:** a `200` from PATCH can still be rejected if the original fills first; watch the trade-updates stream. Can't replace while `accepted`/`pending_new`/`pending_cancel`/`pending_replace`.106- **Cancel semantics:** single cancel → `204`, or `422` if no longer cancelable; cancel-all → `207` per-order results; close-all positions → `207`. Close-single accepts mutually-exclusive `qty` or `percentage`.107108## 7. Idempotency & recurring-invest lessons109110- **Always set `client_order_id`** from your own transaction record. It's your dedup key and lets you look the order up (`orders:by_client_order_id`) if the create response is lost. Note it dedups *lookup*, not necessarily *replay* — combine it with a local "already-submitted?" guard.111- **Recurring/scheduled buys (lesson):** the robust pattern is — fetch pending invest instructions from your DB → check buying power → place a `notional` `market`/`day` order per instruction → record the returned order → mark the instruction done **only after** a successful create. On insufficient funds, cancel the instruction and notify, don't silently skip. Schedule the batch shortly **before** market open and respect the market clock (`alpaca-broker-market-data`).112- Track fills via the **trade events SSE stream**, not by polling each order — see `alpaca-broker-sse-events`.113114**Related skills:** prices/assets/clock → `alpaca-broker-market-data`; fills in real time → `alpaca-broker-sse-events`; rate limits on bulk placement → `alpaca-broker-rate-limits-resilience`; money formatting → `alpaca-broker-money-precision`.