Skip to content
MyStocks Developers
API v1

API v1

Current stable contract

Versioning policyRelease changelog
Sandbox console
Reference

Pagination

MyStocks list endpoints use cursor-based pagination — pass ?limit= and ?cursor=; every response carries hasMore and nextCursor so you never calculate offsets. Per-endpoint defaults and maximums included.

List endpoints use cursor-based pagination. Pass ?limit= to control page size and ?cursor= to advance to the next page. Every paginated response includes hasMore and nextCursor so you never need to calculate offsets.

Request parameters

FieldTypeRequiredDescription
limitintegerNoItems per page. Default and max vary by endpoint (see below). Orders, users and market intelligence reject invalid or out-of-range limits with 400.
cursorstringNoOpaque cursor returned as nextCursor in the previous response. Omit on the first request. Treat cursors as opaque strings — do not construct or parse them.

Response fields

FieldTypeRequiredDescription
countintegerNoNumber of items in this page (≤ limit).
hasMorebooleanNotrue means continue scanning with nextCursor; a filtered page can contain no matches. false means scanning has finished.
nextCursorstring | nullNoPass as ?cursor= on the next request. null when hasMore is false.

Limit defaults & maximums by endpoint

EndpointDefaultMax
GET /users100200
GET /market-intel20100
GET /orders50200
GET /users/{userId}/orders50200
GET /users/{userId}/transactions50200
GET /audit50200
GET /webhooks/{id}/deliveries20100

Fetching all pages — JavaScript

async function fetchAllOrders(apiKey) {
  const orders = [];
  let cursor = null;
 
  do {
    const url = new URL('https://mystocks.africa/api/v1/partner/orders');
    url.searchParams.set('limit', '200');
    if (cursor) url.searchParams.set('cursor', cursor);
 
    const res = await fetch(url, { headers: { 'x-api-key': apiKey } });
    const page = await res.json();
 
    orders.push(...page.orders);
    cursor = page.nextCursor; // null on the last page
  } while (cursor);
 
  return orders;
}

Filtered pages and cursor lifetime

Master orders honor status, from and to. Date-only bounds include the entire UTC day; ISO date-times specify precise bounds. Invalid or reversed dates return 400. Users accept status=active|frozen|closed|ALL and a direct externalId lookup; status also applies to that lookup.

Orders and users scan at most 1,000 records per request. An empty matching page can therefore have hasMore: true; continue with its nextCursor rather than stopping at count: 0. Do not interpret count as the total matching collection size.

Market intelligence accepts exchange, symbol, limit and cursor, orders by creation time descending with ID as a tie-breaker, and returns articles, count, hasMore, nextCursor.

Keep the same filters when advancing. Cursors are not snapshots: deletions, edits or a changed filter can invalidate them. Unknown cursors return 400 on these endpoints; user cursors must belong to the requesting partner. Restart from the first page if necessary. Do not reuse cursors across keys or environments.

Customer ledger and Sandbox order queries

Customer transactions in both environments accept inclusive from and to UTC dates or timezone-aware ISO timestamps. Date-only to includes 23:59:59.999Z. Invalid or reversed bounds, limits outside 1–200 and unknown owned-account cursors return 400 VALIDATION_ERROR.

Customer order pages and Sandbox test-tool orders return hasMore and nextCursor. Keep following continuation on empty filtered pages; the per-request scan budget is 1,000 records. GET /test-tools/orders requires subAccountId in Sandbox.

How do I read across my customers?

Use GET /orders/blotter for owned customer orders. The master GET /orders remains a master-account endpoint. The blotter orders customers by customer ID, then orders by newest createdAt within each customer; it is not a globally chronological feed. Each request scans at most 20 customers and 1,000 orders and returns up to 200 matching orders (default 50). Follow nextCursor while hasMore is true, even when a filtered page is empty. Keep the same filters and environment on continuation.

Use GET /users/summary?userIds=customer-1,external-2 for 1–20 distinct owned customer IDs or external IDs. Unknown and foreign customers receive the same per-item NOT_FOUND error. Each account includes its canonical identity, actual currency wallets, USD buying power, currency closure blockers and read timestamp. The request reads each account independently and reuses the wallet accounting checks; it is not an atomic consolidated statement or a replacement for final closure checks. Wallet read cost depends on each customer's wallet, entitlement and order history. Do not add USD and KES balances together.

Set API_BASE=https://partners.mystocks.africa/api/sandbox/v1/partner for virtual Sandbox accounts, or /api/v1/partner for approved Live access. Customer reads appear in the partner currency workspace and /admin/partners currency workspace. Admin reads require ledger-view permission; hosted API reads require the relevant scoped credential.

GET

/orders/blotter

List orders across owned customer accounts

Full endpoint documentation
Sandbox availablelistCustomerOrderBlotter

Customer ID ascending, then createdAt descending within each customer; not global chronological ordering. Scans at most 20 customers and 1,000 orders per request. Continue using nextCursor even when orders is empty. Cursors are scoped to partner, environment and filters. This does not change the master-account /orders endpoint.

Parameters

limitintegerquery

Page size, default 50.

Example: 50

cursorstringquery

Opaque continuation from this endpoint.

symbolstringquery

Instrument symbol.

statusALL | PENDING | WORKING | PARTIALLY_FILLED | FILLED | REJECTED | CANCELLED | EXPIRED | COMPLETEDquery

Canonical execution status; COMPLETED aliases FILLED.

fromstringquery

Inclusive ISO date or timezone-aware timestamp.

tostringquery

Inclusive ISO date (through 23:59:59.999 UTC) or timezone-aware timestamp.

Headers

AuthorizationstringRequired

Bearer sk_sandbox_<your_key> for sandbox requests.

Response fields

ordersobject[]Required
orders[].orderIdstring
orders[].subAccountIdstring
orders[].externalIdstring | null
orders[].symbolstring
orders[].typestring
orders[].statusstring
orders[].canonicalStatusstring
orders[].settlementStatusstring
orders[].quantitynumber
orders[].filledQuantitynumber
orders[].remainingQuantitynumber
orders[].currencystring
orders[].createdAtstring | null
orders[].filledAtstring | null
orders[].settledAtstring | null
orders[].rejectionCodestring | null
orders[].orderLifecyclestring | null

EXPIRED for expiry; expiry is not a rejection.

orders[].cancelReasonstring | null
countintegerRequired
hasMorebooleanRequired
nextCursorstring | nullRequired
orderingstringRequired
scopestring
snapshotModelstring
scannedOrdersinteger
sandboxboolean

Response codes

200Owned customer results with explicit read and continuation limits.
400Invalid bounds, identifiers or cursor.
401Valid environment-specific API key required.
403Required read permission is missing.
429Partner request rate limit exceeded.
GET

/orders

List orders

Full endpoint documentation
Sandbox availablelistOrders

Returns your partner order history. Filter by status, symbol, or date range. Filters from/to are enforced against createdAt. ISO dates cover full UTC days. Sparse matches may yield an empty page with hasMore=true; follow nextCursor. Each call scans at most 1000 source records. Invalid limits, dates and cursors return 400.

Parameters

statusPENDING | WORKING | PARTIALLY_FILLED | PROCESSING | COMPLETED | FILLED | REJECTED | CANCELLED | EXPIREDquery
symbolstringquery

Example: SCOM.KE

fromstringquery

ISO date (whole UTC day) or timezone-qualified ISO date-time; invalid or reversed bounds return 400.

Example: 2026-01-01

tostringquery

ISO date (whole UTC day) or timezone-qualified ISO date-time; invalid or reversed bounds return 400.

Example: 2026-03-31

cursorstringquery

Opaque cursor from nextCursor of the previous response. Omit for the first page.

limitintegerquery

Example: 50

Headers

AuthorizationstringRequired

Bearer sk_sandbox_<your_key> for sandbox requests.

Response fields

ordersobject[]Required
orders[].orderIdstring

Unique order ID. Use to poll status or cancel.

Example: "ord_abc123xyz"

orders[].symbolstring

Example: "SCOM.KE"

orders[].namestring

Example: "Safaricom PLC"

orders[].exchangestring

Example: "NSE"

orders[].orderTypeMARKET | LIMIT | STOP | STOP_LIMIT

Order type. MARKET orders are quote-gated; LIMIT/STOP/STOP_LIMIT rest as WORKING until triggered.

Example: "LIMIT"

orders[].limitPricenumber | null

Limit price in the stock's local currency (LIMIT / STOP_LIMIT only).

orders[].stopPricenumber | null

Stop price in the stock's local currency (STOP / STOP_LIMIT only).

orders[].cancelableboolean

Whether the order can be cancelled now (DELETE). True while PENDING or WORKING.

orders[].replaceableboolean

Whether the order can be modified now (PATCH). True only for a WORKING resting order.

orders[].typeBUY | SELL
orders[].statusPENDING | WORKING | PARTIALLY_FILLED | PROCESSING | COMPLETED | FILLED | REJECTED | CANCELLED | EXPIRED

PENDING after submission. WORKING = resting LIMIT/STOP/STOP_LIMIT order waiting for its trigger price (becomes PENDING on trigger). PARTIALLY_FILLED and FILLED are the canonical execution states. REJECTED, CANCELLED, and EXPIRED are terminal outcomes. PROCESSING is a legacy transition alias and COMPLETED is a v1 compatibility alias of FILLED; neither represents custody settlement. Settlement is tracked separately as PENDING, SETTLED, or FAILED.

orders[].quoteIdstring | null

Pre-trade quote ID consumed by this order.

orders[].quoteExpiresAtstring | null
orders[].quoteUsdPricenumber | null
orders[].clientOrderIdstring | null

Partner-supplied order reference.

orders[].omsStatusstring | null

Canonical OMS lifecycle status.

orders[].timeInForcestring | null
orders[].filledQuantitynumber
orders[].remainingQuantitynumber
orders[].averageFillPricenumber | null
orders[].executionVenuestring | null
orders[].lastExecutionReportIdstring | null
orders[].executionSlaunion

Live execution SLA clock for orders that have entered the PENDING dealing-desk queue. Null while a resting order is still WORKING.

orders[].quantityinteger
orders[].priceAtOrdernumber

Stock price in local currency at submission.

orders[].usdPriceAtOrdernumber

USD equivalent of priceAtOrder.

orders[].fxRateAtOrdernumber | null

Managed FX rate used for local-to-USD conversion at order submission.

orders[].fxSourcestring | null
orders[].fxRateAsOfstring | null
orders[].fxConversionunion
orders[].feenumber

Total fee in USD (base + partner markup).

orders[].baseFeenumber

MyStocks base broker fee (0.75% of gross).

orders[].partnerMarkupFeenumber

Your markup fee component. Zero if markupBps = 0.

orders[].totalAmountnumber

BUY: gross + fee. SELL: gross - fee.

orders[].currencystring

Example: "USD"

orders[].localCurrencystring

Example: "KES"

orders[].rejectionCodestring | null

Structured rejection code. Populated when status = REJECTED.

orders[].rejectionReasonstring | null

Free-text rejection detail provided by the MyStocks internal-book execution service. Populated when status = REJECTED.

orders[].cancelledAtstring | null
orders[].settledAtstring | null

Set when custody settlement completes; independent of the canonical FILLED execution state. Legacy completed records that pre-date this field return their immutable completion/creation timestamp as compatibility evidence.

orders[].createdAtstring
orders[].canonicalStatusPENDING | WORKING | PARTIALLY_FILLED | FILLED | REJECTED | CANCELLED | EXPIRED
orders[].settlementStatusPENDING | SETTLED | FAILED
orders[].orderLifecyclestring | null

EXPIRED for expiry; expiry is not a rejection.

orders[].cancelReasonstring | null
countintegerRequired
hasMorebooleanRequired
nextCursorstring | nullRequired

Response codes

200Order list.
GET

/users/summary

Read up to 20 customer wallet summaries

Full endpoint documentation
Sandbox availablegetCustomerWalletSummaries

Supply 1–20 distinct owned customer IDs or external IDs, separated by commas. Unknown and foreign IDs return the same NOT_FOUND item error. Actual currency wallets, USD buying power and currency closure blockers are returned per account. Accounts are read independently, not as an atomic consolidated statement. Open orders, positions and settlements still require closure-time checks.

Parameters

userIdsstringqueryRequired

1–20 distinct comma-separated customer IDs or external IDs.

Headers

AuthorizationstringRequired

Bearer sk_sandbox_<your_key> for sandbox requests.

Response fields

accountsobject[]Required
accounts[].requestedIdstring
accounts[].subAccountIdstring
accounts[].userIdstring
accounts[].externalIdstring
accounts[].statusstring
accounts[].asOfstring
accounts[].currencyWalletsobject[]
accounts[].buyingPowerobject
accounts[].currencyClosureBlockersobject[]
accounts[].errorobject
countintegerRequired
asOfstringRequired
snapshotModelstringRequired
sandboxboolean

Response codes

200Owned customer results with explicit read and continuation limits.
400Invalid bounds, identifiers or cursor.
401Valid environment-specific API key required.
403Required read permission is missing.
429Partner request rate limit exceeded.

Was this page useful?

Your signal helps us tighten partner onboarding docs.

Send note

Last updated on

On this page