Skip to content
MyStocks Developers
API v1

API v1

Current stable contract

Versioning policyRelease changelog
Sandbox console
Reference

Errors

Every MyStocks Partner API error carries a machine-readable error.code and a human-readable error.message. Full reference of HTTP status codes, error codes, and structured order-rejection codes.

Every error response carries a top-level error object with a machine-readable code string and a human-readable message. This applies to all endpoints. Check error.code for programmatic handling; use error.message for logging or UI display.

// 401 auth error
{ "error": { "code": "AUTH_MISSING", "message": "Provide a valid partner API key via Authorization: Bearer pk_live_... or X-Api-Key header." } }
 
// 400 validation error
{ "error": { "code": "VALIDATION_ERROR", "message": "Ambiguous symbol \"MTN\" matches multiple listings: MTN.GH, MTN.NG. Use an exchange-qualified symbol." } }

HTTP status codes

FieldTypeRequiredDescription
200OKNoRequest succeeded.
201CreatedNoResource created (register, create sub-account).
202AcceptedNoOrder accepted into the live fill flow (trade, subscribe). Funds escrowed immediately; settlement is asynchronous.
400Bad RequestNoMissing or invalid parameters, a missing idempotency key, or insufficient funds. Check error.code.
401UnauthorizedNoAPI key missing, invalid, or expired.
403ForbiddenNoAction not permitted: revoked/suspended key, missing scope, KYC required, frozen sub-account, or an operation outside your tier.
404Not FoundNoStock symbol, sub-account, holding, or instrument not found.
409ConflictNoIdempotency collision — a concurrent request with the same Idempotency-Key is still in progress. Retry after it resolves.
422UnprocessableNoBusiness-rule violation such as MARKET_CLOSED, a fund not being open, or requested units exceeding holdings.
429Too Many RequestsNoRate limit exceeded. Check X-RateLimit-Remaining and X-RateLimit-Reset headers.
500Internal Server ErrorNoServer-side failure — retry with exponential backoff or contact support.
503Service UnavailableNoPlanned maintenance (MAINTENANCE) or an unplanned dependency outage (SERVICE_UNAVAILABLE). Always carries Retry-After. Never means your request was wrong — retry it unchanged.

Machine-readable error codes

When the error field is an object, error.code is one of the values below.

FieldTypeRequiredDescription
AUTH_MISSING401NoNo API key supplied. Send Authorization: Bearer pk_live_... or X-Api-Key.
AUTH_INVALID401NoKey format is wrong or the key does not exist in the registry.
AUTH_SUSPENDED403NoPartner account temporarily suspended. Contact support@mystocks.africa.
AUTH_REVOKED403NoKey permanently revoked. A new key must be issued — revocation cannot be undone.
RATE_LIMITED429NoToo many requests. Back off and retry after X-RateLimit-Reset.
MISSING_PARAM400NoA required query/body parameter is absent. Check error.param or error.params[].
MISSING_IDEMPOTENCY_KEY400NoAn authenticated POST, PATCH, PUT, or DELETE was sent without the required Idempotency-Key header. The documented registration, reset, application, upgrade, and OAuth token endpoints are exempt.
INVALID_SYMBOL400NoSymbol fails format validation (2–20 alphanumerics, optionally dot-separated). Check error.invalid[] in a batch.
UNKNOWN_EXCHANGE400NoExchange code not recognised. See the supported codes list.
INVALID_TYPE400NoA parameter has the wrong type.
VALIDATION_ERROR400NoRequest body or query param failed validation. See error.message.
BATCH_LIMIT_EXCEEDED400NoBatch exceeds the max symbol count. Check error.max and error.received.
NOT_FOUND404NoRequested resource does not exist.
CONFLICT409NoConcurrent request conflict: idempotency collision or order state changed mid-operation.
QUOTE_REQUIRED400NoNo quoteId supplied on a trade. Every trade needs a fresh single-use quoteId from GET /quote/{symbol}.
QUOTE_NOT_FOUND404NoquoteId never issued, or already expired and garbage-collected. Fetch a fresh quote.
QUOTE_SCOPE_MISMATCH403NoquoteId belongs to a different account context. Fetch with the same subAccountId you trade on.
STALE_QUOTE409NoquoteId expired (60s TTL) or already used. Fetch a fresh quote and retry.
QUOTE_ORDER_MISMATCH409NoquoteId does not match the submitted symbol/side/quantity/cashValue. Request a new quote.
INSUFFICIENT_FUNDS400NoWallet balance too low. Top up the master wallet or deposit into the sub-account first.
KYC_REQUIRED403NoSub-account KYC not verified. Assert via POST /users/{userId}/kyc then retry.
MARKET_CLOSED422NoThe exchange is closed. Check GET /market/status and retry when the venue is open.
FORBIDDEN403NoAuthenticated but not authorised (e.g. read-only data key on a write endpoint).
INTERNAL_ERROR500NoUnexpected server-side failure. Safe to retry with backoff.
MAINTENANCE503NoPlanned maintenance window. Honour the Retry-After header. In read-only maintenance, GETs still succeed and only mutations return this.
SERVICE_UNAVAILABLE503NoUnplanned outage in a downstream dependency. Retry with exponential backoff; check the status page.

Maintenance and downtime

During a maintenance window the Partner API returns 503 with a Retry-After header (in seconds). There are two modes, reported as error.mode:

FieldTypeRequiredDescription
fullmodeNoEvery request is refused with 503 MAINTENANCE.
read_onlymodeNoGET and HEAD are served normally; every mutation (orders, payments, account changes) is refused with 503 MAINTENANCE. Used during a settlement or payment-rail incident so you can still read positions and reconcile while money movement is frozen.
// 503 during a read-only window
{
  "error": {
    "code": "MAINTENANCE",
    "message": "The Partner API is in read-only maintenance: reads are being served normally, but order, payment, and account mutations are temporarily refused.",
    "mode": "read_only",
    "endsAt": "2026-07-14T02:00:00.000Z",
    "statusPage": "https://mystocks.africa/status"
  }
}

Do not treat a 503 as a rejected request — nothing was processed, and the same request is safe to send again once Retry-After elapses. Keep your Idempotency-Key the same when you retry.

Live status, uptime, and incident history are published at mystocks.africa/status (raw JSON: GET /api/v1/status). You do not need to poll it: incidents are pushed to your webhooks as incident.declared / incident.resolved.

Order rejection codes

The codes above are returned synchronously — the HTTP call itself fails and no order is created. A rejectionCode is different: it is attached asynchronously, when an order that was already accepted (202, status PENDING) is later rejected by the MyStocks internal-book execution engine. You learn about it through the order.rejected webhook, the execution report, or rejectionCode on GET /orders/{orderId} — always alongside the free-text rejectionReason.

This is the exhaustive list. No other value is ever written.

FieldTypeRequiredDescription
INSUFFICIENT_FUNDSretriableNoThe account lacked settled balance at settlement time. Top up and resubmit.
MARKET_CLOSEDretriableNoThe exchange was closed when the desk attempted execution. Orders placed outside market hours are normally queued rather than rejected, so this usually indicates an unscheduled closure. Check GET /market/status, then resubmit.
TECHNICAL_ISSUEretriableNoExecution failed for an operational reason at the venue or on the desk. Safe to resubmit; if it recurs, check /status for an open incident.
KYC_REQUIREDnot retriableNoThe account is not KYC-approved. Complete KYC via POST /users/{userId}/kyc, wait for kyc.updated, then resubmit. Retrying as-is will be rejected again.
COMPLIANCE_HOLDnot retriableNoBlocked by compliance review (sanctions, market-abuse check, restricted security, or an account hold). Do not resubmit — contact support@mystocks.africa.
OTHERnot retriableNoDoes not fit a category above. rejectionReason always carries the specific explanation.

retriable means resubmitting the same order unchanged could plausibly succeed later. not retriable means something must change first — funds, KYC, the order itself — or the same rejection will repeat.

Order expiry is not a rejection. When a DAY or GTD order reaches its deadline it is cancelled, carrying orderLifecycle: "EXPIRED" and a cancelReason — never a rejectionCode.

Was this page useful?

Your signal helps us tighten partner onboarding docs.

Send note

Last updated on

On this page