Skip to content
Partner API Docs

Authentication

How to authenticate to the MyStocks Partner API — API keys via Authorization or x-api-key header, scoped read-only data keys for client-side use, Firebase-token endpoints, and the mandatory Idempotency-Key for money-movement calls.

Every request requires your API key — except four partner-portal endpoints (POST /register, GET /me, POST /upgrade-request, and POST /api-keys/revoke), which authenticate with a Firebase ID token from your portal session instead (see Firebase-token endpoints). Send the API key using either method:

Option A — Authorization header (recommended)

# Sandbox
Authorization: Bearer sk_sandbox_<your_key>
 
# Production
Authorization: Bearer pk_live_<your_key>

Option B — Custom header

x-api-key: sk_sandbox_<your_key>  # or pk_live_<your_key>

Never expose a full key (pk_live_) in client-side code or public repositories. Use a data key (pk_data_) for any browser or mobile context — see below. Sandbox keys carry no financial risk; production keys control real funds — guard them accordingly.

Data keys — scoped read-only credentials

A pk_data_ key is a scoped, read-only credential derived from your full key. It cannot trade, move funds, or access sub-account PII, so exposing one carries no financial risk — suitable for low-stakes surfaces like public widgets. For production mobile or web apps, prefer proxying market data through your backend or minting short-lived tokens via POST /oauth/token: an embedded key can be extracted by anyone, it shares your full key's rate-limit bucket (an abuser can exhaust your quota), and since only one data key is active per partner, rotating it breaks every shipped install at once.

Generate a data key via POST /api-keys/data-key (see Key Management). Data keys share the rate-limit bucket of their parent full key. Any call outside the allowed list returns 403 FORBIDDEN.

Allowed endpoints (GET only): /stocks, /stocks/{symbol}, /stocks/{symbol}/chart, /stocks/{symbol}/history, /stocks/{symbol}/pulse, /etfs, /etfs/{symbol}, /etfs/{symbol}/chart, /etfs/{symbol}/history, /market/quotes, /market/status, /market/movers, /market/holidays, /market/settlement, /bonds, /bonds/{id}, /funds, /funds/{id}, /companies, /companies/{symbol}, /companies/{symbol}/chart, /companies/{symbol}/news, /companies/tickers, /market-intel, /market-intel/{id}, /opportunities, /dividends/calendar.

Short-lived tokens — OAuth client credentials

For client-side surfaces where you don't want to embed a long-lived key, exchange your key for a short-lived bearer token via POST /api/v1/partner/oauth/token — a standard OAuth 2.0 client-credentials grant. The token inherits your key's type (full or data) and scopes, expires in 15 minutes, and is prefixed ms_oauth_. This keeps a leaked credential useful for minutes rather than indefinitely, and lets you rotate without breaking shipped installs.

This grant must be enabled for your partner account first (oauthClientCredentialsEnabled). Until it is, the endpoint returns 403 — contact us to switch it on.

Send your client_secret (your API key) via HTTP Basic auth, or in a form / JSON body. An optional scope narrows the token to a subset of your key's scopes:

POST /api/v1/partner/oauth/token
Content-Type: application/x-www-form-urlencoded
 
grant_type=client_credentials&client_id=<your_key_id>&client_secret=pk_live_<your_key>&scope=market:read
{
  "access_token": "ms_oauth_…",
  "token_type": "Bearer",
  "expires_in": 900,
  "scope": "market:read"
}

Use the returned token exactly like a key: Authorization: Bearer ms_oauth_…. When it expires, request a new one. The token endpoint is rate-limited per source IP to deter credential stuffing.

Firebase-token endpoints

Four endpoints authenticate with a Firebase ID token (Authorization: Bearer <firebase-id-token>) obtained from the partner-portal login session rather than an API key: POST /register (issues and rotates your key), GET /me (powers the dashboard UI), POST /upgrade-request (request production access), and POST /api-keys/revoke (must remain reachable even after a key is compromised). All other routes use your partner API key as normal.

Idempotency — safe retries for money-movement calls

On unstable mobile networks a POST can succeed on the server but time out on the client — causing a double-charge if the app retries. Every money-movement and state-changing write — deposit, withdraw, trade, topup, payout, KYC assertion, fund/bond subscribe & redeem, and webhook creationrequires a unique Idempotency-Key header. Calls that omit it are rejected with 400 MISSING_IDEMPOTENCY_KEY. We deduplicate by key for 24 hours and return the cached response on retry.

POST /api/v1/partner/users/{userId}/deposit
Authorization: Bearer pk_live_<your_key>
Idempotency-Key: dep_riven_user_42_1743152580   # unique per attempt

Use any unique string — a UUID or your own transaction ID works well. If a concurrent duplicate is detected you receive HTTP 409 until the first request completes.

When is it safe to retry?

Call typeSafe to retry?How
GET (all read endpoints)AlwaysRetry freely with exponential backoff. Reads have no side effects.
POST that moves money / places orders / changes account stateOnly with Idempotency-KeyRequired — returns 400 MISSING_IDEMPOTENCY_KEY without it. Resend the exact request with the same key to get the original response back, never a duplicate movement.
POST /users (create sub-account)YesThe externalId is the natural idempotency key — an existing sub-account is returned unchanged.
PATCH (settings, pricing, float)YesFull-replace semantics — repeating the same request converges to the same state.
DELETE (cancel order, remove webhook)YesRepeats return 404/422 once the resource is gone or settled — treat that as success.

On 429, wait for Retry-After before retrying (see Rate Limits). On 5xx for a money-movement call, always retry with the same Idempotency-Key — never generate a fresh key for a retry of the same logical operation.

On this page