Viewing: Trades
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
Stocks, trades, depth, market, historical, company data, and financials.
Realtime
WebSocket
Stocks, trades, and depth channels, limits, and reconnect/error handling.
Alerts & Webhooks
Notification workflow
Event webhooks, rules, delivery behavior, 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://api.sahmk.sa/api/v1Existing integrations using app.sahmk.sa will continue to work.
Your First Request
curl -X GET "https://api.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": 26.6,
"change_percent": 0.0,
"volume": 6601208,
"updated_at": "2026-08-12T12:20:00+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.
Next for data operations: use Data Center in your dashboard when you need bulk exports and structured dataset workflows.
Authentication
All API requests require authentication using an API key. Include your key in the header named X-API-Key.
X-API-Key: YOUR_API_KEYAPI key types:
shmk_live_*- Production keysshmk_test_*- Test keys (same data access; usage counts toward the account's shared daily quota)
curl -X GET "https://api.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"If a newly documented SDK/CLI feature is missing locally, upgrade by running pip install -U sahmk.
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
AI & Agents
Use SAHMK inside ChatGPT, Claude.ai, Claude Code (hosted MCP), or Claude Desktop and Cursor (local MCP). Hosted and local MCP both use your existing SAHMK subscription, permissions, and quota. For direct agent consumption, use the MCP server for tool calling and/api-docs.mdfor machine-readable API docs.
ChatGPT
SAHMK is listed in the ChatGPT Plugins Directory. Add it from the public listing.
You need a SAHMK developer account and an API key. Create an account or open API Keys if you already have one.
Add SAHMK to ChatGPT- Open the SAHMK listing in the ChatGPT Plugins Directory.
- Add or connect SAHMK.
- Sign in to SAHMK.
- Select an API key and allow access.
- Start a conversation and select @SAHMK.
Usage follows your SAHMK subscription, permissions, and daily quota. Real-time data is available only when your plan includes it.
Example:
ما سعر سهم أرامكو الآن، وما حجم التداول وأفضل سعري طلب وعرض؟
Full walkthrough: MCP quick start tutorial
Claude.ai
SAHMK is listed in the Claude directory. Add it from the public listing.
You need a SAHMK developer account and an API key. Create an account or open API Keys if you already have one.
Add SAHMK to Claude- Open the SAHMK listing in the Claude directory.
- Sign in to Claude and add SAHMK.
- Sign in to SAHMK.
- Select an API key and allow access.
- In a conversation, Claude will automatically use SAHMK tools when relevant.
Usage follows your SAHMK subscription, permissions, and daily quota. Real-time data is available only when your plan includes it.
Example:
ما سعر سهم أرامكو الآن، وما حجم التداول وأفضل سعري طلب وعرض؟
Claude Code (CLI)
Connect Claude Code CLI to SAHMK over HTTPS. No Python install or local server required.
Add the hosted MCP server:
claude mcp add --transport http sahmk https://mcp.sahmk.sa/mcpVerify the server is registered:
claude mcp list- Run the add command above in your terminal.
- Inside a Claude Code session, use
/mcpto complete OAuth and authorize SAHMK. - Select your SAHMK API key when prompted.
You can add --scope user to make the server available for all projects.
Local MCP
localInstall the Python package for local MCP with Claude Desktop and Cursor (stdio-based).
pip install -U sahmk-mcpIf MCP tools reject newer params (for example historical interval options), run pip install -U sahmk-mcp and restart your MCP client.
Package: pypi.org/project/sahmk-mcp · MCP quick start tutorial
Related Tutorials
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 current stock quotes and prices for individual or multiple companies. For streaming updates, see Real-time WebSocket.
GET /quote/{symbol}/
FreeGet current price and trading data for a single stock symbol.
Endpoint
https://api.sahmk.sa/api/v1/quote/2222/?identifier=أرامكو
Parameters
(*) Required
| Parameter | Type | Allowed |
|---|---|---|
| symbol * | string | stock ticker |
| identifier | string | company name or alias |
| data_mode | string | realtimedelayedplan default if omitted |
Use symbol when identifier lookup is ambiguous.
Data availability by plan:
- Free / Starter: Delayed prices (~15 minutes)
- Pro+: Real-time prices
{
"symbol": "2222",
"name": "أرامكو السعودية",
"name_en": "Saudi Arabian Oil Co",
"price": 26.6,
"change": 0.0,
"change_percent": 0.0,
"open": 26.6,
"high": 26.68,
"low": 26.52,
"previous_close": 26.6,
"volume": 6601208,
"value": 175637052.96,
"trade_count": 8330,
"bid": 26.6,
"bid_size": 9181,
"ask": 26.62,
"ask_size": 38155,
"liquidity": {
"inflow_value": 99498351.21,
"inflow_volume": 3739041,
"inflow_trades": 4858,
"outflow_value": 76138701.51,
"outflow_volume": 2862167,
"outflow_trades": 3472,
"net_value": 23359649.699999988
},
"auction": {
"phase": "opening",
"price": 26.58,
"price_updated_at": "2026-10-07T06:31:01+00:00",
"volume": 11646,
"volume_updated_at": "2026-10-07T06:31:01+00:00",
"symbol_state": null,
"symbol_session": "pre-open"
},
"updated_at": "2026-08-12T12:20:00+00:00",
"is_delayed": false,
"resolved_instrument": {
"input": "أرامكو",
"symbol": "2222",
"name": "أرامكو",
"match_type": "alias_exact",
"confidence": "high"
}
}Indicative auction
- The same auction object is on the single quote, batch quotes, and the stocks WebSocket.
- It is null outside a live opening or closing auction, and always null on a delayed plan.
- price and updated_at stay the last official trade.
- auction.phase is opening or closing.
View Temporary Price-Data Error
{
"error": {
"code": "PRICE_DATA_TEMPORARILY_UNAVAILABLE",
"message": "Price data for '2222' is temporarily unavailable. Please retry shortly."
}
}GET /quotes/
Starter+Get quotes for multiple companies in a single request. Requires Starter plan or higher.
Free tier fallback: GET /quote/{symbol}/ for single-company quotes.
Endpoint
https://api.sahmk.sa/api/v1/quotes/?symbols=2222,1120
Parameters
(*) Required
| Parameter | Type | Allowed |
|---|---|---|
| symbols * | string | comma-separated tickers |
| identifiers | string | comma-separated company names |
| data_mode | string | realtimedelayedplan default if omitted |
Use either symbols or identifiers, not both.
{
"quotes": [
{
"symbol": "2222",
"name": "أرامكو السعودية",
"name_en": "Saudi Arabian Oil Co",
"price": 26.6,
"change": 0.0,
"change_percent": 0.0,
"open": 26.6,
"high": 26.68,
"low": 26.52,
"previous_close": 26.6,
"volume": 6601208,
"value": 175637052.96,
"trade_count": 8330,
"bid": 26.6,
"ask": 26.62,
"bid_size": 9181,
"ask_size": 38155,
"liquidity": {
"inflow_value": 99498351.21,
"inflow_volume": 3739041,
"inflow_trades": 4858,
"outflow_value": 76138701.51,
"outflow_volume": 2862167,
"outflow_trades": 3472,
"net_value": 23359649.699999988
},
"net_liquidity": 23359649.699999988,
"auction": {
"phase": "opening",
"price": 26.58,
"price_updated_at": "2026-10-07T06:31:01+00:00",
"volume": 11646,
"volume_updated_at": "2026-10-07T06:31:01+00:00",
"symbol_state": null,
"symbol_session": "pre-open"
},
"updated_at": "2026-09-23T01:03:00+03:00",
"is_delayed": false
},
{
"symbol": "1120",
"name": "مصرف الراجحي",
"name_en": "Al Rajhi Banking & Investment Corp SJSC",
"price": 64.05,
"change": -0.05,
"change_percent": -0.08,
"open": 64.1,
"high": 64.3,
"low": 63.7,
"previous_close": 64.1,
"volume": 3775688,
"value": 241832816.4,
"trade_count": 4120,
"bid": 63.95,
"ask": 64.05,
"bid_size": 15,
"ask_size": 43300,
"liquidity": {
"inflow_value": 80000000,
"inflow_volume": 1250000,
"inflow_trades": 1800,
"outflow_value": 127806410.94999999,
"outflow_volume": 2525688,
"outflow_trades": 2320,
"net_value": -47806410.94999999
},
"net_liquidity": -47806410.94999999,
"auction": null,
"updated_at": "2026-09-23T01:03:00+03:00",
"is_delayed": false
}
],
"count": 2,
"max_symbols": 50,
"requested_count": 2
}Batch limits
Requests are limited to 50 symbols. If more are requested, the response also includes truncated: true and a warning explaining that only the first 50 were processed.
Identifier resolution
identifiers is used, the response also includes a resolution object with the requested count requested_count, the resolved count resolved_count, ambiguous names ambiguous, and names not found not_found.Need symbols? Use GET /companies/
Quick conventions: use path params for a single resource (for example /quote/{symbol}/) and query params for list filtering (for example /events/?symbol=2222&limit=20). Common HTTP/API errors are centralized in Error Codes.
Trades
Get recent trades for a symbol. For streaming updates, see Real-time WebSocket.
GET /market/trades/{symbol}/
Pro+Fetch the latest trades for one symbol, newest first.
Endpoint
https://api.sahmk.sa/api/v1/market/trades/2222/?limit=1
Parameters
(*) Required
| Parameter | Type | Allowed |
|---|---|---|
| symbol * | string | stock ticker |
| limit | integer | default 50, max 200 |
{
"symbol": "2222",
"updated_at": "2026-08-12T12:19:10+00:00",
"count": 1,
"events": [
{
"trade_seq": 204505,
"event_time": "2026-08-12T12:19:10+00:00",
"price": 26.6,
"quantity": 20,
"value": 532.0,
"side": "buy"
}
],
"summary": {
"event_count": 1,
"trade_quantity": 20,
"trade_value": 532.0,
"latest_event_time": "2026-08-12T12:19:10+00:00"
}
}View Endpoint-Specific Errors
{
"error": {
"code": "INVALID_SYMBOL",
"message": "Stock symbol '9999' not found."
}
}
{
"error": {
"code": "INVALID_LIMIT",
"message": "limit must be a valid integer."
}
}Trade sequence (trade_seq)
- A trade is uniquely identified by the combination of its symbol, the trading date derived from event_time in the Asia/Riyadh timezone, and trade_seq.
- The sequence number may repeat in a later trading session.
Market
Access market-wide data, including index performance, top gainers and losers, trading activity, and sector performance.
GET /market/summary/?index=TASI
FreeGet market index value, volume, and market sentiment.
Endpoint
https://api.sahmk.sa/api/v1/market/summary/?index=TASI
Parameters
(*) Required
| Parameter | Type | Allowed |
|---|---|---|
| index | string | TASINOMUdefault TASI |
| data_mode | string | realtimedelayedplan default if omitted |
{
"index": "TASI",
"is_delayed": false,
"timestamp": "2026-08-12T12:20:00+00:00",
"index_value": 10844.12,
"index_change": 10.94,
"index_change_percent": 0.1,
"total_volume": 256387075,
"advancing": 141,
"declining": 112,
"unchanged": 17,
"market_mood": "Bullish"
}GET /market/gainers/?limit=1&index=TASI
FreeGet top gaining stocks by percentage change.
Endpoint
https://api.sahmk.sa/api/v1/market/gainers/?limit=1&index=TASI
Parameters
(*) Required
| Parameter | Type | Allowed |
|---|---|---|
| index | string | TASINOMUdefault TASI |
| limit | number | default 10, max 50 |
| data_mode | string | realtimedelayedplan default if omitted |
{
"index": "TASI",
"is_delayed": false,
"gainers": [
{
"symbol": "6040",
"name": "شركة تبوك للتنمية الزراعية",
"name_en": "Tabuk Agriculture Development C",
"price": 9.13,
"change": 0.83,
"change_percent": 10.0,
"volume": 4846179,
"updated_at": "2026-08-12T13:00:00+00:00"
}
],
"count": 1
}GET /market/losers/?limit=1&index=TASI
FreeGet top losing stocks by percentage change.
Endpoint
https://api.sahmk.sa/api/v1/market/losers/?limit=1&index=TASI
Parameters
(*) Required
| Parameter | Type | Allowed |
|---|---|---|
| index | string | TASINOMUdefault TASI |
| limit | number | default 10, max 50 |
| data_mode | string | realtimedelayedplan default if omitted |
{
"index": "TASI",
"is_delayed": false,
"losers": [
{
"symbol": "4011",
"name": "شركة لازوردي للمجوهرات",
"name_en": "L'azurde Company for Jewelry SJSC",
"price": 10.43,
"change": -0.47,
"change_percent": -4.31,
"volume": 369026,
"updated_at": "2026-08-12T13:00:00+00:00"
}
],
"count": 1
}GET /market/volume/?limit=1&index=TASI
FreeGet top stocks by trading volume.
Endpoint
https://api.sahmk.sa/api/v1/market/volume/?limit=1&index=TASI
Parameters
(*) Required
| Parameter | Type | Allowed |
|---|---|---|
| index | string | TASINOMUdefault TASI |
| limit | number | default 10, max 50 |
| data_mode | string | realtimedelayedplan default if omitted |
{
"index": "TASI",
"is_delayed": false,
"stocks": [
{
"symbol": "6015",
"name": "شركة أمريكانا للمطاعم العالمية بي إل سي - شركة أجنبية",
"name_en": "AMERICANA",
"price": 2.38,
"change": 0.07,
"change_percent": 3.03,
"volume": 22562421,
"updated_at": "2026-08-12T13:00:00+00:00"
}
],
"count": 1
}GET /market/value/?limit=1&index=TASI
FreeGet top stocks by trading value (SAR).
Endpoint
https://api.sahmk.sa/api/v1/market/value/?limit=1&index=TASI
Parameters
(*) Required
| Parameter | Type | Allowed |
|---|---|---|
| index | string | TASINOMUdefault TASI |
| limit | number | default 10, max 50 |
| data_mode | string | realtimedelayedplan default if omitted |
{
"index": "TASI",
"is_delayed": false,
"stocks": [
{
"symbol": "2380",
"name": "شركة رابغ للتكرير والبتروكيماويات",
"name_en": "Rabigh Refining and Petrochemic",
"price": 17.6,
"change": 0.51,
"change_percent": 2.98,
"volume": 14502060,
"value": 257478357.3,
"updated_at": "2026-08-12T13:00:00+00:00"
}
],
"count": 1
}GET /market/sectors/?index=TASI
FreeGet sector performance and statistics.
Endpoint
https://api.sahmk.sa/api/v1/market/sectors/?index=TASI
Parameters
(*) Required
| Parameter | Type | Allowed |
|---|---|---|
| index | string | TASINOMUdefault TASI |
| data_mode | string | realtimedelayedplan default if omitted |
{
"index": "TASI",
"is_delayed": false,
"sectors": [
{
"sector_name": "Insurance",
"sector_name_ar": "التأمين",
"change_percent": 1.49,
"avg_change_percent": 0.02,
"volume": 9819397,
"num_stocks": 26
}
],
"count": 20
}View Endpoint-Specific Error (invalid index)
{
"error": {
"code": "INVALID_INDEX",
"message": "Invalid index 'XYZ'. Supported values: TASI, NOMU."
}
}Depth
Get order book depth snapshots for a symbol with selectable level controls. For streaming updates, see Real-time WebSocket.
GET /market/depth/{symbol}/
Pro+Fetch depth snapshots for a symbol with requested level depth.
Access requirement
A Pro or higher plan is required, and REST depth access must also be enabled for your account. Plan eligibility alone does not activate this market-data entitlement.
Endpoint
https://api.sahmk.sa/api/v1/market/depth/2222/?levels=5
Parameters
(*) Required
| Parameter | Type | Allowed |
|---|---|---|
| symbol * | string | stock ticker |
| levels | integer | 1-20 |
The number of returned levels may be lower than requested, depending on your account's entitlement and the market depth currently available.
Data availability by plan:
- Pro: Eligible for top-5 depth. Request access
- Business+: Eligible for top-20 Premium depth data. Request access
{
"symbol": "2222",
"updated_at": "2026-08-12T12:19:54.982770+00:00",
"session": "atc",
"book_state": "normal",
"levels": 5,
"best_bid": 26.6,
"best_ask": 26.62,
"spread": 0.02,
"spread_bps": 7.52,
"total_bid_quantity_top5": 241514,
"total_ask_quantity_top5": 491802,
"total_bid_quantity": 241514,
"total_ask_quantity": 491802,
"level_imbalance": -0.3413,
"level_imbalance_top5": -0.3413,
"bids": [
{ "level": 0, "price": 26.6, "quantity": 9181, "order_count": 18 },
{ "level": 1, "price": 26.58, "quantity": 228, "order_count": 3 },
{ "level": 2, "price": 26.56, "quantity": 13363, "order_count": 34 },
{ "level": 3, "price": 26.54, "quantity": 4133, "order_count": 21 },
{ "level": 4, "price": 26.52, "quantity": 214609, "order_count": 120 }
],
"asks": [
{ "level": 0, "price": 26.62, "quantity": 38155, "order_count": 17 },
{ "level": 1, "price": 26.64, "quantity": 25208, "order_count": 12 },
{ "level": 2, "price": 26.66, "quantity": 17877, "order_count": 36 },
{ "level": 3, "price": 26.68, "quantity": 203743, "order_count": 53 },
{ "level": 4, "price": 26.7, "quantity": 206819, "order_count": 72 }
],
"market_order": {
"bid_quantity": 79938,
"bid_order_count": 22,
"bid_updated_at": "2026-10-06T06:31:50+00:00",
"ask_quantity": 87743,
"ask_order_count": 11,
"ask_updated_at": "2026-10-06T06:31:35+00:00"
},
"entitled_levels": 5
}Returned depth
- The response is the best snapshot available at the time of the request.
- You may receive fewer levels than requested, depending on your plan and what the market is publishing.
Market orders
- market_order sits beside the priced ladder and is null when neither side has a positive quantity.
- Price 0 is not a priced level and is not included in the best bid, best ask, spread, or quantity totals.
- A book that contains only market orders is still delivered.
View Endpoint-Specific Errors
{
"error": {
"code": "DEPTH_NOT_AVAILABLE",
"message": "No usable depth snapshot found for '2222'."
}
}
{
"error": {
"code": "MARKET_DATA_ENTITLEMENT_REQUIRED",
"message": "Market-depth access requires an approved entitlement."
}
}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. Includes Sukuk and other instrument types via security_type.
Endpoint
https://api.sahmk.sa/api/v1/companies/?search=aramco&market=TASI&limit=1&offset=0
Parameters
(*) Required
| Parameter | Type | Allowed |
|---|---|---|
| search | string | free text |
| market | string | TASINOMU |
| limit | number | default 100, max 500 |
| offset | number | default 0 |
{
"results": [
{
"symbol": "2222",
"name_ar": "أرامكو السعودية",
"name_en": "SAUDI ARAMCO",
"market": "TASI",
"status": "active",
"security_type": "Equity",
"market_segment": "TASI",
"is_etf": false
}
],
"count": 1,
"total": 1,
"limit": 1,
"offset": 0
}security_type values: Equity, Sukuk, ETF, Closed-End Fund, Unknown. Unknown means source market metadata is incomplete for that symbol. Sukuk instruments are included in discovery responses.
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, security_type, sector_name, sector_name_ar, market_id, description, website
- Starter: + Full fundamentals (PE, EPS, book value, beta, week/month/52w ranges)
- Pro+: + Technicals, valuation, analyst consensus
Endpoint
https://api.sahmk.sa/api/v1/company/2222/
Parameters
(*) Required
| Parameter | Type | Allowed |
|---|---|---|
| symbol * | string | stock ticker |
{
"symbol": "2222",
"name": "أرامكو السعودية",
"name_en": "SAUDI ARAMCO",
"current_price": 26.6,
"is_delayed": false,
"sector_name": "Energy",
"sector_name_ar": "الطاقة",
"market_id": "TASI",
"security_type": "Equity",
"description": "Saudi Arabian Oil Company operates as an integrated energy and chemical company in the Kingdom of Saudi Arabia and internationally. The company operates through two segments, Upstream and Downstream. The Upstream segment explores, develops, produces, and sells crude oil, condensate, natural gas, and natural gas liquids (NGLs). The Downstream segment produces various chemicals, such as aromatics, olefins, and polyolefins; polyols, isocyanates, and synthetic rubber; methanol, MTBE, glycols, linear alpha olefins, polyethylene, polypropylene, polyethylene terephthalate, polyvinyl chloride, polystyrene, polycarbonate, and engineering thermoplastics and their blends; and lubricants and base oils, as well as engages in the refining and petrochemicals, retail operations, distribution, supply and trading, and power generation. It also markets and distributes hydrocarbons, petroleum products; and trades crude oil, refined petroleum, and liquid chemical products. In addition, the company develops, manufactures, and markets high-performance rubber; and provides crude oil storage, investment, consulting, information technology, personnel and other support, agri-nutrients, purchasing, engineering, benefits administration, oil field, insurance, pipeline transport, vendor sourcing, marketing and sales support, financing, support, and marine management and transportation services. Further, it engages in aircraft operations and leasing; aviation; sports club; retail fuel marketing and operations; investment management of post-employment benefit plans; wholesale fuel operations; importing and exporting refined products and crude oil; and real estate holdings. Additionally, the company engages in prospecting, exploring, drilling, processing, manufacturing, refining, extracting, and marketing hydrocarbon substances. The company was founded in 1933 and is headquartered in Dhahran, the Kingdom of Saudi Arabia.",
"website": "https://www.aramco.com",
"country": "Saudi Arabia",
"currency": "SAR",
"fundamentals": {
"market_cap": 6437200000000.0,
"pe_ratio": 15.65,
"forward_pe": 15.93,
"eps": 1.6998,
"eps_ttm": 1.6998,
"basic_eps": 1.4382,
"diluted_eps": null,
"book_value": 6.49,
"price_to_book": 4.1,
"beta": 0.01,
"shares_outstanding": 242000000000,
"float_shares": 6014547000,
"week_high": 26.86,
"week_low": 26.5,
"month_high": 27.26,
"month_low": 26.12,
"fifty_two_week_high": 27.96,
"fifty_two_week_low": 23.04
},
"technicals": {
"rsi_14": 57.45,
"macd_line": 0.0187,
"macd_signal": 0.027,
"macd_histogram": -0.0084,
"fifty_day_average": 26.53,
"technical_strength": 0.0,
"price_direction": "متذبذب",
"updated_at": "2026-08-12T12:45:00.690083+00:00"
},
"valuation": {
"fair_price": 24.71,
"fair_price_confidence": 0.95,
"calculated_at": "2026-08-11T21:00:00.010275+00:00"
},
"analysts": {
"target_mean": 30.12,
"target_median": 29.8,
"target_high": 35.0,
"target_low": 26.8,
"consensus": "buy",
"consensus_score": 2.11,
"num_analysts": 18
}
}security_type is available on Free+ and uses the same values as GET /companies/: Equity, Sukuk, ETF, Closed-End Fund, or Unknown when source market metadata is incomplete.
Historical & Intraday Data
Access historical and intraday OHLCV (Open, High, Low, Close, Volume) data across minute-based, daily, weekly, and monthly intervals for technical analysis and backtesting.
GET /historical/{symbol}/
Starter+Get historical and intraday OHLCV data for a stock.
Endpoint
https://api.sahmk.sa/api/v1/historical/2222/?interval=1d&from=2024-03-03&to=2026-01-01&limit=1&offset=0
Parameters
(*) Required
| Parameter | Type | Allowed |
|---|---|---|
| symbol * | string | ticker, TASI, or NOMU |
| from | string | date as YYYY-MM-DD default ~30 days ago |
| to | string | date as YYYY-MM-DD default today |
| interval | string | 1min5min15min30min60min1d1w1moDefault: 1d. Legacy aliases: 30m → 30min, 60m → 60min, 1m → 1mo (monthly, not 1 minute). |
| limit | integer | default 500, max 2000 |
| offset | integer | Default: 0. Number of bars to skip from the beginning of the chronologically ordered results. |
Data Available by Plan:
- Free: No historical access
- Starter:
1d,1w,1mo - Pro: Starter intervals +
15min,30min,60min, each up to 90 calendar days - Business: All Pro intervals, plus
5min(up to 90 calendar days). Extended history:15minand30minup to 6 calendar months;60minup to 1 calendar year - Enterprise: Custom interval access, including
1minwhere enabled, with historical coverage based on the agreement.
A valid interval not included in your plan, or a requested date range exceeding your plan's limits, returns 403 PLAN_LIMIT.Invalid interval values return 400.Symbol and date coverage can be shorter than the maximum window.Intraday candles are updated in real time during market sessions. Historical coverage limits determine how far back you can retrieve data.
Need a practical intraday workflow? See Intraday Data with SAHMK (SDK + MCP).
{
"symbol": "2222",
"interval": "1d",
"is_intraday": false,
"is_final": true,
"partial": false,
"latest_bar_at": "2024-03-03",
"from": "2024-03-03",
"to": "2026-01-01",
"limit": 1,
"offset": 0,
"total": 500,
"count": 1,
"has_more": true,
"data": [
{
"date": "2024-03-03",
"open": 33.0,
"high": 33.15,
"low": 32.9,
"close": 33.05,
"volume": 12123324,
"adjusted_close": 33.05,
"turnover": 400069692.0,
"trade_count": 8082
}
]
}Candle Row Format
- Intraday intervals
1min,5min,15min,30min, and60minrows use an ISO timestamp indateand includeturnover,trade_count,is_final, andpartial. On30minand60min,number_of_tradesis a temporary alias of the same value. 1d/1w/1morows use a date indateand includeturnover.trade_countis included when EOD trade counts exist.- Weekly and monthly
trade_countvalues are the sum of available daily counts in the ISO week or calendar month. - Legacy aliases:
30m→30min,60m→60min,1m→1mo(monthly, not 1 minute). adjusted_closemirrorsclosefor post-March-2024 and intraday records.- While
has_moreis true, request the next page usingoffset + limit.
Financials
Access structured income statement, balance sheet, and cash flow data for Saudi listed companies.
GET /financials/{symbol}/
Starter+Endpoint
https://api.sahmk.sa/api/v1/financials/1120/?period=annual&history=1y&metrics=extended
Parameters
(*) Required
| Parameter | Type | Allowed |
|---|---|---|
| symbol * | string | stock ticker |
| type | string | incomebalancecashflowalldefault all |
| period | string | annualquarterlyautodefault annual |
| history | string | 1y3y5y10ymax |
| metrics | string | coreextended |
| result | string | serieslatestdefault series |
| limit | integer | default 4, max 20 |
| include_partial | string | 01default 0 |
| include_future_placeholders | string | 01default 0 |
Behavior Notes
period=autoresolves toannualwhen the latest fiscal year is full-year, otherwisequarterly.- Starter clients should request
period=annualexplicitly; ifautoresolves to quarterly, the API returns403 PLAN_LIMIT. - Annual history returns completed fiscal years by default; use
include_partial=1to include the current incomplete annual/YTD row. - Quarterly mode returns the latest available quarters by default.
result=latestfollows the selected/resolved granularity; in annual mode it returns the latest completed year by default unlessinclude_partial=1is used.- API default remains
period=annualfor backward compatibility. - Statements are returned in arrays such as
income_statements,balance_sheets, andcash_flows. - Responses also include
symbol,statement_period,semantics, and public reporting context inreporting.
Data Available by Plan:
- Starter: Annual statements; request period=annual explicitly. History controls support up to 3 years, and a bare request retains the legacy 4-row extended response.
- Pro/Business: Quarterly + extended + 5Y/10Y/max + full views
- Enterprise: Custom financials feature set and access profile by agreement
Response Example
{
"symbol": "1120",
"statement_period": "annual",
"income_statements": [
{
"report_date": "2025-12-31",
"statement_period": "annual",
"fiscal_year": 2025,
"quarters_reported": 4,
"quarters_covered": 4,
"is_full_year": true,
"total_revenue": 39093965000.0,
"gross_profit": 6730335.0,
"operating_income": null,
"net_income": 24791754000.0
}
],
"balance_sheets": [
{
"report_date": "2025-12-31",
"statement_period": "annual",
"fiscal_year": 2025,
"quarters_reported": 4,
"quarters_covered": 4,
"is_full_year": true,
"total_assets": 1043268297000.0,
"total_liabilities": 900355952000.0,
"stockholders_equity": 142912345000.0,
"total_debt": null
}
],
"cash_flows": [
{
"report_date": "2025-12-31",
"statement_period": "annual",
"fiscal_year": 2025,
"quarters_reported": 4,
"quarters_covered": 4,
"is_full_year": true,
"operating_cash_flow": -22373072000.0,
"investing_cash_flow": -1564467000.0,
"financing_cash_flow": 36302349000.0,
"free_cash_flow": null
}
],
"semantics": {
"income": "annual_full_year",
"cashflow": "annual_full_year",
"balance": "year_end_snapshot"
},
"reporting": {
"reporting_cadence": "quarterly",
"quarterly_income_convention": "mixed"
}
}Analytics
Compare and analyze company ratios with compact analytics endpoints.
GET /analytics/ratios/{symbol}/
Starter+Financial ratios for one symbol.
Endpoint
https://api.sahmk.sa/api/v1/analytics/ratios/1120/?history=latest&period=quarterly&metrics=extended
Parameters
(*) Required
| Parameter | Type | Allowed |
|---|---|---|
| symbol * | string | stock ticker |
| history | string | latest3y5y10ymaxdefault latest |
| period | string | annualquarterlydefault annual |
| metrics | string | coreextendeddefault core |
| meta | string | minimalextendeddefault minimal |
Data Available by Plan:
- Free: No analytics access
- Starter: Latest + annual + core only
- Pro/Business: All ratios options (history, period, metrics)
- Enterprise: Custom ratios feature set and access profile by agreement
{
"symbol": "1120",
"ratios": [
{
"report_date": "2026-06-30",
"statement_period": "quarterly",
"fiscal_year": 2026,
"fiscal_quarter": 2,
"ratios": {
"roe": 4.6,
"roa": 0.66,
"net_margin": 64.42,
"revenue_growth_yoy": 13.35,
"net_income_growth_yoy": 14.0,
"asset_turnover": 0.0103
},
"key_metrics": {
"total_revenue": 10884480000.0,
"net_income": 7012137000.0,
"operating_cash_flow": 10379536000.0,
"total_assets": 1054772497000.0,
"stockholders_equity": 152416762000.0
}
}
],
"meta": {
"period": "quarterly",
"metrics": "extended",
"warnings": [
"Some metrics unavailable"
]
}
}GET /analytics/compare/
Starter+Compare ratio snapshots across multiple symbols.
Endpoint
https://api.sahmk.sa/api/v1/analytics/compare/?symbols=1120&metrics=extended
Parameters
(*) Required
| Parameter | Type | Allowed |
|---|---|---|
| symbols * | string | comma-separated tickers |
| metrics | string | coreextendeddefault core |
| meta | string | minimalextendeddefault minimal |
Data Available by Plan:
- Starter: Up to 3 symbols + core metrics only
- Pro: Up to 10 symbols + core/extended metrics
- Business: Up to 20 symbols + core/extended metrics
- Enterprise: Custom symbol limits and compare feature profile by agreement
{
"results": [
{
"symbol": "1120",
"company_name": "مصرف الراجحي",
"sector_name": "Banks",
"sector_name_ar": "البنوك",
"market_id": "TASI",
"market_cap": 384600000000.0,
"current_price": 64.05,
"ratios": {
"roe": 9.48,
"roa": 1.37,
"net_margin": 67.49,
"asset_turnover": 0.0203
},
"key_metrics": {
"total_revenue": 21412480000.0,
"net_income": 14452137000.0,
"total_assets": 1054772497000.0,
"stockholders_equity": 152416762000.0
}
}
],
"count": 1,
"meta": {
"period": "annual",
"metrics": "extended",
"warnings": [
"Some metrics unavailable"
]
}
}Integration tips
- Default metadata is minimal:
meta.period,meta.metrics,meta.warnings. - Use
meta=extendedfor extra fields (ratios:coverage,periods_available,quality,partial_context; compare rows:coverage). - Ratio keys are dynamic, so render ratio objects dynamically.
Dividends
Access historical and upcoming cash dividends, along with the trailing 12-month dividend yield for a stock.
GET /dividends/{symbol}/
Starter+Get dividend history and yield information.
Endpoint
https://api.sahmk.sa/api/v1/dividends/2222/?limit=1
Parameters
(*) Required
| Parameter | Type | Allowed |
|---|---|---|
| symbol * | string | stock ticker |
| limit | number | default 10, max 50 |
{
"symbol": "2222",
"current_price": 26.6,
"trailing_12m_yield": 5.07,
"trailing_12m_dividends": 1.3491,
"payments_last_year": 4,
"upcoming": [
{
"value": 0.3393,
"period": "Q2",
"eligibility_date": "2026-08-19",
"distribution_date": "2026-08-27"
}
],
"history": [
{
"value": 0.3393,
"value_percent": null,
"period": "Q2",
"fiscal_year": "2026",
"announcement_date": "2026-08-04",
"eligibility_date": "2026-08-19",
"distribution_date": "2026-08-27"
}
]
}Stock Events
Get AI-generated summaries of significant stock events and news.
GET /events/
Pro+Get stock events with AI-generated analysis.
Note
Event types are UPPERCASE (e.g., FINANCIAL_REPORT, DIVIDEND_ANNOUNCEMENT).
Use uppercase values when filtering by importance. Response values are returned in lowercase.
Endpoint
https://api.sahmk.sa/api/v1/events/?symbol=4190&importance=IMPORTANT&limit=1
Parameters
(*) Required
| Parameter | Type | Allowed |
|---|---|---|
| symbol | string | stock ticker |
| type | string | FINANCIAL_REPORTDIVIDEND_ANNOUNCEMENTSTOCK_SPLITMERGER_ACQUISITIONMANAGEMENT_CHANGENEW_LISTINGDELISTINGREGULATORY_ACTIONPRODUCT_LAUNCHPARTNERSHIPLEGAL_ISSUEMARKET_EXPANSIONRESTRUCTURINGEARNINGS_SURPRISEINSIDER_TRADINGANALYST_RATING_CHANGECAPITAL_INCREASESHAREHOLDER_MEETINGOTHER |
| importance | string | CRITICALIMPORTANTREGULAR |
| limit | number | default 20, max 100 |
| offset | number | default 0 |
{
"events": [
{
"symbol": "4190",
"stock_name": "مكتبة جرير",
"event_type": "MARKET_EXPANSION",
"importance": "important",
"sentiment": "positive",
"description": "افتتحت مكتبة جرير معرضاً جديداً بمطار الملك فهد الدولي في الدمام، وهو المعرض التاسع والستون داخل المملكة، ويعد الخامس خلال عام 2026، باستثمارات بلغت 2 مليون ريال سعودي.",
"event_date": "2026-08-10",
"article_date": "2026-08-10T13:14:06.653295+00:00",
"created_at": "2026-08-10T13:14:10.917105+00:00"
}
],
"count": 1,
"total": 78,
"limit": 1,
"offset": 0,
"has_more": true,
"available_types": [
"FINANCIAL_REPORT", "DIVIDEND_ANNOUNCEMENT", "STOCK_SPLIT",
"MERGER_ACQUISITION", "MANAGEMENT_CHANGE", "NEW_LISTING",
"DELISTING", "REGULATORY_ACTION", "PRODUCT_LAUNCH",
"PARTNERSHIP", "LEGAL_ISSUE", "MARKET_EXPANSION",
"RESTRUCTURING", "EARNINGS_SURPRISE", "INSIDER_TRADING",
"ANALYST_RATING_CHANGE", "CAPITAL_INCREASE", "SHAREHOLDER_MEETING", "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
Portfolio
SAHMK Portfolio provides a structured transaction ledger with holdings and performance summaries, available through API and the developer dashboard.
GET/POST /portfolios/
FreeCreate and manage portfolios within your plan limits.
Endpoint
https://api.sahmk.sa/api/v1/portfolios/
Parameters
(*) Required
| Parameter | Type | Allowed |
|---|---|---|
| name * | string | portfolio name |
| description | string | optional description |
| workspace | number | optional workspace id |
{
"id": 42,
"owner_id": 1198,
"workspace": null,
"name": "Core Portfolio",
"description": "Long-only Tadawul",
"base_currency": "SAR",
"metadata": {},
"created_at": "2026-10-17T14:20:00Z",
"updated_at": "2026-10-17T14:20:00Z"
}POST /portfolios/{portfolio_id}/transactions/
FreeAppend transactions to the portfolio ledger (deposit, buy, sell, dividend, fee, withdrawal).
Endpoint
https://api.sahmk.sa/api/v1/portfolios/{portfolio_id}/transactions/
Parameters
(*) Required
| Parameter | Type | Allowed |
|---|---|---|
| transaction_type * | string | depositbuyselldividendfeewithdrawal |
| amount_sar * | number | transaction amount in SAR |
| symbol | string | required for buy/sell |
| quantity | number | required for buy/sell |
| unit_price_sar | number | unit price for buy/sell |
| fee_sar | number | optional fee |
Note
The ledger is append-only, and only the latest transaction can be reversed.
Data Available by Plan:
- Free: Up to 1 portfolio, delayed valuation, and performance up to 30 days.
- Starter: Up to 3 portfolios, delayed valuation, and performance up to 1 year.
- Pro: Up to 10 portfolios, realtime valuation, and performance up to 3 years.
- Business: Up to 50 portfolios, realtime valuation, and full available performance window.
Workspace integration
Workspace attachment is optional. It adds an operational layer for monitoring and alerts while the portfolio accounting model remains independent.
Real-time WebSocket - Stocks
Stream real-time quote updates through the stocks channel.
WS /ws/v1/stocks/
Pro+Real-time quotes channel for live stock updates.
Endpoint
wss://api.sahmk.sa/ws/v1/stocks/?api_key=YOUR_API_KEY
Connected message
{
"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-07-09T15:05:37.749535+00:00"
}Client actions
{"action":"subscribe","symbols":["2222","1120"]}
{"action":"unsubscribe","symbols":["1120"]}
{"action":"ping"}
{"action":"subscribe","symbols":["*"]}The last command, subscribing to all symbols, is available on the Enterprise plan only.
Quote message
{
"type": "quote",
"symbol": "2222",
"mode": "standard",
"data": {
"price": 25.86,
"open": 25.4,
"high": 25.9,
"low": 25.2,
"close": 25.86,
"change": 0.86,
"change_percent": 3.44,
"previous_close": 25.0,
"volume": 9803705,
"value": 252308343.0,
"trade_count": 12289,
"bid": 25.84,
"ask": 25.88,
"bid_size": 79,
"ask_size": 188,
"liquidity": {
"inflow_value": 154800000.0,
"inflow_volume": 6000000,
"outflow_value": 97508343.0,
"outflow_volume": 3803705,
"net_value": 57291657.0
},
"trade_time": "2026-10-06T12:00:00+00:00",
"market_session": "pre-open",
"auction": {
"phase": "opening",
"price": 25.9,
"price_updated_at": "2026-10-07T06:31:01+00:00",
"volume": 11646,
"volume_updated_at": "2026-10-07T06:31:01+00:00",
"symbol_state": null,
"symbol_session": "pre-open"
}
},
"timestamp": "2026-10-07T06:31:01.114000+00:00",
"latency_ms": 14
}Indicative auction
- data.auction uses the same object as the REST quote.
- It is null outside a live opening or closing auction, and always null on a delayed plan.
- data.price stays the last official trade.
Related Tutorials
Real-time WebSocket - Trades
Stream executed trades in real time through the trades channel. When subscribing to specific symbols, you first receive a subscribed acknowledgement, followed by a trades_snapshot for each newly subscribed symbol, then continuous live trade updates.
WS /ws/v1/market/trades/
Pro+Real-time trades channel for live trade updates.
Endpoint
wss://api.sahmk.sa/ws/v1/market/trades/?api_key=YOUR_API_KEY
Optional: add symbol=2222 to auto-subscribe on connect.
Connected message
{
"type": "connected",
"channel": "trades",
"plan": "pro",
"delivery_profile": "standard",
"limits": {
"max_symbols_per_connection": 60,
"max_symbols_per_call": 20,
"snapshot_limit_max": 200
},
"message": "Connected to SAHMK real-time trades stream",
"timestamp": "2026-07-28T09:31:58.197050+00:00"
}Client actions
{"action":"subscribe","symbols":["2222","1120"]}
{"action":"unsubscribe","symbols":["1120"]}
{"action":"snapshot","symbol":"2222","limit":50}
{"action":"ping"}
{"action":"subscribe","symbols":["*"]}The last command, subscribing to all symbols, is available on the Enterprise plan only.
Snapshot limit defaults to 50 (max 200).
Snapshot message
{
"type": "trades_snapshot",
"symbol": "2222",
"updated_at": "2026-07-28T09:31:55+00:00",
"count": 2,
"events": [
{
"trade_seq": 204505,
"event_time": "2026-07-28T09:31:55+00:00",
"price": 26.3,
"quantity": 40,
"value": 1052.0,
"side": "buy"
},
{
"trade_seq": 204490,
"event_time": "2026-07-28T09:31:47+00:00",
"price": 26.3,
"quantity": 5000,
"value": 131500.0,
"side": "sell"
}
],
"summary": {
"event_count": 2,
"trade_quantity": 5040,
"trade_value": 132552.0,
"latest_event_time": "2026-07-28T09:31:55+00:00"
},
"timestamp": "2026-07-28T09:31:58.300000+00:00"
}Trade message
{
"type": "trade",
"symbol": "2222",
"trade_seq": 204520,
"event_time": "2026-07-28T09:31:58+00:00",
"price": 26.32,
"quantity": 10,
"value": 263.2,
"side": "buy",
"market_session": "REGULAR",
"timestamp": "2026-07-28T09:31:58.774829+00:00"
}trade_seq
- The symbol, the Asia/Riyadh day of event_time, and trade_seq identify the trade.
- The number can repeat on a later session.
Related Tutorials
Real-time WebSocket - Depth
Stream real-time level-by-level bid/ask depth updates through the depth channel.
WS /ws/v1/market/depth/
Pro+Real-time depth channel for level-by-level bid/ask streaming.
Endpoint
wss://api.sahmk.sa/ws/v1/market/depth/?api_key=YOUR_API_KEY
Data availability by plan:
- Pro: Eligible for top-5 depth. Request access
- Business+: Eligible for top-20 Premium depth data. Request access
Market depth requires separate approval. Having an eligible subscription plan does not automatically enable access.
Connected message
{
"type": "connected",
"channel": "depth",
"symbol": null,
"plan": "enterprise",
"entitled_levels": 20,
"limits": {
"max_symbols_per_connection": 200,
"max_symbols_per_call": 20,
"wildcard_allowed": true
},
"timestamp": "2026-07-09T15:05:37.840967+00:00"
}Optional: add symbol=2222 to receive connected followed by an initial depth_snapshot.
Client actions
{"action":"snapshot","symbol":"2222","levels":5}
{"action":"subscribe","symbols":["2222","1120"],"levels":20}
{"action":"unsubscribe","symbols":["1120"]}
{"action":"ping"}
{"action":"subscribe","symbols":["*"],"levels":20}The last command, subscribing to all symbols, is available on the Enterprise plan only.
Use symbols for subscribe/unsubscribe. For single-symbol requests, the server also accepts symbol.
Depth message ordering
- One depth_snapshot for each symbol, then subscribed.
- Later updates use the same depth_snapshot message.
Subscribed message
{
"type": "subscribed",
"symbols": ["1120", "2222"],
"total": 2,
"limit": 200
}Depth snapshot message
{
"type": "depth_snapshot",
"symbol": "2222",
"available": true,
"updated_at": "2026-07-09T15:05:37.840967+00:00",
"session": "postmarket",
"book_state": "normal",
"levels": 20,
"best_bid": 26.68,
"best_ask": 26.72,
"spread": 0.04,
"spread_bps": 14.99,
"total_bid_quantity_top5": 29085,
"total_ask_quantity_top5": 504644,
"total_bid_quantity": 29085,
"total_ask_quantity": 504644,
"level_imbalance": -0.891,
"level_imbalance_top5": -0.891,
"bids": [
{
"level": 0,
"price": 26.68,
"quantity": 79,
"order_count": 10
}
],
"asks": [
{
"level": 0,
"price": 26.72,
"quantity": 41188,
"order_count": 38
}
],
"market_order": {
"bid_quantity": 79938,
"bid_order_count": 22,
"bid_updated_at": "2026-07-09T15:05:30+00:00",
"ask_quantity": 87743,
"ask_order_count": 11,
"ask_updated_at": "2026-07-09T15:05:15+00:00"
},
"entitled_levels": 20,
"timestamp": "2026-07-09T15:05:37.872738+00:00"
}The bids and asks arrays in this example are shortened for readability. When levels is 20, a complete response can include up to 20 priced levels on each side.
Market orders
- market_order uses the same object as REST depth.
- It is null when neither side has a positive quantity.
- Price 0 is not a priced level and is not included in the best bid, best ask, spread, or quantity totals.
- A book that contains only market orders is still delivered.
Related Tutorials
Real-time WebSocket - Errors
Handle WebSocket errors using a consistent error message format. Use the connection close code to identify the error category, and the latest error.code and error.details to determine the appropriate action.
WebSocket Error Codes
WS_INTERNAL_ERROR—4000An internal connection failure interrupted setup.
Action: Retry with capped exponential backoff and jitter.
WS_AUTH_OR_ACCESS—4401 / 4403MARKET_DATA_ENTITLEMENT_*
Account access or entitlement issue blocks streaming access.
Action: 4401 means fix API key auth, 4403 means fix account/plan entitlement state. Do not auto-reconnect.
WS_RATE_LIMIT—4429DEPTH_WS_*_LIMIT / *_RATE_LIMITED
Connection, subscription, or action throttles were exceeded.
Action: Retry with backoff and jitter, and honor retry_after_seconds from the latest error details when it is present.
WS_INVALID_REQUESTINVALID_JSON / UNKNOWN_ACTION / SYMBOL_REQUIRED and lowercase variants
Malformed payload or unsupported action.
Action: Codes are channel-specific and case-sensitive. Fix the payload and retry the action; the socket normally remains open.
Multi-connection guardrails
- Open sockets with a 200-400ms stagger instead of starting all at once.
- Queue reconnect attempts globally per API key.
- Avoid synchronized reconnect storms around market open.
Off-hours connections
You may keep WebSocket connections open outside market hours. SAHMK does not intentionally disconnect clients at market close. Continue sending a ping every 30 seconds, reconnect with capped exponential backoff and jitter when necessary, and restore subscriptions after every reconnect. Market events may be absent while the market is inactive.
Error Envelope
{
"type": "error",
"code": "ERROR_CODE",
"message": "Human-readable message",
"details": {}
}Real-time WebSocket - Limits
Concurrent connection limits are independent per channel (Stocks, Trades, Depth). Keep these separate from plan-specific subscription limits such as max symbols per connection and max symbols per subscribe call.
A qualifying plan does not by itself activate every real-time product or authorize end-user display and redistribution. Request access from Realtime Data Products, and read the Commercial Usage Policy. Responsible API use is covered in the Developer Usage Terms.
When SAHMK data is displayed, whether historical, delayed, real-time, or otherwise, reference it as data provided by SAHMK.
Stocks channel subscription limits
| Plan | Max symbols/connection | Max symbols/call | Subscribe all (*) |
|---|---|---|---|
| Pro | 60 | 20 | No |
| Business | 120 | 40 | No |
| Enterprise | 200 | Up to 100 | Yes |
Trades channel subscription limits
| Plan | Max symbols/connection | Max symbols/call | Subscribe all (*) |
|---|---|---|---|
| Pro | 60 | 20 | No |
| Business | 120 | 40 | No |
| Enterprise | 200 | Up to 100 | Yes |
Depth channel subscription limits
| Plan | Entitled levels | Max symbols/connection | Max symbols/call | Subscribe all (*) |
|---|---|---|---|---|
| Pro | 5 | 60 | 20 | No |
| Business | 20 | 120 | 40 | No |
| Enterprise | 20 | 200 | Up to 100 | Yes |
Concurrent connection limits
| Channel | Per API key | Per account, across all keys |
|---|---|---|
| Stocks | 20 | 40 |
| Trades | 20 | 40 |
| Depth | 8 | 16 |
Alerts & Webhooks - Overview
Configure event-driven notifications using alert rules and webhook destinations.
Dashboard + API model
/api/v1/webhooks/ and /api/v1/alerts/). Both paths produce the same webhook delivery envelope.Event Webhooks
Register secure destinations and verify delivery endpoints.
Event Rules
Define symbols, thresholds, and trigger conditions.
Delivery Lifecycle
Track statuses from queued to delivered or failed.
Payload Reference
Canonical event envelope and field contract.
Signature Verification
Validate webhook signatures before processing.
Event History
Inspect delivery outcomes and retries.
Event Types & Conditions
Review supported event types and conditions_matched shapes.
Error Codes
Use code-to-action mappings for integration troubleshooting.
Alerts & Webhooks - Event Webhooks
Register and verify webhook endpoints before enabling alert rules.
POST /api/v1/webhooks/
Pro+Create a webhook destination for alerts and webhook notifications.
Endpoint
https://api.sahmk.sa/api/v1/webhooks/
Parameters
(*) Required
| Parameter | Type | Allowed |
|---|---|---|
| url * | string | HTTPS URL |
| name | string | optional label |
- Required:
url(HTTPS destination URL) - Optional:
name(friendly label) - Verification: a challenge handshake is sent on create; success requires HTTP 200.
- Secret handling:
signing_secretis returned only on create and is not included in list/get responses. Save it securely at creation time.
{
"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"
}Next step: continue with Signature Verification and Delivery Lifecycle for production readiness.
Alerts & Webhooks - Payload Reference
Webhook deliveries use one canonical envelope across all real-time event types, whether rules are created in Dashboard or via API.
Webhook Event Envelope
Pro+Canonical payload shape for webhook consumers.
{
"event_id": "9a6b8f22-2d1d-4b3f-b75f-7f9d5f101234",
"event_type": "large_move",
"symbol": "2222",
"detected_at": "2026-05-08T13:48:22+03:00",
"severity": "warning",
"title": "Large move detected",
"summary": "Price moved more than configured threshold in the selected window.",
"metrics": {
"price": 25.86,
"pct_change": 3.4,
"volume": 9803705,
"value": 252308343.0,
"avg_volume": 8243000,
"reference_value": 245000000.0,
"rolling_volume_baseline": 4100000,
"rolling_value_baseline": 120000000.0,
"baseline_source": "rolling_window",
"baseline_sample_count": 30,
"volume_baseline_sample_count": 30,
"value_baseline_sample_count": 30,
"high": 25.86,
"low": 25.60,
"change": 0.85,
"window": "15m",
"threshold": 3.0
},
"conditions_matched": {
"window": "15m",
"operator": "abs>=",
"observed_pct_change": 3.4,
"threshold_pct": 3.0,
"baseline_price": 25.01,
"current_price": 25.86
},
"correlation_id": "large_move:2222:29650608",
"version": "v1",
"destination": {
"type": "webhook",
"webhook_id": 19,
"webhook_url": "https://example.com/hooks/sahmk"
}
}Important
unusual_value_traded uses the rolling traded-value reference (value / rolling_value_baseline), not reference_value.Forward compatibility: clients should ignore unknown keys and rely on required top-level fields.
Alerts & Webhooks - Event Rules
Define market conditions and actions that trigger webhook deliveries. You can create rules in Dashboard or programmatically with this API.
Dashboard and event-type reference
You can create and manage alerts from the Developer Dashboard. For the complete event-type and condition reference, see Event Types & Conditions.
POST /api/v1/alerts/
Pro+Create an alert rule that triggers webhook deliveries when conditions match.
Endpoint
https://api.sahmk.sa/api/v1/alerts/
Parameters
(*) Required
| Parameter | Type | Allowed |
|---|---|---|
| symbol * | string | stock ticker |
| event_type * | string | price_alertlarge_moveabnormal_volumeunusual_value_traded |
| condition | string | price_aboveprice_belowpct_changepct_change_abs_gteratio_gtedepends on event type |
| value * | number | comparison value |
| webhook_id | number | webhook id |
| destination | object | JSON object for the delivery destination |
| config | object | JSON object with extra rule settings |
| once | boolean | truefalsedefault true |
Rule design guidance
- Start with narrow symbol scope and explicit thresholds.
- Use cooldown windows to reduce notification noise.
- Validate in staging before enabling production rules.
conditionis required forprice_alert; other documented event types normalize it automatically.- Ratio thresholds must be greater than 1.0. Large-move thresholds are capped at 10% for TASI and 30% for NOMU.
oncedefaults totrue, so the rule triggers once. Setting it tofalseallows another trigger when the conditions are met again, subject to any cooldown. It does not fire on every market update. The examples usefalse, which is why the response showsfire_once: false.
{
"id": 57,
"workspace_id": null,
"symbol": "2222",
"event_type": "large_move",
"target_scope_type": "single_symbol",
"target_scope_id": null,
"symbols": ["2222"],
"evaluated_symbol_rules": 1,
"condition": "pct_change_abs_gte",
"value": "3.0",
"config": {
"window": "5m",
"destination": { "type": "webhook", "webhook_id": 19 }
},
"fire_once": false,
"webhook_id": 19,
"webhook_url": "https://example.com/hooks/sahmk",
"destination": { "type": "webhook", "webhook_id": 19 },
"status": "active",
"last_triggered_at": null,
"total_triggers": 0,
"created_at": "2026-05-08T13:36:45+03:00"
}GET /api/v1/alerts/
Pro+List existing rules and optionally filter by status.
Endpoint
https://api.sahmk.sa/api/v1/alerts/?status=active
Parameters
(*) Required
| Parameter | Type | Allowed |
|---|---|---|
| status | string | activepausedall |
- Query:
status=active|paused|all.allis a filter option, not a returned status. - Status enum:
active,paused,triggered - Mapping:
oncemaps tofire_once.
{
"alerts": [
{
"id": 57,
"workspace_id": null,
"symbol": "2222",
"event_type": "large_move",
"target_scope_type": "single_symbol",
"target_scope_id": null,
"symbols": ["2222"],
"evaluated_symbol_rules": 1,
"condition": "pct_change_abs_gte",
"value": "3.0",
"config": {
"window": "5m",
"destination": { "type": "webhook", "webhook_id": 19 }
},
"status": "active",
"fire_once": false,
"webhook_id": 19,
"webhook_url": "https://example.com/hooks/sahmk",
"destination": { "type": "webhook", "webhook_id": 19 },
"last_triggered_at": null,
"total_triggers": 0,
"created_at": "2026-05-08T13:36:45+03:00"
}
]
}Alerts & Webhooks - Event Types & Conditions
Supported event types and primary condition selectors for rule configuration.
Availability
Generally available types are price_alert, large_move, abnormal_volume, and unusual_value_traded. These are available to accounts eligible for the alerts service. Additional types in the table below are available only to accounts included in the controlled rollout.
| Event type | Conditions |
|---|---|
| price_alert | price_above, price_below, pct_change |
| large_move | pct_change_abs_gte |
| abnormal_volume | ratio_gte |
| unusual_value_traded | ratio_gte |
| large_executed_trade | ratio_gte |
| trade_burst | ratio_gte |
| traded_value_surge | ratio_gte |
| top5_depth_imbalance | ratio_gte |
| spread_widening | ratio_gte |
| top5_liquidity_withdrawal | ratio_gte |
conditions_matched Contracts
The structure of conditions_matched varies by event type and contains the values and conditions that triggered the alert.
{
"price_alert": {
"operator": ">",
"observed": 26.1,
"threshold": 26.0
},
"large_move": {
"window": "15m",
"operator": "abs>=",
"observed_pct_change": 3.4,
"threshold_pct": 3.0,
"baseline_price": 25.01,
"current_price": 25.86
},
"abnormal_volume": {
"event_type": "abnormal_volume",
"operator": ">=",
"metric": "volume_ratio",
"current_value": 9803705,
"baseline_value": 4100000,
"ratio": 2.39,
"threshold": 2.0
},
"unusual_value_traded": {
"event_type": "unusual_value_traded",
"operator": ">=",
"metric": "value_ratio",
"current_value": 252308343.0,
"baseline_value": 120000000.0,
"ratio": 2.1,
"threshold": 2.0
}
}Numeric encoding is JSON number (not string). ratio and observed_pct_change are rounded to 4 decimals.
Alerts & Webhooks - Delivery Lifecycle
Monitor queued, delivered, failed, and retry statuses for operational visibility.
Delivery Lifecycle
Pro+Current lifecycle states and retry sequence for webhook delivery attempts.
Status flow: pending → retrying → delivered or dead_letter
Retry backoff: 2s, 10s, 60s between successive attempts.
Attempts: 4 total (initial attempt + 3 retries), with no jitter.
Retry trigger: all non-2xx outcomes are retried (4xx, 5xx, timeout, and connection errors) until attempts are exhausted.
Automatic deactivation: after 3 consecutive failed deliveries, the webhook is deactivated. Resolve the destination issue and re-verify it before using associated rules again.
Use event_id and correlation_id for end-to-end tracing.
No public replay endpoint is currently available for dead-letter events.
Where to manage
Alerts & Webhooks - Signature Verification
Verify each webhook request signature before processing events in production.
Use the endpoint-specific signing secret (signing_secret) returned when creating the webhook destination.
Delivery headers include X-SAHMK-Signature, X-SAHMK-Event, and X-SAHMK-Event-Id.
- Each delivery includes
X-SAHMK-Signature. - Signature generation uses HMAC-SHA256 over the canonical JSON payload. The example sorts keys and removes extra whitespace before signing.
- Reject mismatches with HTTP 401.
# Header format:
# X-SAHMK-Signature: t=<unix_ts>,v1=<hex_hmac>
timestamp, received = parse_signature_header(request.headers["X-SAHMK-Signature"])
canonical_json = json.dumps(payload, sort_keys=True, separators=(",", ":"))
signed_payload = f"{timestamp}.{canonical_json}"
computed = HMAC_SHA256(signing_secret, signed_payload)
if computed != received:
return 401
process_event(payload)Replay protection
Timestamp is included in the signature header. Receivers should check that it falls within an accepted window to reduce replay of an older webhook request. Verify the signature and timestamp before processing the event.
Alerts & Webhooks - Event History
Review recent webhook delivery results and retry attempts to monitor activity and troubleshoot delivery issues.
Dashboard Event History
Pro+Use the Developer Dashboard to review webhook delivery history.
- Filter by delivery status, event type, or workspace.
- Use
event_idandcorrelation_idto trace individual events and delivery attempts.
Open Developer Dashboard - Smart Alerts to view delivery history and retry statuses.
Alerts & Webhooks - Error Codes
Understand SAHMK API error codes and build reliable webhook consumers that safely handle retries and duplicate deliveries.
The HTTP errors below are returned by SAHMK API requests. Webhook delivery retries follow the delivery lifecycle.
Webhook delivery best practices
- Treat delivery attempts as idempotent; duplicate deliveries can occur on retry.
- Return 2xx quickly after durable persistence; process heavy work asynchronously.
- Track delivery IDs and failure reasons for support and reconciliation.
400—INVALID_PARAMInvalid payload field or unsupported query option.
Action: Fix request payload and retry.
401—AUTH_REQUIREDThe X-API-Key header is missing.
Action: Add an active API key and retry.
403—PLAN_LIMIT / FEATURE_DISABLEDPlan lacks feature access or limits were reached.
Action: Upgrade plan or adjust usage.
404—NOT_FOUNDWebhook or alert resource was not found for this account.
Action: Verify resource id and ownership context.
429—RATE_LIMITED / detail429 responses include error.code RATE_LIMITED. App quota also keeps a detail string. Honor Retry-After when present.
Action: Backoff and retry with jitter.
Error Response Format
{
"error": {
"code": "ERROR_CODE",
"message": "Human-readable message"
}
}Realtime WebSocket errors
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.
Limits may be enforced at the API key, account, and IP address. The rules for responsible use are in the Developer Usage Terms.
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 |
Daily request quota
Daily quotas are shared across the account, including Live and Test keys. Rotating or revoking a key does not create a new daily pool.
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 in Asia/Riyadh (UTC+3). Enterprise limits are contract-based and may be monthly quotas or resource-based.
Rate Limit Headers
Most successful /api/v1/* responses include these headers:
| Header | Description |
|---|---|
| X-RateLimit-Limit | The request limit reported by this header. |
| X-RateLimit-Remaining | Remaining requests according to this header's current calculation. |
| X-RateLimit-Reset | Unix timestamp for the next daily-quota reset. |
X-RateLimit-Reset is the next midnight in Asia/Riyadh. The daily quota applies to the whole account across every API key, while X-RateLimit-Remaining is currently calculated from the key used for the request. With multiple keys, the header can show more remaining requests than the account's actual remaining balance.
Some proxy servers may remove the rate-limit headers. An HTTP 429 response uses error.code RATE_LIMITED whether the limit was enforced at the edge or by an application quota or throttle. Application limit errors may also include a top-level detail field. Honor Retry-After when it is present.
Error Codes
The API uses standard HTTP status codes and returns structured JSON error responses.
HTTP Error Codes
400—INVALID_ROUTEWrong endpoint path was used. The API returns a suggested correct route.
401—AUTH_REQUIREDThe X-API-Key header is missing. The body includes error.code and detail.
403—INVALID_API_KEYAPI key format is invalid, or the key is invalid or revoked. The body includes error.code and detail.
403—PLAN_LIMITEndpoint requires a higher plan (e.g., historical data requires Starter+, or higher-tier financials combinations are requested on Starter).
404—INVALID_SYMBOLExchange symbol was not found.
404 / 409—INVALID_IDENTIFIER / AMBIGUOUS_IDENTIFIERIdentifier query did not resolve or matched multiple companies.
429—RATE_LIMITED(edge)Nginx edge burst or connection guard. The body is error.code and error.message.
429—RATE_LIMITED(application)App daily, per-minute, IP, or temporary security throttle. The body includes error.code and detail.
500—SERVER_ERRORInternal server error. Please retry or contact support.
Temporary security limits for new accounts
Some new free accounts may temporarily hit a security limit. If this happens, the API returns HTTP 429 with a detail containing Temporary security limit reached. Honor Retry-After when present and try again later.
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 ?"
}
}Responses for a missing API key (401), an invalid or revoked key (403), and a quota or rate limit (429) include error.code and error.message, and also retain the top-level detail field. For quota-related HTTP 429 responses, the top-level upgrade object is returned alongside error.
Error Response Formats
Endpoint validation and lookup errors generally use a structured error object:
{
"error": {
"code": "INVALID_SYMBOL",
"message": "Stock symbol '9999' not found."
}
}When the Nginx request-rate limit is exceeded, HTTP 429 is returned before the application processes the request. Retry-After is 1, and the same response is used on api.sahmk.sa and app.sahmk.sa:
{
"error": {
"code": "RATE_LIMITED",
"message": "Request rate limit exceeded. Please retry shortly."
}
}Errors for a missing or rejected API key, the daily quota, the per-minute limit, an IP limit, and a temporary security restriction include error.code and retain the top-level detail field. For a daily-quota HTTP 429, the upgrade object is returned alongside error.
{
"error": {
"code": "AUTH_REQUIRED",
"message": "A valid X-API-Key is required."
},
"detail": "A valid X-API-Key is required."
}Last updated on