Developer documentation · v1

Build for the failure path, not only the happy path.

The complete contract for authentication, account reads, idempotent order writes, timeout reconciliation, rate limits, market data, and resumable account events.

Quickstart

Your first authenticated account read.

Store the secret outside source control, pass it as a Bearer token, and inspect the structured status before writing orders.

curl
curl "https://mayospell.vercel.app/api/v1/account" \
+  -H "Authorization: Bearer $MAYOSPELL_API_KEY"
Least privilege

Give each bot only the access it uses.

Research keys read account and market data; execution keys place the limited orders the sandbox supports. Separate keys keep every revocation contained to one integration.

account:read

Account

Balances, buying power, account state, and configured risk tier.

orders:read

Order history

Working, filled, cancelled, rejected, and expired orders.

orders:write

Order control

Idempotent limit orders, working-order cancellation, and sell-to-close requests.

positions:read

Positions

Open long option positions with conservative marks.

marketdata:read

Market data

Contract discovery, quote snapshots, bars, and authenticated quote events.

events:read

Account events

Resumable order and fill events using Last-Event-ID.

Endpoint reference

Fourteen routes, grouped by the job they perform.

Every authenticated route is account-isolated. Order mutations accept limit orders only and enforce the current sandbox risk policy at the service boundary.

GET
/healthPublic
Read API readiness and environment
GET
/accountaccount:read
Account, balances, buying power, and risk tier
GET
/ordersorders:read
List working and historical orders
POST
/ordersorders:write
Place an idempotent limit order
GET
/orders/{orderId}orders:read
Reconcile one exact order after a timeout
DELETE
/orders/{orderId}orders:write
Cancel a working order
GET
/positionspositions:read
Read long positions and conservative marks
POST
/positions/{symbol}/closeorders:write
Submit a sell-to-close limit order
GET
/fillsorders:read
Read execution fills
GET
/contractsmarketdata:read
Discover active option contracts
GET
/quotesmarketdata:read
Read up to 100 quote snapshots
GET
/barsmarketdata:read
Read historical option-premium bars
GET
/market-streammarketdata:read
Stream authenticated quote events
GET
/eventsevents:read
Resume order and fill events with Last-Event-ID

Python

import os, uuid, requests

BASE_URL = "https://mayospell.vercel.app/api/v1"
API_KEY = os.environ["MAYOSPELL_API_KEY"]
client_order_id = str(uuid.uuid4())

response = requests.post(
    f"{BASE_URL}/orders",
    headers={
        "Authorization": f"Bearer {API_KEY}",
        "Idempotency-Key": client_order_id,
    },
    json={
        "clientOrderId": client_order_id,
        "symbol": "SPY260821C00600000",
        "side": "buy",
        "quantity": 1,
        "type": "limit",
        "limitPrice": 1.25,
        "timeInForce": "day",
    },
    timeout=10,
)
response.raise_for_status()
order = response.json()
print(order["order"]["id"])

JavaScript

const baseUrl = "https://mayospell.vercel.app/api/v1";
const clientOrderId = crypto.randomUUID();

const response = await fetch(baseUrl + "/orders", {
  method: "POST",
  headers: {
    Authorization: "Bearer " + process.env.MAYOSPELL_API_KEY,
    "Content-Type": "application/json",
    "Idempotency-Key": clientOrderId,
  },
  body: JSON.stringify({
    clientOrderId,
    symbol: "SPY260821C00600000",
    side: "buy",
    quantity: 1,
    type: "limit",
    limitPrice: 1.25,
    timeInForce: "day",
  }),
});
if (!response.ok) throw new Error(await response.text());
console.log(await response.json());
Structured failures

Errors your bot can classify without parsing prose.

Every API error includes error.code, error.message, and requestId. Preserve requestId in logs and support evidence.

400invalid_request

Schema, query, symbol, or idempotency input is invalid.

401unauthorized

The Bearer key is missing, invalid, expired, or revoked.

403forbidden

The key lacks scope, ownership, environment, or risk permission.

404not_found

The account-owned resource does not exist.

409idempotency_conflict

A key was reused with a different payload, or order state prevents the action.

429rate_limited

The per-key distributed window is exhausted; honor Retry-After.

Production behavior

Six rules for retries, streams, secrets, and stale state.

These rules are part of the integration contract. A bot that ignores them can turn a recoverable outage into an unsafe order sequence.

Every write is reconcilable

A UUID Idempotency-Key must match clientOrderId. Reusing it with different content returns 409.

Ambiguous timeouts are reads

After a timeout, read the exact order before deciding whether any new write is safe.

Rate limits are explicit

Use RateLimit-* and Retry-After headers. Back off with jitter; never spin against 429.

Streams resume from evidence

Persist the last processed event ID and reconnect account events with Last-Event-ID.

Stale data stops automation

Quote age, disconnected streams, and degraded provider posture are stop conditions—not permission to estimate.

Keys have one job

Use a separate scoped key per bot, keep it in a secret manager, rotate it, and revoke it when retired.

Current market-data boundary

The API is online, but the configured Tradier credential currently returns 401 and the tested Alpaca option feeds lack the required entitlement. Treat quote and bar availability as degraded until provider access is repaired. Never substitute estimated prices in an execution bot.