API Documentation
Welcome to the SAHMK API. Real-time and historical Saudi stock market data for all 350+ companies on TASI and Nomu — complete reference for endpoints, authentication, streaming, and tools.
Getting Started
Create an account, get your API key, and make your first request. Then move from core REST endpoints to SDKs, automation, and real-time streaming as your product grows.
Start Here
First request
Get a live Saudi stock quote by symbol in minutes.
Build
Core API
Quotes, market data, company data, financials, and events.
Realtime Event Engine
Real-time workflow
Streaming, event webhooks, rules, history, and limits.
Quick Start
- Create a free account
- Get your API key from the dashboard
- Make your first API request
- Browse code examples on GitHub
Base URL
https://app.sahmk.sa/api/v1Your 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 →
Quote path uses a symbol. If you need name/alias resolution, pass it through the optional identifier query param.
Expected Response
{
"symbol": "2222",
"name_en": "Saudi Arabian Oil Co",
"price": 25.86,
"change_percent": 0.7,
"volume": 9803705,
"updated_at": "2026-02-10T12:19:22+00:00",
"is_delayed": false
}Success: You just fetched live Saudi market data from SAHMK. Next, use the SDK for faster integration, explore core endpoints, or move to WebSocket if you need streaming updates.
Ambiguity note: If an identifier maps to multiple companies, pass the exact exchange symbol to disambiguate.
Authentication
All API requests require authentication using an API key. Include your key in the X-API-Key header.
X-API-Key: YOUR_API_KEYAPI 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"Python SDK & CLI
Start here if you want the fastest path from API key to working integration. The official SDK gives you a cleaner client, while the CLI is useful for testing, demos, and automated tooling.
pip install -U sahmk
export SAHMK_API_KEY="your_api_key"
sahmk quote "Saudi Aramco"Python SDK:
from sahmk import SahmkClient
client = SahmkClient(api_key="YOUR_API_KEY")
directory = client.companies(search="aramco", market="TASI", limit=20, offset=0)
symbol = directory["results"][0]["symbol"]
print(client.quote("أرامكو السعودية"))If symbol input is uncertain, call client.companies(...) first to discover a valid symbol before quote/company calls.
Use this when: you want less boilerplate than raw REST, typed workflows in Python, or quick command-line access to quotes and market data.
Quote methods accept identifiers (symbol, Arabic/English name, alias). Use symbol when the identifier is ambiguous.
Full examples: github.com/sahmk-sa/sahmk-python • PyPI: pypi.org/project/sahmk
AI & Agents
Use SAHMK inside Claude Desktop, Cursor, and other MCP-compatible clients. For direct agent consumption, use the MCP server for tool calling and/api-docs.mdfor machine-readable API docs.
pip install sahmk-mcpPackage: pypi.org/project/sahmk-mcp · MCP quick start tutorial
Claude Desktop
macOS: ~/Library/Application Support/Claude/claude_desktop_config.json
Windows: %APPDATA%\Claude\claude_desktop_config.json
{
"mcpServers": {
"sahmk": {
"command": "sahmk-mcp",
"env": {
"SAHMK_API_KEY": "your_api_key_here"
}
}
}
}Cursor
Add to your project's .cursor/mcp.json
{
"mcpServers": {
"sahmk": {
"command": "sahmk-mcp",
"env": {
"SAHMK_API_KEY": "your_api_key_here"
}
}
}
}Use this when: your team wants SAHMK available inside AI workflows, research assistants, or agentic tools without building extra integration glue first.
Use companies_list for symbol discovery (`search`, `market`, `limit`, `offset`) before quote tools.
Stocks
Get real-time stock quotes and prices for individual or multiple companies.
GET /quote/{symbol}/
FreeGet current price and trading data for a single stock symbol.
Path Parameters:
symbol— Exchange symbol (e.g.,2222)
Optional Query Parameters:
identifier— Arabic/English name or alias for resolution (example:أرامكو)
GET /api/v1/quote/2222/?identifier=أرامكوNeed symbols? Use GET /companies/
View Response
{
"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 ordersoutflow_value— Total SAR value of sell ordersnet_value— Net liquidity (inflow - outflow)
GET /quotes/
Starter+Get quotes for multiple companies 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 quotes.
Query Parameters:
symbols— Comma-separated exchange symbols (max 50)identifiers— Comma-separated Arabic/English names or aliases
Use symbols or identifiers, not both in the same request.
Need symbols? Use GET /companies/
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
{
"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
Path vs query parameters: Use path parameters (for example /quote/{symbol}/) for a single resource, and query parameters (for example /events/?symbol=2222&limit=20) to filter list endpoints.
Error handling convention: Common HTTP/API errors are centralized in Error Codes. Endpoint sections only show endpoint-specific errors when needed.
Market
Get market-wide data including index values, top movers, and sector performance.
Market index query parameter:
indexis optional on all market endpoints- Supported values:
TASI,NOMU - Default when omitted:
TASI - Existing URLs without
indexstill work
GET /market/summary/?index=TASI
FreeGet market index value, volume, and market sentiment.
Query Parameters:
index— Market index (TASIorNOMU). Default:TASI
View Response
{
"index": "TASI",
"is_delayed": true,
"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/?limit=10&index=TASI
FreeGet top gaining stocks by percentage change.
Query Parameters:
index— Market index (TASIorNOMU). Default:TASIlimit— Number of results (default: 10, max: 50)
View Response
{
"index": "TASI",
"is_delayed": true,
"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/?limit=10&index=TASI
FreeGet top losing stocks by percentage change.
Query Parameters:
index— Market index (TASIorNOMU). Default:TASIlimit— Number of results (default: 10, max: 50)
View Response
{
"index": "TASI",
"is_delayed": true,
"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/?limit=10&index=TASI
FreeGet top stocks by trading volume.
Query Parameters:
index— Market index (TASIorNOMU). Default:TASIlimit— Number of results (default: 10, max: 50)
View Response
{
"index": "TASI",
"is_delayed": true,
"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/?limit=10&index=TASI
FreeGet top stocks by trading value (SAR).
Query Parameters:
index— Market index (TASIorNOMU). Default:TASIlimit— Number of results (default: 10, max: 50)
View Response
{
"index": "TASI",
"is_delayed": true,
"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/?index=TASI
FreeGet sector performance and statistics.
Query Parameters:
index— Market index (TASIorNOMU). Default:TASI
View Response
{
"index": "TASI",
"is_delayed": true,
"sectors": [
{
"id": "TBNI",
"name": "Banks",
"change_percent": 0.45,
"avg_change_percent": 0.38,
"volume": 45027873,
"num_stocks": 10
}
],
"count": 20
}View Endpoint-Specific Error (invalid index)
{
"error": {
"code": "INVALID_INDEX",
"message": "Invalid index 'XYZ'. Supported values: TASI, NOMU."
}
}Company / Symbol APIs
Discover valid symbols and fetch detailed company information.
GET /companies/
FreeLightweight company directory for symbol discovery before calling quote or company endpoints.
Query Parameters:
search— Matches symbol, Arabic name, or English namemarket—TASIorNOMU(NOMUCalias accepted)limit— Default: 100, max: 500offset— Default: 0
View Response
{
"results": [
{
"symbol": "2222",
"name_ar": "أرامكو السعودية",
"name_en": "Saudi Arabian Oil Co",
"market": "TASI",
"status": "active"
}
],
"count": 1,
"total": 590,
"limit": 100,
"offset": 0
}View Errors
{
"error": {
"code": "INVALID_MARKET",
"message": "market must be one of: TASI, NOMU."
}
}
{
"error": {
"code": "INVALID_PARAM",
"message": "limit and offset must be valid integers."
}
}GET /company/{symbol}/
FreeGet 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
is_delayed indicates pricing freshness: true for delayed prices (Free/Starter) and false for real-time prices (Pro/Business/Enterprise).
New: week_high/low, month_high/low — Price levels in last 7/30 days
View Response (Pro+)
{
"symbol": "2222",
"name": "أرامكو السعودية",
"name_en": "Saudi Arabian Oil Co",
"current_price": 25.64,
"is_delayed": false,
"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— Accepted values: 1d, 1w, 1m (default: 1d; 1w and 1m are aggregated from daily candles)
View Response
{
"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 structured income statement, balance sheet, and cash flow data for Saudi listed companies.
GET /financials/{symbol}/
Starter+View Request Examples
# Starter
GET /api/v1/financials/1120/
# Pro/Business/Enterprise
GET /api/v1/financials/1120/?period=quarterly&history=5y&metrics=extendedKey Parameters
type—income,balance,cashflow,allperiod—annual,quarterlyhistory—1y,3y,5y,10y,maxmetrics—core,extendedresult—series,latestinclude_quality=1— Include metadata and coverage infoinclude_future_placeholders=1— Include future-dated placeholder rows (default is hidden)
Data Available by Plan:
- Starter: Annual + core + up to 3y + latest snapshot
- Pro+: Pro, Business, and Enterprise include quarterly + extended + 5Y/10Y/max + full views
Notes
Response format: Financial statements are returned directly in statement arrays such as income_statements, balance_sheets, and cash_flows.
Included fields: You also get fields such as symbol, statement_period, and optional quality when requested.
Response Example
View Response
{
"symbol": "1120",
"statement_period": "annual",
"quality": {
"coverage": "high",
"warnings": []
},
"income_statements": [
{
"report_date": "2025-09-30",
"total_revenue": 123456789.0,
"net_income": 9876543.0
}
]
}Analytics
Compare and analyze company ratios with compact analytics endpoints.
Response meta: Analytics responses include compact meta fields: period, metrics, warnings.
Ratio keys: Available ratio keys vary by symbol and data coverage, so render keys dynamically instead of assuming a fixed schema.
GET /analytics/ratios/{symbol}/
Starter+Financial ratios for one symbol.
Query Parameters:
history—latest,3y,5y,10y,max(default:latest)period—annual,quarterly(default:annual)metrics—core,extended(default:core)
Note: Free has no analytics access. Starter supports latest + annual + core only. Pro, Business, and Enterprise unlock all ratios options.
Pro Tip: Start with history=latest for low-latency cards, then load longer history only when users open detail views.
View Request Examples
# Starter
GET /api/v1/analytics/ratios/1120/
# Pro/Business/Enterprise
GET /api/v1/analytics/ratios/1120/?history=5y&period=quarterly&metrics=extendedView Ratios Response
{
"symbol": "1120",
"ratios": [
{
"report_date": "2025-12-31",
"statement_period": "annual",
"fiscal_year": 2025,
"fiscal_quarter": null,
"ratios": {
"roe": 9.77,
"roa": 3.89,
"net_margin": 19.09,
"debt_to_equity": 0.6,
"revenue_growth_yoy": 10.0,
"net_income_growth_yoy": 16.67
},
"key_metrics": {
"total_revenue": 1100.0,
"operating_income": 250.0,
"net_income": 210.0,
"operating_cash_flow": 280.0,
"total_assets": 5400.0,
"stockholders_equity": 2150.0,
"total_debt": 1300.0
}
}
],
"meta": {
"period": "annual",
"metrics": "core",
"warnings": []
}
}GET /analytics/compare/
Starter+Compare ratio snapshots across multiple symbols.
Query Parameters:
symbols— required comma-separated symbols (example:1120,1180,1010)metrics—core,extended(default:core)
Note: Starter supports up to 3 symbols and core metrics only. Pro supports up to 10 symbols, while Business and Enterprise support up to 20 symbols; all three support extended.
Pro Tip: Keep a small default symbol set for fast first paint, then let users add symbols progressively.
View Request Examples
# Starter
GET /api/v1/analytics/compare/?symbols=1120,1180,1010
# Business/Enterprise
GET /api/v1/analytics/compare/?symbols=1120,1180,1010,2222&metrics=extendedView Compare Response
{
"results": [
{
"symbol": "1120",
"company_name": "الراجحي",
"sector": "Financial Services",
"market_cap": 1000001120.0,
"current_price": 89.4,
"coverage": "high",
"ratios": {
"roe": 9.77,
"roa": 3.89,
"net_margin": 19.09,
"debt_to_equity": 0.6,
"revenue_growth_yoy": 10.0,
"net_income_growth_yoy": 16.67,
"asset_turnover": 0.2037,
"debt_ratio": 0.2407
},
"key_metrics": {
"total_revenue": 1100.0,
"operating_income": 250.0,
"net_income": 210.0,
"operating_cash_flow": 280.0,
"total_assets": 5400.0,
"stockholders_equity": 2150.0,
"total_debt": 1300.0
}
}
],
"count": 1,
"meta": {
"period": "annual",
"metrics": "extended",
"warnings": []
}
}Dividends
Get dividend history, upcoming distributions, and trailing yield for a stock.
GET /dividends/{symbol}/
Starter+Get dividend history and yield information.
Query Parameters:
limit— Number of records (default: 10, max: 50)
View Response
{
"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.
GET /events/
Pro+Get stock events with AI-generated analysis.
Query Parameters:
symbol— Filter by stock ticker (optional)type— Filter by event type (optional, UPPERCASE)importance— Filter by importance:CRITICAL,IMPORTANT,REGULAR(optional)limit— Number of results (default: 20, max: 100)
Note: Event types are UPPERCASE (e.g., FINANCIAL_REPORT, DIVIDEND_ANNOUNCEMENT).
View Response
{
"events": [
{
"symbol": "4190",
"stock_name": "جرير للتسويق",
"event_type": "FINANCIAL_REPORT",
"importance": "important",
"sentiment": "positive",
"description": "شركة جرير للتسويق تعلن عن نتائج مالية قياسية للربع الرابع 2025...",
"event_date": "2026-01-29",
"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", "OTHER"
]
}available_types: Server-defined list that may expand in future releases.
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:
wss://app.sahmk.sa/ws/v1/stocks/?api_key=YOUR_API_KEYPro/Business/Enterprise: WebSocket streaming requires Pro, Business, or Enterprise plan. Updates are delivered during active trading sessions. Enterprise limits may vary by delivery profile.
Subscription Limits
| Plan | Max symbols/connection | Max symbols/call | Subscribe all (*) |
|---|---|---|---|
| Pro | 60 | 20 | ✗ |
| Business | 120 | 40 | ✗ |
| Enterprise | 200 | 20 (or 100 on enterprise realtime profile) | ✓ |
Notes: Use multiple connections to exceed your plan's per-connection cap. Limits are also returned in the initial connected message under limits.
Client → Server Messages
| Action | Message | Description |
|---|---|---|
| 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 for symbols |
| Ping | {"action": "ping"} | Keep-alive |
Server → Client Messages
| Type | Description |
|---|---|
| connected | Connection confirmed with plan and limits |
| subscribed | Subscription confirmed |
| unsubscribed | Unsubscribe confirmed |
| quote | Real-time price update |
| pong | Ping response |
| error | Error message |
View Connected Message Format
{
"type": "connected",
"plan": "business",
"delivery_profile": "business_standard",
"limits": {
"max_symbols_per_connection": 120,
"max_symbols_per_call": 40,
"stream_modes": ["standard"]
},
"message": "Connected to SAHMK real-time stock stream",
"timestamp": "2026-02-10T10:00:00.000Z"
}View Quote Message Format
{
"type": "quote",
"symbol": "2222",
"mode": "standard",
"timestamp": "2026-02-10T10:30:15.123Z",
"latency_ms": 42,
"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,
"market_session": "REGULAR",
"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 = () => {
ws.send(JSON.stringify({
action: "subscribe",
symbols: ["2222", "1120", "4191"]
}));
};
ws.onmessage = (event) => {
const msg = JSON.parse(event.data);
if (msg.type === "connected") {
console.log("Plan:", msg.plan, "Limits:", msg.limits);
}
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
| Metric | REST Polling | WebSocket |
|---|---|---|
| Latency | 1-60+ seconds | <1 second |
| API calls/day | Thousands | 1 connection |
| Missed updates | Possible | None |
| Complexity | Simple | Slightly more |
WebSocket Error Codes
These are WebSocket service-specific errors. For common HTTP/API errors, see Error Codes.
| Code | Meaning |
|---|---|
| 4000 | Generic connection or server failure |
| 4001 | Invalid or missing API key |
Event Webhooks Pro+
Register webhook endpoints for Realtime Event Engine delivery. This extends existing alert callbacks with delivery status tracking and retry behavior for unsuccessful deliveries.
POST /api/v1/webhooks/
Create a webhook destination for Realtime Event Engine notifications.
Parameters:
url(required) — HTTPS destination URLname(optional) — Friendly label for dashboard/use cases
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://example.com/hooks/sahmk",
"name": "Trading Production Hook"
}'Practical rule examples
Use these templates directly with your own symbol, threshold, and webhook id.
# 1) Large move rule (>= 3% absolute move in 15m)
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",
"event_type": "large_move",
"condition": "pct_change_abs_gte",
"value": 3.0,
"webhook_id": 19,
"once": false,
"config": { "window": "15m" }
}'
# 2) Abnormal volume rule (>= 2.5x baseline volume)
curl -X POST "https://app.sahmk.sa/api/v1/alerts/" \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"symbol": "2010",
"event_type": "abnormal_volume",
"condition": "ratio_gte",
"value": 2.5,
"webhook_id": 19,
"once": false
}'
# 3) Unusual traded value rule (>= 3.0x traded value baseline)
curl -X POST "https://app.sahmk.sa/api/v1/alerts/" \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"symbol": "1180",
"event_type": "unusual_value_traded",
"condition": "ratio_gte",
"value": 3.0,
"webhook_id": 19,
"once": false
}'View Response
{
"id": 19,
"url": "https://example.com/hooks/sahmk",
"name": "Trading Production Hook",
"signing_secret": "xxxxxxxxxxxxxxxx",
"is_verified": false,
"is_active": true,
"created_at": "2026-05-08T13:30:12+03:00"
}Webhook signing: each delivery includes X-SAHMK-Signature (HMAC-SHA256 over canonical payload). Always verify signature and reject mismatches with HTTP 401.
Delivery lifecycle: pending → retrying → delivered or dead_letter. Retry backoff: 2s, 10s, 60s.
Event Rules Pro+
Event rules are the evolution of legacy price alerts. Create rules that evaluate market conditions and trigger webhook events for your subscribed symbols.
Supported event types:
price_alertlarge_moveabnormal_volumeunusual_value_tradedPOST /api/v1/alerts/
Create a realtime event rule.
Parameters:
symbol(required) — Stock symbol such as 2222event_type(required) — one of the realtime event typescondition(required in practice; type-specific) — condition selector used by rule matchingvalue(required) — numeric thresholdwebhook_id(required) — destination created via webhooks endpointonce(optional) — fire once then deactivateconfig(optional) — deterministic options (for examplewindowforlarge_move)
Condition rules:
price_alert:price_above,price_below,pct_changelarge_move:pct_change_abs_gte(normalized by API)abnormal_volume:ratio_gteunusual_value_traded:ratio_gte
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",
"event_type": "large_move",
"condition": "pct_change_abs_gte",
"value": 3.0,
"webhook_id": 19,
"once": false,
"config": { "window": "15m" }
}'View Response
{
"id": 42,
"symbol": "2222",
"event_type": "large_move",
"condition": "pct_change_abs_gte",
"value": "3.0",
"webhook_id": 19,
"status": "active",
"fire_once": false
}GET /api/v1/alerts/
List existing rules and filter by status.
Query options:
status=active|paused|all
curl "https://app.sahmk.sa/api/v1/alerts/?status=active" \
-H "X-API-Key: YOUR_API_KEY"View Response
{
"alerts": [
{
"id": 42,
"symbol": "2222",
"event_type": "large_move",
"condition": "pct_change_abs_gte",
"value": "3.0",
"fire_once": false,
"status": "active",
"webhook_id": 19,
"created_at": "2026-05-08T13:36:45+03:00"
}
]
}Event History Pro+
Query recent realtime event deliveries and outcomes for observability, replay, and debugging.
GET /api/developers/events/
Return delivery history across event types and webhook endpoints.
Authentication: Developer JWT (Authorization: Bearer <token>)
Query options:
status— pending, delivered, retrying, dead_letterevent_type(optional) — filter by event typelimit,offset— pagination
curl "https://app.sahmk.sa/api/developers/events/?status=dead_letter&limit=20" \
-H "Authorization: Bearer YOUR_DEVELOPER_JWT"View Response
{
"events": [
{
"event_id": "be03f577-c9d0-4d8f-a7ef-5353f11e59a7",
"event_type": "abnormal_volume",
"symbol": "2222",
"detected_at": "2026-05-08T13:48:22+03:00",
"delivery_status": "retrying",
"delivery_attempts": 2,
"last_attempt_number": 2,
"webhook_id": 19
}
],
"total": 1,
"has_more": false
}Realtime Event Engine Error Codes
These are Realtime Event Engine service-specific errors. For common HTTP/API errors, see Error Codes.
| HTTP | Error Code | Meaning |
|---|---|---|
| 403 | PLAN_LIMIT | Free/Starter attempted Realtime Event Engine endpoints, or account event limits were reached. |
| 400 | VALIDATION | Invalid payload field or unsupported query option. |
| 404 | NOT_FOUND | Webhook, rule, or history record does not belong to this developer. |
| 401 | INVALID_SIGNATURE | Webhook receiver rejected signature verification. |
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
| Plan | Daily Limit | Burst Limit | API Keys | WebSocket | Event Webhooks | Event Rules |
|---|---|---|---|---|---|---|
| Free | 100/day | 10/min | 1 | ✗ | 0 | 0 |
| Starter | 5,000/day | 100/min | 3 | ✗ | 0 | 0 |
| Pro | 50,000/day | 500/min | 10 | ✓ | 3 | 10 |
| Business | 150,000/day | 1,000/min | 30 | ✓ | 10 | 50 |
| Enterprise | Custom | Custom | Custom | ✓ | Custom | Custom |
Burst Protection: To prevent abuse and protect stability, per-minute throttling is applied at both API-key and account levels. Requests exceeding these limits return HTTP 429. Daily limits reset at midnight (UTC+3). Enterprise limits are contract-based and may be monthly quotas or resource-based.
Rate Limit Headers
Each response includes headers to help track your usage:
X-RateLimit-Limit: 5000
X-RateLimit-Remaining: 4987
X-RateLimit-Reset: 1769816400X-RateLimit-Reset is a Unix timestamp (seconds).
Error Codes
The API uses standard HTTP status codes and returns structured JSON error responses.
400INVALID_ROUTEWrong endpoint path was used. The API returns a suggested correct route.
401INVALID_API_KEYAPI key is missing, invalid, or revoked.
403PLAN_LIMITEndpoint requires a higher plan (e.g., historical data requires Starter+, or higher-tier financials combinations are requested on Starter).
404INVALID_SYMBOLCompany identifier not resolved in TASI or Nomu. Use the exchange symbol when ambiguous.
429RATE_LIMITDaily quota or per-minute burst limit exceeded.
500SERVER_ERRORInternal server error. Please retry or contact support.
Some new free accounts may temporarily hit a security limit. If this happens, the API may return HTTP 429 with error code TEMP_SECURITY_LIMIT. User action: try again later or upgrade for higher limits.
Common integration error: Calling GET /api/v1/quote/batch/ returns 400 INVALID_ROUTE with route guidance.
{
"error": {
"code": "INVALID_ROUTE",
"message": "Did you mean /api/v1/quotes/?symbols=2222,1120 ?"
}
}Error Response Format
{
"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.