Paper Trade API
Paper Trade maintains one simulated Brokerage Account per Wealth Manager Investor and exposes on-demand Tradable Security data.
# Base URL
https://api.papertrade.example
# Every business operation is authenticated
Authorization: Bearer <credential>Introduction
Paper Trade is a private, API-only service that maintains exactly one simulated Brokerage Account per Wealth Manager Investor and exposes on-demand Tradable Security data.
Every business operation is authenticated with an opaque shared service credential. The reference below documents each resource, its supported methods, request shapes, and the full set of success and error responses.
All monetary fields ending in Cents are integer cents. Share quantities are positive whole shares. Request values are restricted to JavaScript safe integers where stated.
Authentication
All business operations require the opaque shared service credential passed in an Authorization: Bearer <credential> header. The credential is not a JWT — it is an opaque shared secret configured server-side as PAPER_TRADE_SERVICE_CREDENTIAL.
OPTIONS and unsupported-method responses do not authenticate. A missing, malformed, or incorrect credential returns 401 unauthorized.
Keep the credential server-side. It grants full access to every Investor account and should never be exposed to browser code.
Authorization: Bearer <credential>Idempotency
Every account mutation requires an Idempotency-Key header. Keys are scoped to the exact Investor and retained with terminal results.
Repeating the same key with a normalized-identical request replays the original status and body. Reusing the same key for a different request returns 409 idempotency_conflict.
Additional properties on mutation bodies are accepted, ignored, and excluded from the idempotency fingerprint.
Idempotency-Key: deposit-investor-123-1Method handling
Documented HEAD operations are provided by Next.js by invoking GET and suppressing the body.
Unsupported exported methods return 405 with {"error":{"code":"method_not_allowed","message":"Method not allowed."}} and the same Allow value shown by the path’s OPTIONS operation. These rejecting methods are intentionally omitted as OpenAPI operations.
OPTIONS responses expose Allow only; they are not complete CORS preflight responses and emit no Access-Control-Allow-* headers.
{
"error": {
"code": "method_not_allowed",
"message": "Method not allowed."
}
}Brokerage Accounts
Brokerage Account lifecycle, balances, Positions, and Account Activity.
/api/investors/{investorId}/accountGet a Brokerage Account
Returns durable cash, cumulative Realized Gain or Loss, and Positions. No market data is requested. Positions are ordered by Ticker ascending.
Parameters
investorIdpathstringrequiredExact, opaque Investor identifier. The application does not trim, normalize, length-limit, or pattern-check this value.
Responses
200Brokerage Account found.
200 response { "investorId": "investor-123", "availableCashCents": 1000000, "realizedGainLossCents": 0, "positions": [] }401Bearer credential is missing, malformed, or incorrect.
401 response { "error": { "code": "unauthorized", "message": "A valid bearer credential is required." } }404No Brokerage Account exists for the exact Investor ID.
404 response { "error": { "code": "not_found", "message": "Brokerage Account not found." } }500The route caught an unexpected failure, including missing credential configuration and database failures.
500 response { "error": { "code": "internal_error", "message": "The request could not be completed." } }
curl -X GET "https://api.papertrade.example/api/investors/investor-123/account" \
-H "Authorization: Bearer <credential>"{
"investorId": "investor-123",
"availableCashCents": 1000000,
"realizedGainLossCents": 0,
"positions": []
}/api/investors/{investorId}/accountCreate a Brokerage Account
Creates the account and its starting_cash Account Activity atomically. One account is allowed per exact Investor ID. An exact idempotent replay returns the original 201 body and does not create another activity.
Parameters
investorIdpathstringrequiredExact, opaque Investor identifier. The application does not trim, normalize, length-limit, or pattern-check this value.
Idempotency-KeyheaderstringrequiredInvestor-scoped mutation key containing at least one non-whitespace character. The original value is retained as the key; no maximum length is enforced.
Request body
Starting cash for the new account, in integer cents.
Responses
201Account created, or the original successful result replayed.
201 response { "investorId": "investor-123", "availableCashCents": 1000000, "realizedGainLossCents": 0, "positions": [] }400Body or Idempotency-Key is missing, malformed, or invalid.
400 response { "error": { "code": "invalid_request", "message": "startingCashCents must be a non-negative safe integer and Idempotency-Key is required." } }401Bearer credential is missing, malformed, or incorrect.
401 response { "error": { "code": "unauthorized", "message": "A valid bearer credential is required." } }409The idempotency key conflicts, or the Investor already has an account.
409 response { "error": { "code": "account_already_exists", "message": "A Brokerage Account already exists for this Investor." } }500The route caught an unexpected failure, including missing credential configuration and database failures.
500 response { "error": { "code": "internal_error", "message": "The request could not be completed." } }
curl -X POST "https://api.papertrade.example/api/investors/investor-123/account" \
-H "Authorization: Bearer <credential>" \
-H "Idempotency-Key: deposit-investor-123-1" \
-H "Content-Type: application/json" \
-d '{ "startingCashCents": 1000000 }'{
"startingCashCents": 1000000
}{
"investorId": "investor-123",
"availableCashCents": 1000000,
"realizedGainLossCents": 0,
"positions": []
}/api/investors/{investorId}/accountCheck for a Brokerage Account
Runs the GET operation, including authentication and database reads, but returns no body.
Parameters
investorIdpathstringrequiredExact, opaque Investor identifier. The application does not trim, normalize, length-limit, or pattern-check this value.
Responses
- 200Brokerage Account found; body omitted.
- 401Bearer credential is missing or invalid; body omitted.
- 404Brokerage Account not found; body omitted.
- 500The request could not be completed; body omitted.
curl -X HEAD "https://api.papertrade.example/api/investors/investor-123/account" \
-H "Authorization: Bearer <credential>"/api/investors/{investorId}/accountNo authList supported account methods
Returns Allow: GET, HEAD, OPTIONS, POST. No body and no authentication.
Parameters
investorIdpathstringrequiredExact, opaque Investor identifier. The application does not trim, normalize, length-limit, or pattern-check this value.
Responses
- 204Supported methods returned; no response body. Allow: GET, HEAD, OPTIONS, POST.
curl -X OPTIONS "https://api.papertrade.example/api/investors/investor-123/account"/api/investors/{investorId}/account/depositsDeposit cash
Adds cash and records one cash_deposit Account Activity atomically. A successful exact replay returns the original account snapshot without moving cash again. A terminal 404 or 422 is also retained for replay. Allow: OPTIONS, POST.
Parameters
investorIdpathstringrequiredExact, opaque Investor identifier. The application does not trim, normalize, length-limit, or pattern-check this value.
Idempotency-KeyheaderstringrequiredInvestor-scoped mutation key containing at least one non-whitespace character. The original value is retained as the key; no maximum length is enforced.
Request body
Cash amount to deposit, in positive integer cents.
Responses
200Deposit committed, or the original successful result replayed.
200 response { "investorId": "investor-123", "availableCashCents": 1025000, "realizedGainLossCents": 0, "positions": [] }400Body, amount, or Idempotency-Key is missing, malformed, or invalid.
400 response { "error": { "code": "invalid_request", "message": "amountCents must be a positive safe integer and Idempotency-Key is required." } }401Bearer credential is missing, malformed, or incorrect.
401 response { "error": { "code": "unauthorized", "message": "A valid bearer credential is required." } }404No Brokerage Account exists for the exact Investor ID.
404 response { "error": { "code": "not_found", "message": "Brokerage Account not found." } }409The Investor-scoped key was already used with a different normalized request.
409 response { "error": { "code": "idempotency_conflict", "message": "Idempotency key was already used with a different request." } }422The deposit would make Available Cash exceed the supported safe-integer limit.
422 response { "error": { "code": "cash_limit_exceeded", "message": "Cash Deposit would exceed the supported cash limit." } }500The route caught an unexpected failure, including missing credential configuration and database failures.
500 response { "error": { "code": "internal_error", "message": "The request could not be completed." } }
curl -X POST "https://api.papertrade.example/api/investors/investor-123/account/deposits" \
-H "Authorization: Bearer <credential>" \
-H "Idempotency-Key: deposit-investor-123-1" \
-H "Content-Type: application/json" \
-d '{ "amountCents": 25000 }'{
"amountCents": 25000
}{
"investorId": "investor-123",
"availableCashCents": 1025000,
"realizedGainLossCents": 0,
"positions": []
}/api/investors/{investorId}/account/depositsNo authList supported deposit methods
Returns Allow: OPTIONS, POST. No body and no authentication.
Parameters
investorIdpathstringrequiredExact, opaque Investor identifier. The application does not trim, normalize, length-limit, or pattern-check this value.
Responses
- 204Supported methods returned; no response body. Allow: OPTIONS, POST.
curl -X OPTIONS "https://api.papertrade.example/api/investors/investor-123/account/deposits"/api/investors/{investorId}/account/withdrawalsWithdraw cash
Subtracts cash and records one cash_withdrawal Account Activity atomically. Withdrawing the entire Available Cash balance is allowed. Concurrent withdrawals are serialized and cannot overdraw the account. Allow: OPTIONS, POST.
Parameters
investorIdpathstringrequiredExact, opaque Investor identifier. The application does not trim, normalize, length-limit, or pattern-check this value.
Idempotency-KeyheaderstringrequiredInvestor-scoped mutation key containing at least one non-whitespace character. The original value is retained as the key; no maximum length is enforced.
Request body
Cash amount to withdraw, in positive integer cents.
Responses
200Withdrawal committed, or the original successful result replayed.
200 response { "investorId": "investor-123", "availableCashCents": 970000, "realizedGainLossCents": 0, "positions": [] }400Body, amount, or Idempotency-Key is missing, malformed, or invalid.
400 response { "error": { "code": "invalid_request", "message": "amountCents must be a positive safe integer and Idempotency-Key is required." } }401Bearer credential is missing, malformed, or incorrect.
401 response { "error": { "code": "unauthorized", "message": "A valid bearer credential is required." } }404No Brokerage Account exists for the exact Investor ID.
404 response { "error": { "code": "not_found", "message": "Brokerage Account not found." } }409The Investor-scoped key was already used with a different normalized request.
409 response { "error": { "code": "idempotency_conflict", "message": "Idempotency key was already used with a different request." } }422The withdrawal exceeds currently locked Available Cash.
422 response { "error": { "code": "insufficient_cash", "message": "Cash Withdrawal exceeds Available Cash." } }500The route caught an unexpected failure, including missing credential configuration and database failures.
500 response { "error": { "code": "internal_error", "message": "The request could not be completed." } }
curl -X POST "https://api.papertrade.example/api/investors/investor-123/account/withdrawals" \
-H "Authorization: Bearer <credential>" \
-H "Idempotency-Key: deposit-investor-123-1" \
-H "Content-Type: application/json" \
-d '{ "amountCents": 25000 }'{
"amountCents": 25000
}{
"investorId": "investor-123",
"availableCashCents": 970000,
"realizedGainLossCents": 0,
"positions": []
}/api/investors/{investorId}/account/withdrawalsNo authList supported withdrawal methods
Returns Allow: OPTIONS, POST. No body and no authentication.
Parameters
investorIdpathstringrequiredExact, opaque Investor identifier. The application does not trim, normalize, length-limit, or pattern-check this value.
Responses
- 204Supported methods returned; no response body. Allow: OPTIONS, POST.
curl -X OPTIONS "https://api.papertrade.example/api/investors/investor-123/account/withdrawals"/api/investors/{investorId}/account/activitiesList Account Activity
Returns at most 100 activities, newest first. Equal timestamps are ordered by the internal activity identifier descending. There is no pagination, filtering, or caller-selectable limit. Allow: GET, HEAD, OPTIONS.
Parameters
investorIdpathstringrequiredExact, opaque Investor identifier. The application does not trim, normalize, length-limit, or pattern-check this value.
Responses
200Account exists; its activity history is returned.
200 response { "activities": [ { "type": "cash_withdrawal", "amountCents": 30000, "createdAt": "2026-07-13T12:02:00.000Z" }, { "type": "cash_deposit", "amountCents": 25000, "createdAt": "2026-07-13T12:01:00.000Z" }, { "type": "starting_cash", "amountCents": 1000000, "createdAt": "2026-07-13T12:00:00.000Z" } ] }401Bearer credential is missing, malformed, or incorrect.
401 response { "error": { "code": "unauthorized", "message": "A valid bearer credential is required." } }404No Brokerage Account exists for the exact Investor ID.
404 response { "error": { "code": "not_found", "message": "Brokerage Account not found." } }500The route caught an unexpected failure, including missing credential configuration and database failures.
500 response { "error": { "code": "internal_error", "message": "The request could not be completed." } }
curl -X GET "https://api.papertrade.example/api/investors/investor-123/account/activities" \
-H "Authorization: Bearer <credential>"{
"activities": [
{
"type": "cash_withdrawal",
"amountCents": 30000,
"createdAt": "2026-07-13T12:02:00.000Z"
},
{
"type": "cash_deposit",
"amountCents": 25000,
"createdAt": "2026-07-13T12:01:00.000Z"
},
{
"type": "starting_cash",
"amountCents": 1000000,
"createdAt": "2026-07-13T12:00:00.000Z"
}
]
}/api/investors/{investorId}/account/activitiesCheck Account Activity availability
Runs the GET operation, including database reads, but returns no body.
Parameters
investorIdpathstringrequiredExact, opaque Investor identifier. The application does not trim, normalize, length-limit, or pattern-check this value.
Responses
- 200Account exists; body omitted.
- 401Bearer credential is missing or invalid; body omitted.
- 404Brokerage Account not found; body omitted.
- 500The request could not be completed; body omitted.
curl -X HEAD "https://api.papertrade.example/api/investors/investor-123/account/activities" \
-H "Authorization: Bearer <credential>"/api/investors/{investorId}/account/activitiesNo authList supported activity methods
Returns Allow: GET, HEAD, OPTIONS. No body and no authentication.
Parameters
investorIdpathstringrequiredExact, opaque Investor identifier. The application does not trim, normalize, length-limit, or pattern-check this value.
Responses
- 204Supported methods returned; no response body. Allow: GET, HEAD, OPTIONS.
curl -X OPTIONS "https://api.papertrade.example/api/investors/investor-123/account/activities"Market Orders
Synchronous simulated Buy and Sell fills.
/api/investors/{investorId}/account/market-ordersExecute a Buy or Sell Market Order
Executes synchronously against a freshly fetched quote and returns an immutable Fill; no Market Order resource is persisted. Tickers are trimmed and uppercased before validation. The simplified session is Monday–Friday, 09:30 inclusive to 16:00 exclusive in America/New_York; holidays and early closes are ignored. Facts and quote data are fetched before account lookup, so an unknown Investor can receive market-session, security, or provider errors before 404. Matching idempotent replays occur before those checks. Allow: OPTIONS, POST.
Parameters
investorIdpathstringrequiredExact, opaque Investor identifier. The application does not trim, normalize, length-limit, or pattern-check this value.
Idempotency-KeyheaderstringrequiredInvestor-scoped mutation key containing at least one non-whitespace character. The original value is retained as the key; no maximum length is enforced.
Request body
Order side, ticker (trimmed and uppercased before validation), and positive whole-share quantity.
Responses
200The order filled, or the original Fill was replayed.
200 response { "type": "buy_fill", "ticker": "AAPL", "quantity": 2, "priceCents": 21134, "totalCents": 42268, "quoteTimestamp": "2026-07-13T14:30:00.000Z" }400Body, order fields, or Idempotency-Key is missing, malformed, or invalid.
400 response { "error": { "code": "invalid_request", "message": "side must be \"buy\" or \"sell\", Ticker must be valid, quantity must be a positive safe whole number, and Idempotency-Key is required." } }401Bearer credential is missing, malformed, or incorrect.
401 response { "error": { "code": "unauthorized", "message": "A valid bearer credential is required." } }404No Brokerage Account exists for the exact Investor ID.
404 response { "error": { "code": "not_found", "message": "Brokerage Account not found." } }409The Investor-scoped key was already used with a different normalized request.
409 response { "error": { "code": "idempotency_conflict", "message": "Idempotency key was already used with a different request." } }422A terminal domain rule rejected the order: market_closed, unsupported_ticker, insufficient_cash, position_limit_exceeded, insufficient_shares, or account_limit_exceeded.
422 response { "error": { "code": "insufficient_cash", "message": "Buy Market Order exceeds Available Cash." } }500The route caught an unexpected failure, including missing credential configuration and database failures.
500 response { "error": { "code": "internal_error", "message": "The request could not be completed." } }503Provider credentials, timeout, transport, status, JSON, or returned data were unavailable or unusable. Provider details are not exposed.
503 response { "error": { "code": "market_data_unavailable", "message": "Market data is temporarily unavailable." } }
curl -X POST "https://api.papertrade.example/api/investors/investor-123/account/market-orders" \
-H "Authorization: Bearer <credential>" \
-H "Idempotency-Key: deposit-investor-123-1" \
-H "Content-Type: application/json" \
-d '{ "side": "buy", "ticker": " aapl ", "quantity": 2 }'{
"side": "buy",
"ticker": " aapl ",
"quantity": 2
}{
"type": "buy_fill",
"ticker": "AAPL",
"quantity": 2,
"priceCents": 21134,
"totalCents": 42268,
"quoteTimestamp": "2026-07-13T14:30:00.000Z"
}/api/investors/{investorId}/account/market-ordersNo authList supported Market Order methods
Returns Allow: OPTIONS, POST. No body and no authentication.
Parameters
investorIdpathstringrequiredExact, opaque Investor identifier. The application does not trim, normalize, length-limit, or pattern-check this value.
Responses
- 204Supported methods returned; no response body. Allow: OPTIONS, POST.
curl -X OPTIONS "https://api.papertrade.example/api/investors/investor-123/account/market-orders"Tradable Securities
On-demand security facts, quotes, and daily price history.
/api/securities/{ticker}Look up a Tradable Security
Performs an exact lookup after trimming and uppercasing the Ticker. There is no fuzzy search or alias matching. The provider request is not cached. Returned name and exchange are trimmed; exchange casing is provider-controlled. Allow: GET, HEAD, OPTIONS.
Parameters
tickerpathstringrequiredTicker input. Trimmed and uppercased, then required to be 1–10 ASCII characters matching ^[A-Z][A-Z0-9.-]{0,9}$. Lowercase and surrounding whitespace are accepted.
Responses
200An active security on an accepted US exchange was found.
200 response { "ticker": "AAPL", "name": "Apple Inc.", "exchange": "NASDAQ" }400Ticker is malformed after trimming and uppercasing.
400 response { "error": { "code": "invalid_request", "message": "Ticker is malformed." } }401Bearer credential is missing, malformed, or incorrect.
401 response { "error": { "code": "unauthorized", "message": "A valid bearer credential is required." } }422Provider returned not-found, or facts identify an inactive security or a listing outside the accepted exchange set.
422 response { "error": { "code": "unsupported_ticker", "message": "Ticker is not a supported active US-listed stock or ETF." } }500The route caught an unexpected failure, including missing credential configuration and database failures.
500 response { "error": { "code": "internal_error", "message": "The request could not be completed." } }503Provider credentials, timeout, transport, status, JSON, or returned data were unavailable or unusable. Provider details are not exposed.
503 response { "error": { "code": "market_data_unavailable", "message": "Market data is temporarily unavailable." } }
curl -X GET "https://api.papertrade.example/api/securities/AAPL" \
-H "Authorization: Bearer <credential>"{
"ticker": "AAPL",
"name": "Apple Inc.",
"exchange": "NASDAQ"
}/api/securities/{ticker}Check Tradable Security availability
Runs the GET operation, including its provider request, but returns no body.
Parameters
tickerpathstringrequiredTicker input. Trimmed and uppercased, then required to be 1–10 ASCII characters matching ^[A-Z][A-Z0-9.-]{0,9}$. Lowercase and surrounding whitespace are accepted.
Responses
- 200Tradable Security found; body omitted.
- 400Ticker is malformed; body omitted.
- 401Bearer credential is missing or invalid; body omitted.
- 422Ticker is unsupported; body omitted.
- 500The request could not be completed; body omitted.
- 503Market data is unavailable; body omitted.
curl -X HEAD "https://api.papertrade.example/api/securities/AAPL" \
-H "Authorization: Bearer <credential>"/api/securities/{ticker}No authList supported security lookup methods
Returns Allow: GET, HEAD, OPTIONS. No body and no authentication.
Parameters
tickerpathstringrequiredTicker input. Trimmed and uppercased, then required to be 1–10 ASCII characters matching ^[A-Z][A-Z0-9.-]{0,9}$. Lowercase and surrounding whitespace are accepted.
Responses
- 204Supported methods returned; no response body. Allow: GET, HEAD, OPTIONS.
curl -X OPTIONS "https://api.papertrade.example/api/securities/AAPL"/api/securities/{ticker}/quoteGet a current quote
Fetches a non-cached provider snapshot. Price is rounded to the nearest cent. This endpoint does not fetch company facts, so it does not itself verify active status, exchange, or asset class. Allow: GET, HEAD, OPTIONS.
Parameters
tickerpathstringrequiredTicker input. Trimmed and uppercased, then required to be 1–10 ASCII characters matching ^[A-Z][A-Z0-9.-]{0,9}$. Lowercase and surrounding whitespace are accepted.
Responses
200A usable quote was returned by the provider.
200 response { "ticker": "AAPL", "priceCents": 21134, "quoteTimestamp": "2026-07-13T14:30:00.000Z" }400Ticker is malformed after trimming and uppercasing.
400 response { "error": { "code": "invalid_request", "message": "Ticker is malformed." } }401Bearer credential is missing, malformed, or incorrect.
401 response { "error": { "code": "unauthorized", "message": "A valid bearer credential is required." } }422Provider returned not-found, or facts identify an inactive security or a listing outside the accepted exchange set.
422 response { "error": { "code": "unsupported_ticker", "message": "Ticker is not a supported active US-listed stock or ETF." } }500The route caught an unexpected failure, including missing credential configuration and database failures.
500 response { "error": { "code": "internal_error", "message": "The request could not be completed." } }503Provider credentials, timeout, transport, status, JSON, or returned data were unavailable or unusable. Provider details are not exposed.
503 response { "error": { "code": "market_data_unavailable", "message": "Market data is temporarily unavailable." } }
curl -X GET "https://api.papertrade.example/api/securities/AAPL/quote" \
-H "Authorization: Bearer <credential>"{
"ticker": "AAPL",
"priceCents": 21134,
"quoteTimestamp": "2026-07-13T14:30:00.000Z"
}/api/securities/{ticker}/quoteCheck quote availability
Runs the GET operation, including its provider request, but returns no body.
Parameters
tickerpathstringrequiredTicker input. Trimmed and uppercased, then required to be 1–10 ASCII characters matching ^[A-Z][A-Z0-9.-]{0,9}$. Lowercase and surrounding whitespace are accepted.
Responses
- 200Quote available; body omitted.
- 400Ticker is malformed; body omitted.
- 401Bearer credential is missing or invalid; body omitted.
- 422Ticker is unsupported; body omitted.
- 500The request could not be completed; body omitted.
- 503Market data is unavailable; body omitted.
curl -X HEAD "https://api.papertrade.example/api/securities/AAPL/quote" \
-H "Authorization: Bearer <credential>"/api/securities/{ticker}/quoteNo authList supported quote methods
Returns Allow: GET, HEAD, OPTIONS. No body and no authentication.
Parameters
tickerpathstringrequiredTicker input. Trimmed and uppercased, then required to be 1–10 ASCII characters matching ^[A-Z][A-Z0-9.-]{0,9}$. Lowercase and surrounding whitespace are accepted.
Responses
- 204Supported methods returned; no response body. Allow: GET, HEAD, OPTIONS.
curl -X OPTIONS "https://api.papertrade.example/api/securities/AAPL/quote"/api/securities/{ticker}/pricesGet daily historical prices
Returns provider daily OHLCV rows in the requested inclusive date range. startDate must be no later than endDate. Rows are not sorted, deduplicated, synthesized, or filled; provider order is preserved and an empty array is valid. One malformed or out-of-range provider row rejects the entire response as 503. Allow: GET, HEAD, OPTIONS.
Parameters
tickerpathstringrequiredTicker input. Trimmed and uppercased, then required to be 1–10 ASCII characters matching ^[A-Z][A-Z0-9.-]{0,9}$. Lowercase and surrounding whitespace are accepted.
startDatestring (date)requiredInclusive first calendar date. Must be no later than endDate.
endDatestring (date)requiredInclusive last calendar date. Must be no earlier than startDate.
Responses
200Historical rows returned; the list may be empty.
200 response { "ticker": "AAPL", "prices": [ { "date": "2026-01-02", "openCents": 24385, "highCents": 24415, "lowCents": 24191, "closeCents": 24336, "volume": 40230800 } ] }400Ticker or inclusive date range is invalid.
400 response { "error": { "code": "invalid_request", "message": "startDate and endDate must be valid YYYY-MM-DD dates in chronological order." } }401Bearer credential is missing, malformed, or incorrect.
401 response { "error": { "code": "unauthorized", "message": "A valid bearer credential is required." } }422Provider returned not-found, or facts identify an inactive security or a listing outside the accepted exchange set.
422 response { "error": { "code": "unsupported_ticker", "message": "Ticker is not a supported active US-listed stock or ETF." } }500The route caught an unexpected failure, including missing credential configuration and database failures.
500 response { "error": { "code": "internal_error", "message": "The request could not be completed." } }503Provider credentials, timeout, transport, status, JSON, or returned data were unavailable or unusable. Provider details are not exposed.
503 response { "error": { "code": "market_data_unavailable", "message": "Market data is temporarily unavailable." } }
curl -X GET "https://api.papertrade.example/api/securities/AAPL/prices?startDate=2026-01-02&endDate=2026-01-05" \
-H "Authorization: Bearer <credential>"{
"ticker": "AAPL",
"prices": [
{
"date": "2026-01-02",
"openCents": 24385,
"highCents": 24415,
"lowCents": 24191,
"closeCents": 24336,
"volume": 40230800
}
]
}/api/securities/{ticker}/pricesCheck historical-price availability
Runs the GET operation, including provider requests, but returns no body.
Parameters
tickerpathstringrequiredTicker input. Trimmed and uppercased, then required to be 1–10 ASCII characters matching ^[A-Z][A-Z0-9.-]{0,9}$. Lowercase and surrounding whitespace are accepted.
Responses
- 200Historical-price request succeeded; body omitted.
- 400Ticker or date range is invalid; body omitted.
- 401Bearer credential is missing or invalid; body omitted.
- 422Ticker is unsupported; body omitted.
- 500The request could not be completed; body omitted.
- 503Market data is unavailable; body omitted.
curl -X HEAD "https://api.papertrade.example/api/securities/AAPL/prices" \
-H "Authorization: Bearer <credential>"/api/securities/{ticker}/pricesNo authList supported historical-price methods
Returns Allow: GET, HEAD, OPTIONS. No body and no authentication.
Parameters
tickerpathstringrequiredTicker input. Trimmed and uppercased, then required to be 1–10 ASCII characters matching ^[A-Z][A-Z0-9.-]{0,9}$. Lowercase and surrounding whitespace are accepted.
Responses
- 204Supported methods returned; no response body. Allow: GET, HEAD, OPTIONS.
curl -X OPTIONS "https://api.papertrade.example/api/securities/AAPL/prices"Data models
The response shapes returned across the API. Monetary values ending in Cents are integer cents; safe integers stay within JavaScript's safe range.
BrokerageAccount
Durable state of an Investor’s simulated brokerage account.
investorIdstringExact, opaque Investor identifier.
availableCashCentsinteger ≥ 0Available Cash in cents.
realizedGainLossCentsintegerCumulative signed Realized Gain or Loss in cents.
positionsPosition[]Positions ordered by Ticker ascending.
Position
A single held security within a Brokerage Account.
tickerCanonicalTicker^[A-Z][A-Z0-9.-]{0,9}$, 1–10 chars.
quantityinteger ≥ 1Whole shares.
averageCostBasisCentsinteger ≥ 0Rounded weighted-average cost basis in cents per share.
MarketOrderFill
Immutable result of a filled Market Order. Discriminated by type into BuyFill and SellFill.
type"buy_fill" | "sell_fill"Discriminator.
tickerCanonicalTickerFilled security.
quantityinteger ≥ 1Whole shares filled.
priceCentsinteger ≥ 1Fill price per share in cents.
totalCentsinteger ≥ 1Total consideration in cents.
costBasisCentsinteger ≥ 0optionalSell only: basis released by the sale.
realizedGainLossCentsintegeroptionalSell only: signed realized gain or loss.
quoteTimestampstring (date-time)Provider quote timestamp (UTC ISO 8601).
AccountActivity
One entry in an account’s history. Discriminated by type.
typeactivity typeDiscriminator for the activity kind.
amountCentsintegeroptionalCash activities: amount moved, in cents.
tickerCanonicalTickeroptionalFill activities: filled security.
createdAtstring (date-time)When the activity was recorded (UTC ISO 8601).
List responses return at most 100 activities, newest first.
TradableSecurity
Company facts for an active, US-listed security.
tickerCanonicalTickerCanonical uppercase ticker.
namestringTrimmed provider name; at least one non-whitespace char.
exchangestringOne of AMEX, ARCA, BATS, CBOE, CBOE BZX, NASDAQ, NYSE, NYSE AMERICAN, NYSE ARCA. Provider casing preserved.
SecurityQuote
A single point-in-time provider quote.
tickerCanonicalTickerCanonical uppercase ticker.
priceCentsinteger ≥ 1Quote price rounded to the nearest cent.
quoteTimestampstring (date-time)Provider source timestamp normalized to UTC ISO 8601.
DailyPrice
A single daily OHLCV row. High is at least every OHLC value; low is at most every OHLC value.
datestring (date)Trading day (YYYY-MM-DD).
openCentsinteger ≥ 1Opening price in cents.
highCentsinteger ≥ 1High price in cents.
lowCentsinteger ≥ 1Low price in cents.
closeCentsinteger ≥ 1Closing price in cents.
volumeinteger ≥ 0 | nullProvider-supplied daily volume, or null when unavailable.
Error codes
Errors are returned as { error: { code, message } } with a stable machine-readable code.
| Code | Status | Message | Where |
|---|---|---|---|
unauthorizedA valid bearer credential is required. | 401 | A valid bearer credential is required. | Any authenticated endpoint |
invalid_requestBody, ticker, dates, or Idempotency-Key is malformed. | 400 | Body, ticker, dates, or Idempotency-Key is malformed. | Account, cash, order, and security endpoints |
not_foundBrokerage Account not found. | 404 | Brokerage Account not found. | Account, cash, order, activity endpoints |
account_already_existsA Brokerage Account already exists for this Investor. | 409 | A Brokerage Account already exists for this Investor. | Create account |
idempotency_conflictIdempotency key was already used with a different request. | 409 | Idempotency key was already used with a different request. | All mutations |
cash_limit_exceededCash Deposit would exceed the supported cash limit. | 422 | Cash Deposit would exceed the supported cash limit. | Deposit |
insufficient_cashCash Withdrawal / Buy exceeds Available Cash. | 422 | Cash Withdrawal / Buy exceeds Available Cash. | Withdraw, Buy order |
market_closedMarket Orders are accepted only during the Paper Trade session. | 422 | Market Orders are accepted only during the Paper Trade session. | Market order |
unsupported_tickerTicker is not a supported active US-listed stock or ETF. | 422 | Ticker is not a supported active US-listed stock or ETF. | Order, security, quote, prices |
position_limit_exceededBuy Market Order would exceed the supported Position limit. | 422 | Buy Market Order would exceed the supported Position limit. | Buy order |
insufficient_sharesSell Market Order exceeds the Position quantity. | 422 | Sell Market Order exceeds the Position quantity. | Sell order |
account_limit_exceededSell Market Order would exceed the supported account limit. | 422 | Sell Market Order would exceed the supported account limit. | Sell order |
internal_errorThe request could not be completed. | 500 | The request could not be completed. | Any endpoint |
market_data_unavailableMarket data is temporarily unavailable. | 503 | Market data is temporarily unavailable. | Order and security endpoints |
method_not_allowedMethod not allowed. | 405 | Method not allowed. | Unsupported HTTP methods |