Python SDK & CLI

bash
pip install -U sahmk
pip install sahmk --upgrade
export SAHMK_API_KEY="your_api_key"
sahmk quote 2222

Python SDK:

python
from sahmk import SahmkClient

client = SahmkClient(api_key="YOUR_API_KEY")

print(client.quote("2222"))

Full examples: github.com/sahmk-sa/sahmk-pythonPyPI: pypi.org/project/sahmk

Getting Started

Welcome to the SAHMK API. Access real-time and historical Saudi stock market data for all 350+ companies listed on TASI and Nomu.

Quick Start

  1. Create a free account
  2. Get your API key from the dashboard
  3. Make your first API request
  4. Browse code examples on GitHub

Base URL

text
https://app.sahmk.sa/api/v1

Your First Request

curl -X GET "https://app.sahmk.sa/api/v1/quote/2222/" \
     -H "X-API-Key: YOUR_API_KEY"

Full examples available on GitHub →

Authentication

All API requests require authentication using an API key. Include your key in the X-API-Key header.

http
X-API-Key: YOUR_API_KEY

API Key Types:
shmk_live_* — Production keys
shmk_test_* — Test keys (same data, separate quota)

curl -X GET "https://app.sahmk.sa/api/v1/quote/2222/" \
     -H "X-API-Key: YOUR_API_KEY"

Stocks

Get real-time stock quotes and prices for individual or multiple stocks.

GET /quote/{symbol}/

Free

Get current price and trading data for a single stock.

Path Parameters:

  • symbol — Stock ticker (e.g., 2222 for Aramco)
View Response
json
{
  "symbol": "2222",
  "name": "أرامكو السعودية",
  "name_en": "Saudi Arabian Oil Co",
  "price": 25.86,
  "change": 0.18,
  "change_percent": 0.7,
  "open": 25.6,
  "high": 25.86,
  "low": 25.6,
  "previous_close": 25.68,
  "volume": 9803705,
  "value": 252308343.0,
  "bid": 25.82,
  "ask": 25.86,
  "liquidity": {
    "inflow_value": 184950463.03,
    "inflow_volume": 7182468,
    "inflow_trades": 7261,
    "outflow_value": 67357881.91,
    "outflow_volume": 2621237,
    "outflow_trades": 5028,
    "net_value": 117592581.12
  },
  "updated_at": "2026-02-10T12:19:22+00:00",
  "is_delayed": false
}

Liquidity Fields:

  • inflow_value — Total SAR value of buy orders
  • outflow_value — Total SAR value of sell orders
  • net_value — Net liquidity (inflow - outflow)

GET /quotes/

Starter+

Get quotes for multiple stocks in a single request. Requires Starter plan or higher.

Note: This bulk endpoint requires a Starter plan or higher. Free-tier users can use GET /quote/{symbol}/ for individual stock quotes.

Query Parameters:

  • symbols — Comma-separated tickers (max 50)

Pro Tip: Use this batch endpoint instead of polling individual /quote/{symbol}/ calls — it's more efficient and counts as a single API request.

View Response
json
{
  "quotes": [
    {
      "symbol": "2222",
      "name": "أرامكو السعودية",
      "name_en": "Saudi Arabian Oil Co",
      "price": 25.86,
      "change": 0.18,
      "change_percent": 0.7,
      "volume": 9803705,
      "net_liquidity": 117592581.12,
      "updated_at": "2026-02-10T12:19:22+00:00",
      "is_delayed": false
    },
    {
      "symbol": "1120",
      "name": "الراجحي",
      "name_en": "Al Rajhi Banking & Investment Corp SJSC",
      "price": 108.6,
      "change": 0.2,
      "change_percent": 0.18,
      "volume": 4023570,
      "net_liquidity": 45230000.50,
      "updated_at": "2026-02-10T12:18:56+00:00",
      "is_delayed": false
    }
  ],
  "count": 2
}

net_liquidity — Net money flow (buy value - sell value) in SAR

Market

Get market-wide data including index values, top movers, and sector performance.

GET /market/summary/

Free

Get TASI index value, volume, and market sentiment.

View Response
json
{
  "timestamp": "2026-01-28T12:20:00+00:00",
  "index_value": 11458.11,
  "index_change": 76.28,
  "index_change_percent": 0.67,
  "total_volume": 279874553,
  "advancing": 117,
  "declining": 139,
  "unchanged": 14,
  "market_mood": "bullish"
}

GET /market/gainers/

Free

Get top gaining stocks by percentage change.

Query Parameters:

  • limit — Number of results (default: 10, max: 50)
View Response
json
{
  "gainers": [
    {
      "symbol": "4194",
      "name": "مجموعة منزل التسويق للتجارة",
      "name_en": "Maison Marketing Trade Group",
      "price": 59.5,
      "change": 4.9,
      "change_percent": 8.97,
      "volume": 611349,
      "updated_at": "2026-01-28T12:19:50+00:00"
    }
  ],
  "count": 10
}

GET /market/losers/

Free

Get top losing stocks by percentage change.

Query Parameters:

  • limit — Number of results (default: 10, max: 50)
View Response
json
{
  "losers": [
    {
      "symbol": "9639",
      "name": "شركة أنماط التقنية للتجارة",
      "name_en": "Anmat Technology Trading Co",
      "price": 8.2,
      "change": -0.8,
      "change_percent": -8.89,
      "volume": 9206,
      "updated_at": "2026-01-28T12:10:18+00:00"
    }
  ],
  "count": 10
}

GET /market/volume/

Free

Get top stocks by trading volume.

Query Parameters:

  • limit — Number of results (default: 10, max: 50)
View Response
json
{
  "stocks": [
    {
      "symbol": "2222",
      "name": "أرامكو السعودية",
      "name_en": "Saudi Arabian Oil Co",
      "price": 25.64,
      "change": 0.38,
      "change_percent": 1.5,
      "volume": 15738067,
      "updated_at": "2026-01-28T12:19:48+00:00"
    }
  ],
  "count": 10
}

GET /market/value/

Free

Get top stocks by trading value (SAR).

Query Parameters:

  • limit — Number of results (default: 10, max: 50)
View Response
json
{
  "stocks": [
    {
      "symbol": "2222",
      "name": "أرامكو السعودية",
      "name_en": "Saudi Arabian Oil Co",
      "price": 25.64,
      "change": 0.38,
      "change_percent": 1.5,
      "volume": 15738067,
      "value": 402108076.72,
      "updated_at": "2026-01-28T12:19:48+00:00"
    }
  ],
  "count": 10
}

GET /market/sectors/

Free

Get sector performance and statistics.

View Response
json
{
  "sectors": [
    {
      "id": "TBNI",
      "name": "Banks",
      "change_percent": 0.45,
      "avg_change_percent": 0.38,
      "volume": 45027873,
      "num_stocks": 10
    }
  ],
  "count": 20
}

Company Info

Get detailed company information including fundamentals, technicals, and valuation data.

GET /company/{symbol}/

Free

Get company information. Response varies by plan.

Data Available by Plan:

  • Free: Name, sector, industry, description, website
  • Starter: + Full fundamentals (PE, EPS, book value, beta, week/month/52w ranges)
  • Pro: + Technicals, valuation, analyst consensus

New: week_high/low, month_high/low — Price levels in last 7/30 days

View Response (Pro)
json
{
  "symbol": "2222",
  "name": "أرامكو السعودية",
  "name_en": "Saudi Arabian Oil Co",
  "current_price": 25.64,
  "sector": "Energy",
  "industry": "Oil & Gas",
  "description": "Saudi Aramco is the world's largest oil producer...",
  "website": "https://www.aramco.com",
  "country": "Saudi Arabia",
  "currency": "SAR",
  
  "fundamentals": {
    "market_cap": 6258120000000,
    "pe_ratio": 16.77,
    "forward_pe": 15.48,
    "eps": 1.54,
    "book_value": 6.16,
    "price_to_book": 4.19,
    "beta": 0.104,
    "shares_outstanding": 242000000000,
    "float_shares": 5969578000,
    "week_high": 26.10,
    "week_low": 25.40,
    "month_high": 27.20,
    "month_low": 24.80,
    "fifty_two_week_high": 27.85,
    "fifty_two_week_low": 23.04
  },
  
  "technicals": {
    "rsi_14": 55.3,
    "macd_line": 0.12,
    "macd_signal": 0.08,
    "macd_histogram": 0.04,
    "fifty_day_average": 26.1,
    "technical_strength": 0.65,
    "price_direction": "bullish",
    "updated_at": "2026-01-28T10:00:00+03:00"
  },
  
  "valuation": {
    "fair_price": 28.50,
    "fair_price_confidence": 0.85,
    "calculated_at": "2026-01-28T10:00:00+03:00"
  },
  
  "analysts": {
    "target_mean": 29.5,
    "target_median": 29.0,
    "target_high": 35.0,
    "target_low": 24.0,
    "consensus": "buy",
    "consensus_score": 2.1,
    "num_analysts": 15
  }
}

Historical Data

Access historical OHLCV (Open, High, Low, Close, Volume) data for technical analysis and backtesting.

GET /historical/{symbol}/

Starter+

Get historical price data for a stock.

Query Parameters:

  • from — Start date YYYY-MM-DD (default: 30 days ago)
  • to — End date YYYY-MM-DD (default: today)
  • interval — Data interval: 1d, 1w, 1m (default: 1d)
View Response
json
{
  "symbol": "2222",
  "interval": "1d",
  "from": "2026-01-01",
  "to": "2026-01-28",
  "count": 20,
  "data": [
    {
      "date": "2026-01-28",
      "open": 25.3,
      "high": 25.68,
      "low": 25.3,
      "close": 25.64,
      "volume": 15738067,
      "adjusted_close": 25.64,
      "turnover": 402108076.72
    }
  ]
}

Financials

Access financial statements including income statements, balance sheets, and cash flow data.

GET /financials/{symbol}/

Starter+

Get financial statements for a company.

View Response
json
{
  "symbol": "2222",
  "income_statements": [
    {
      "report_date": "2025-09-30",
      "total_revenue": 418116750000.0,
      "gross_profit": 215000000000.0,
      "operating_income": 180000000000.0,
      "net_income": 105000000000.0
    }
  ],
  "balance_sheets": [
    {
      "report_date": "2025-09-30",
      "total_assets": 2516431000000.0,
      "total_liabilities": 1026431000000.0,
      "stockholders_equity": 1490000000000.0,
      "total_debt": 356540000000.0
    }
  ],
  "cash_flows": [
    {
      "report_date": "2025-09-30",
      "operating_cash_flow": 135375000000.0,
      "investing_cash_flow": -45000000000.0,
      "financing_cash_flow": -82337000000.0,
      "free_cash_flow": 88500000000.0
    }
  ]
}

Dividends

Get dividend history, upcoming distributions, and trailing yield for a stock.

GET /dividends/{symbol}/

Starter+

Get dividend history and yield information.

View Response
json
{
  "symbol": "2222",
  "current_price": 25.64,
  "trailing_12m_yield": 4.2,
  "trailing_12m_dividends": 1.60,
  "payments_last_year": 4,
  "upcoming": [
    {
      "value": 0.40,
      "period": "Q4",
      "eligibility_date": "2026-03-15",
      "distribution_date": "2026-04-01"
    }
  ],
  "history": [
    {
      "value": 0.40,
      "value_percent": 1.5,
      "period": "Q3",
      "fiscal_year": 2025,
      "announcement_date": "2025-09-01",
      "eligibility_date": "2025-09-15",
      "distribution_date": "2025-10-01"
    }
  ]
}

Stock Events

Get AI-generated summaries of significant stock events and news (Arabic only).

GET /events/

Pro+

Get stock events with AI-generated analysis.

Query Parameters:

  • symbol — Filter by stock ticker (optional)
  • limit — Number of results (default: 20)

Note: Event descriptions are in Arabic only. Event types are UPPERCASE (e.g., FINANCIAL_REPORT, DIVIDEND_ANNOUNCEMENT).

View Response
json
{
  "events": [
    {
      "symbol": "4190",
      "stock_name": "جرير للتسويق",
      "event_type": "FINANCIAL_REPORT",
      "importance": "important",
      "sentiment": "positive",
      "description": "شركة جرير للتسويق تعلن عن نتائج مالية قياسية للربع الرابع 2025...",
      "article_date": "2026-01-29T17:10:06+00:00",
      "created_at": "2026-01-29T17:10:12+00:00"
    }
  ],
  "count": 1,
  "available_types": [
    "FINANCIAL_REPORT", "DIVIDEND_ANNOUNCEMENT", "STOCK_SPLIT",
    "MERGER_ACQUISITION", "MANAGEMENT_CHANGE", "NEW_LISTING",
    "REGULATORY_ACTION", "PARTNERSHIP", "MARKET_EXPANSION",
    "RESTRUCTURING", "EARNINGS_SURPRISE", "PRODUCT_LAUNCH", "OTHER"
  ]
}

importance: critical, important, regular

sentiment: very_positive, positive, slightly_positive, neutral, slightly_negative, negative, very_negative

WebSocket Streaming

Real-time stock price streaming via WebSocket, with market updates pushed as soon as they change.

Connection

Pro+

WebSocket URL:

text
wss://app.sahmk.sa/ws/v1/stocks/?api_key=YOUR_API_KEY

Pro/Enterprise Only: WebSocket streaming requires Pro or Enterprise plan. Updates are delivered during market hours (Sun-Thu, 10:00-15:30 Saudi time). Enterprise connection limits are contract-based.

Subscription Limits

PlanMax symbols/connectionMax symbols/callSubscribe all (*)
Pro6020
EnterpriseCustomCustom

Tip: Use multiple connections to track more than 60 symbols on Pro plan.

Client → Server Messages

ActionMessageDescription
Subscribe{"action": "subscribe", "symbols": ["2222", "1120"]}Subscribe to specific stocks
Subscribe All{"action": "subscribe", "symbols": ["*"]}Subscribe to all stocks (Enterprise only)
Unsubscribe{"action": "unsubscribe", "symbols": ["2222"]}Stop receiving updates
Ping{"action": "ping"}Keep-alive

Server → Client Messages

TypeDescription
connectedConnection confirmed with plan info
subscribedSubscription confirmed with symbol list
quoteReal-time price update
pongPing response
errorError message
View Connected Message Format
json
{
  "type": "connected",
  "plan": "pro",
  "limits": {
    "max_symbols_per_connection": 60,
    "max_symbols_per_call": 20
  },
  "message": "Connected to SAHMK real-time stock stream",
  "timestamp": "2026-02-10T10:00:00.000Z"
}
View Quote Message Format
json
{
  "type": "quote",
  "symbol": "2222",
  "timestamp": "2026-02-10T10:30:15.123Z",
  "data": {
    "price": 25.86,
    "open": 25.60,
    "high": 25.86,
    "low": 25.60,
    "close": 25.86,
    "change": 0.18,
    "change_percent": 0.7,
    "previous_close": 25.68,
    "volume": 9803705,
    "value": 252308343.0,
    "bid": 25.82,
    "ask": 25.86,
    "liquidity": {
      "inflow_value": 184950463.03,
      "inflow_volume": 7182468,
      "outflow_value": 67357881.91,
      "outflow_volume": 2621237,
      "net_value": 117592581.12
    },
    "trade_time": "2026-02-10T10:30:12+00:00"
  }
}

Code Examples

const API_KEY = 'shmk_live_xxxxxxxxxxxxxxxx';
const ws = new WebSocket(`wss://app.sahmk.sa/ws/v1/stocks/?api_key=${API_KEY}`);

ws.onopen = () => {
  console.log('Connected');
  ws.send(JSON.stringify({
    action: 'subscribe',
    symbols: ['2222', '1120', '4191']
  }));
};

ws.onmessage = (event) => {
  const msg = JSON.parse(event.data);
  if (msg.type === 'quote') {
    console.log(`${msg.symbol}: ${msg.data.price} (${msg.data.change_percent}%)`);
  }
};

// Keep-alive ping every 30 seconds
setInterval(() => {
  if (ws.readyState === WebSocket.OPEN) {
    ws.send(JSON.stringify({ action: 'ping' }));
  }
}, 30000);

View more examples on GitHub →

REST vs WebSocket Comparison

MetricREST PollingWebSocket
Latency1-60+ seconds<1 second
API calls/dayThousands1 connection
Missed updatesPossibleNone
ComplexitySimpleSlightly more

WebSocket Error Codes

CodeMeaning
4001Invalid or missing API key
4003Free plan — upgrade required
4004Account inactive or expired

Webhooks Pro+

Register webhook URLs to receive real-time HTTP POST callbacks when price alert conditions are met during trading hours. Pro plans support up to 3 webhooks; Enterprise limits are custom (contract-based).

Endpoints

MethodEndpointDescription
GET/api/v1/webhooks/List registered webhooks
POST/api/v1/webhooks/Register a new webhook URL
DELETE/api/v1/webhooks/{id}/Remove webhook and all its alerts
POST/api/v1/webhooks/{id}/verify/Retry webhook verification

Register a Webhook

curl -X POST "https://app.sahmk.sa/api/v1/webhooks/" \
     -H "X-API-Key: YOUR_API_KEY" \
     -H "Content-Type: application/json" \
     -d '{
       "url": "https://your-server.com/callback",
       "name": "My Production Hook"
     }'

Verification: On creation, SAHMK sends a POST to your URL with { "event": "webhook.verify", "challenge": "..." }. Respond with HTTP 200 to verify. Alerts won't fire until the webhook is verified.

Safety Features

  • HTTPS-only webhook URLs required
  • Webhooks auto-disabled after 3 consecutive delivery failures
  • 3 retry attempts with backoff (2s, 10s, 60s) if delivery fails
  • Duplicate URLs are rejected (409 DUPLICATE)

Price Alerts Pro+

Create price alerts that fire webhook callbacks when conditions are met. Pro plans support up to 10 active alerts; Enterprise limits are custom (contract-based).

Endpoints

MethodEndpointDescription
GET/api/v1/alerts/List alerts (filter: ?status=active|triggered|all)
POST/api/v1/alerts/Create a price alert
DELETE/api/v1/alerts/{id}/Delete an alert

Create an Alert

curl -X POST "https://app.sahmk.sa/api/v1/alerts/" \
     -H "X-API-Key: YOUR_API_KEY" \
     -H "Content-Type: application/json" \
     -d '{
       "symbol": "2222",
       "condition": "price_below",
       "value": 28.50,
       "webhook_id": 1,
       "once": true
     }'

Alert Conditions

ConditionDescriptionExample
price_aboveFires when stock price goes above the valuevalue: 30.00
price_belowFires when stock price goes below the valuevalue: 28.50
pct_changeFires when daily % change exceeds ±valuevalue: 3.0 (means ±3%)

once parameter: When true, the alert fires once then auto-deactivates. When false, it keeps firing with a 5-minute cooldown between triggers.

Webhook Payload

When an alert triggers, your webhook URL receives a POST request with this payload:

json
{
  "event": "price_alert",
  "alert_id": 42,
  "symbol": "2222",
  "condition": "price_below",
  "threshold": 28.5,
  "current_price": 28.40,
  "pct_change": -1.5,
  "volume": 12500000,
  "high": 29.8,
  "low": 28.2,
  "change": -0.45,
  "triggered_at": "2026-02-21T12:30:15+03:00"
}

Error Responses

403PLAN_LIMIT

Free/Starter tried to access webhooks, or alert/webhook limit reached.

400VALIDATION

Missing or invalid fields in the request body.

404NOT_FOUND

Webhook or alert doesn't belong to this developer.

409DUPLICATE

Webhook URL already registered.

Webhook & Alert Limits

PlanWebhooksActive Alerts
Free
Starter
Pro310
EnterpriseCustomCustom

See Rate Limits for the full plan comparison including API quotas, burst limits, and API keys.

Rate Limits

API access is rate-limited based on your subscription plan. There are two types of limits: daily quotas and per-minute burst limits.

Full Plan Comparison

PlanDaily LimitBurst LimitAPI KeysWebSocketWebhooksAlerts
Free100/day10/min1
Starter5,000/day100/min3
Pro50,000/day500/min10310
Enterprise A (Shared)High-volume (Custom)CustomCustomCustomCustom
Enterprise B (Dedicated)Scales with resourcesScales with resourcesCustomCustomCustom

Burst Protection: To prevent abuse, all plans have per-minute burst limits. Requests exceeding the burst limit will receive a 429 response. Daily limits reset at midnight (UTC+3). Enterprise limits are contract-based and may be designed as monthly quotas or resource-based.

Rate Limit Headers

Each response includes headers to help track your usage:

http
X-RateLimit-Limit: 5000
X-RateLimit-Remaining: 4987
X-RateLimit-Reset: 2026-01-30T00:00:00+03:00

Error Codes

The API uses standard HTTP status codes and returns structured JSON error responses.

401INVALID_API_KEY

API key is missing, invalid, or revoked.

403PLAN_LIMIT

Endpoint requires a higher plan (e.g., historical data requires Starter+).

404INVALID_SYMBOL

Stock symbol not found in TASI or Nomu.

429RATE_LIMIT

Daily quota or per-minute burst limit exceeded.

500SERVER_ERROR

Internal server error. Please retry or contact support.

Error Response Format

json
{
  "error": {
    "code": "RATE_LIMIT",
    "message": "Daily request limit exceeded. Resets at midnight UTC+3."
  }
}

Market Data Usage

Sahmk market data may be used within your applications, tools, or products.

Developer plans are intended for development, internal tools, and small-scale applications.

Large-scale public market data platforms, commercial display services, or data redistribution may require an enterprise agreement.

Reselling market data or providing it as a standalone API or data feed is not permitted without a separate agreement with Sahmk.

Need More Help?

Check the machine-readable docs at /api-docs.md, browse examples on GitHub, or contact our team.