Viewing: Overview
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 X-API-Key header.
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, run pip install -U sahmk to upgrade.
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 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 -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. See Real-time WebSocket for streaming updates.
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 | Example |
|---|---|---|
| symbol * | string | 2222 |
| identifier | string | أرامكو |
| data_mode | string | delayed |
Use symbol when identifier lookup is ambiguous.data_mode supports realtime, delayed. If omitted, plan default behavior is used.
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,
"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
},
"updated_at": "2026-08-12T12:20:00+00:00",
"is_delayed": false,
"resolved_instrument": {
"input": "أرامكو",
"symbol": "2222",
"name": "أرامكو",
"match_type": "alias_exact",
"confidence": "high"
}
}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 | Example |
|---|---|---|
| symbols * | string | 2222,1120 |
| identifiers | string | أرامكو، الراجحي |
| data_mode | string | delayed |
Use either symbols or identifiers, not both.data_mode supports realtime, delayed. If omitted, plan default behavior is used.
{
"quotes": [
{
"symbol": "2222",
"name": "شركة الزيت العربية السعودية",
"name_en": "Saudi Arabian Oil Co",
"price": 26.6,
"change": 0.0,
"change_percent": 0.0,
"high": 26.68,
"low": 26.52,
"bid": 26.6,
"ask": 26.62,
"bid_size": 9181,
"ask_size": 38155,
"volume": 6601208,
"net_liquidity": 23359649.699999988,
"updated_at": "2026-08-12T13:00:00+00: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,
"high": 64.3,
"low": 63.7,
"bid": 63.95,
"ask": 64.05,
"bid_size": 15,
"ask_size": 43300,
"volume": 3775688,
"net_liquidity": -47806410.94999999,
"updated_at": "2026-08-12T13:00:00+00: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 requested_count, resolved_count, ambiguous, and 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. See Real-time WebSocket for streaming updates.
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 | Example |
|---|---|---|
| symbol * | string | 2222 |
| limit | integer | 5 |
limit defaults to 50 (max 200). Results are newest first.
{
"symbol": "2222",
"updated_at": "2026-08-12T12:19:10+00:00",
"count": 1,
"events": [
{
"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."
}
}Market
Get market-wide data including index values, top movers, 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 | Example |
|---|---|---|
| index | string | TASI |
| data_mode | string | delayed |
Supported values: TASI, NOMU. Default is TASI.data_mode supports realtime, delayed. If omitted, plan default behavior is used.
{
"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 | Example |
|---|---|---|
| index | string | TASI |
| limit | number | 1 |
| data_mode | string | delayed |
data_mode supports realtime, delayed. If omitted, plan default behavior is used.
{
"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 | Example |
|---|---|---|
| index | string | TASI |
| limit | number | 1 |
| data_mode | string | delayed |
data_mode supports realtime, delayed. If omitted, plan default behavior is used.
{
"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 | Example |
|---|---|---|
| index | string | TASI |
| limit | number | 1 |
| data_mode | string | delayed |
data_mode supports realtime, delayed. If omitted, plan default behavior is used.
{
"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 | Example |
|---|---|---|
| index | string | TASI |
| limit | number | 1 |
| data_mode | string | delayed |
data_mode supports realtime, delayed. If omitted, plan default behavior is used.
{
"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 | Example |
|---|---|---|
| index | string | TASI |
| data_mode | string | delayed |
data_mode supports realtime, delayed. If omitted, plan default behavior is used.
{
"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. See Real-time WebSocket for streaming updates.
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 | Example |
|---|---|---|
| symbol * | string | 2222 |
| levels | integer | 5 (or any numeric value) |
Choose the depth you need with levels (typically 5 or up to 20). Returned depth may be lower based on your entitlement and current market-data availability.
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 }
],
"entitled_levels": 5
}Depth Serving Notes
- The API returns the best available snapshot at request time.
- Returned depth may be lower than requested based on entitlement and current market-data availability.
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 | Example |
|---|---|---|
| search | string | aramco |
| market | string | TASI |
| limit | number | 1 |
| offset | number | 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 | Example |
|---|---|---|
| symbol * | string | 2222 |
{
"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 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.
Endpoint
https://api.sahmk.sa/api/v1/historical/2222/?interval=1d&from=2024-01-01&to=2026-01-01&limit=1&offset=0
Parameters
(*) Required
| Parameter | Type | Example |
|---|---|---|
| symbol * | string | 2222, TASI, or NOMU |
| from | string | 2024-01-01 |
| to | string | 2026-01-01 |
| interval | string | 1d |
| limit | integer | 1 |
| offset | integer | 0 |
symbol: accepts a stock ticker or the index code TASI or NOMU. from: default is ~30 days ago. to: default is today. interval: 1d, 1w, 1m, 30m, 60m (default 1d). limit: default 500, maximum 2000. offset: default 0.
Data Available by Plan:
- Free:No historical access
- Starter:
1d,1w,1m - Pro:Starter intervals +
60mup to 90 days - Business:Starter intervals +
30mup to 6 months +60mup to 1 year - Enterprise:Business availability by default; custom retention, intervals, and delivery options are available by agreement
If the requested interval or date range exceeds your plan limits, the API returns 403 PLAN_LIMIT.
Need a practical intraday workflow? See Intraday Data with SAHMK (SDK + MCP).
{
"symbol": "2222",
"interval": "1d",
"source": "historical_eod",
"is_intraday": false,
"is_final": true,
"partial": false,
"latest_bar_at": "2024-01-01",
"from": "2024-01-01",
"to": "2026-01-01",
"limit": 1,
"offset": 0,
"total": 500,
"count": 1,
"has_more": true,
"data": [
{
"date": "2024-01-01",
"open": 33.0,
"high": 33.15,
"low": 32.9,
"close": 33.05,
"volume": 12123324,
"adjusted_close": 33.05
}
]
}Candle Row Format
30m/60mrows use an ISO timestamp indateand includeis_final,partial, andnumber_of_trades.1d/1w/1mrows use a date indate.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 | Example |
|---|---|---|
| symbol * | string | 1120 |
| type | string | all |
| period | string | quarterly |
| history | string | latest |
| metrics | string | extended |
| result | string | series |
| limit | integer | 4 (max 20) |
type: income, balance, cashflow, all. period: annual, quarterly, auto. history: 1y, 3y, 5y, 10y, max. metrics: core, extended. result: series, latest. Optional flags: limit: defaults to 4 (max 20). include_future_placeholders=1, include_partial=1.
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, 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
}
],
"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 | Example |
|---|---|---|
| symbol * | string | 1120 |
| history | string | 5y |
| period | string | quarterly |
| metrics | string | extended |
| meta | string | extended |
history: latest, 3y, 5y, 10y, max (default latest). period: annual, quarterly (default annual). metrics: core, extended (default core). meta: minimal (default) or extended.
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 | Example |
|---|---|---|
| symbols * | string | 1120,1180,1010,2222 |
| metrics | string | extended |
| meta | string | extended |
symbols is required as comma-separated values (example 1120,1180,1010). metrics: core or extended (default core). meta: minimal (default) or extended.
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
Get dividend history, upcoming distributions, and trailing 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 | Example |
|---|---|---|
| symbol * | string | 2222 |
| limit | number | 1 |
{
"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).
Endpoint
https://api.sahmk.sa/api/v1/events/?symbol=4190&importance=IMPORTANT&limit=1
Parameters
(*) Required
| Parameter | Type | Example |
|---|---|---|
| symbol | string | 4190 |
| type | string | FINANCIAL_REPORT |
| importance | string | IMPORTANT |
| limit | number | 1 |
| offset | number | 0 |
limit defaults to 20 (max 100). offset defaults to 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
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":["*"]} // Enterprise onlyQuote message
{
"type": "quote",
"symbol": "2222",
"mode": "standard",
"data": {
"price": 25.86,
"bid": 25.84,
"bid_size": 79,
"ask": 25.88,
"ask_size": 188
},
"timestamp": "2026-07-09T15:05:41.114000+00:00",
"latency_ms": 14
}Related Tutorials
Real-time WebSocket - Trades
Stream live trades through the trades channel. Explicit-symbol subscriptions receive a subscribed acknowledgement, then one trades_snapshot per newly subscribed symbol, followed by 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":["*"]} // Enterprise onlySnapshot 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": [
{
"event_time": "2026-07-28T09:31:55+00:00",
"price": 26.3,
"quantity": 40,
"value": 1052.0,
"side": "buy"
},
{
"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",
"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"
}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
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} // Enterprise onlyUse symbols for subscribe/unsubscribe. For single-symbol requests, the server also accepts symbol.
Depth message ordering
- Explicit symbols: one
depth_snapshotper symbol, thensubscribed. - Enterprise wildcard:
subscribedfirst, then the initial snapshot burst. - Ongoing depth updates are also
depth_snapshotmessages.
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
}
],
"entitled_levels": 20,
"timestamp": "2026-07-09T15:05:37.872738+00:00"
}Related Tutorials
Real-time WebSocket - Errors
Handle websocket failures with a unified error envelope. Treat close code as class and latest `error.code/details` as the action signal.
WebSocket Error Codes
WS_INTERNAL_ERROR4000An internal connection failure interrupted setup.
Action: Retry with capped exponential backoff and jitter.
WS_AUTH_OR_ACCESS4401 / 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_LIMIT4429DEPTH_WS_*_LIMIT / *_RATE_LIMITEDConnection, subscription, or action throttles were exceeded.
Action: Retry with backoff + jitter and honor `retry_after_seconds` from latest error details when present.
WS_INVALID_REQUESTINVALID_JSON / UNKNOWN_ACTION / SYMBOL_REQUIRED and lowercase variantsMalformed 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
Compare plan limits across real-time Stocks, Trades, and Depth channels. Use the connected.limits values as runtime truth.
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 |
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 |
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 | Example |
|---|---|---|
| url * | string | https://example.com/hooks/sahmk |
| name | string | Trading Production Hook |
Authentication uses X-API-Key: YOUR_API_KEY.
- 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.
{
"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"
}
}event_idevent_typesymboldetected_atseveritytitle,summary
metricsconditions_matchedcorrelation_idversiondestination
Important
unusual_value_traded uses rolling traded-value baseline (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 | Example |
|---|---|---|
| symbol * | string | 2222 |
| event_type * | string | large_move |
| condition | string | pct_change_abs_gte |
| value * | number | 3.0 |
| webhook_id | number | 19 |
| destination | object | { "type": "webhook", "webhook_id": 19 } |
| config | object | { "window": "15m" } |
| once | boolean | true (default) |
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. The examples set it tofalseexplicitly to create recurring rules, which is why their responses showfire_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 | Example |
|---|---|---|
| status | string | active |
- Query:
status=active|paused|all - 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. 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
Event-specific shape differences are primarily expressed in conditions_matched.
{
"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.
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 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 canonical payload.
- 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)Timestamp is included in the signature header. Receivers should apply a time-window check for replay protection.
Alerts & Webhooks - Event History
Query recent delivery outcomes for observability and troubleshooting.
Dashboard Event History
Pro+Use the Developer Dashboard history view to inspect delivery outcomes and retries.
- Filter by delivery status, event type, and workspace context in the dashboard.
- Use event IDs and correlation IDs to trace deliveries end to end.
- History is intended for observability and troubleshooting workflows.
Helper
Use Developer Dashboard - Smart Alerts to review delivery outcomes and retry states.
Alerts & Webhooks - Error Codes
Handle webhook delivery failures with retry-safe consumers and clear status mapping.
Operational guidance
- 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.
400INVALID_PARAMInvalid payload field or unsupported query option.
Action: Fix request payload and retry.
401AUTH_REQUIREDThe X-API-Key header is missing.
Action: Add an active API key and retry.
403PLAN_LIMIT / FEATURE_DISABLEDPlan lacks feature access or limits were reached.
Action: Upgrade plan or adjust usage.
404NOT_FOUNDWebhook or alert resource was not found for this account.
Action: Verify resource id and ownership context.
429detailBurst or quota throttle reached; 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.
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 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 X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset.
X-RateLimit-Reset is a Unix timestamp for the next Asia/Riyadh midnight. The daily quota is enforced account-wide across all keys. However, X-RateLimit-Remaining is currently calculated from the key used for the request, so with multiple keys it can be higher than the account's true remaining quota.
Edge proxies may strip headers. Use HTTP 429, the response detail, and Retry-After when present as the fallback throttle contract.
Error Codes
The API uses standard HTTP status codes and returns structured JSON error responses.
HTTP Error Codes
400INVALID_ROUTEWrong endpoint path was used. The API returns a suggested correct route.
401detailThe X-API-Key header is missing.
403detailAPI key format is invalid, or the key is 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_SYMBOLExchange symbol was not found.
404 / 409INVALID_IDENTIFIER / AMBIGUOUS_IDENTIFIERIdentifier query did not resolve or matched multiple companies.
429detailDaily, burst, IP, or temporary security throttle.
500SERVER_ERRORInternal server error. Please retry or contact support.
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 ?"
}
}Error Response Formats
Endpoint validation and lookup errors generally use a structured error object:
{
"error": {
"code": "INVALID_SYMBOL",
"message": "Stock symbol '9999' not found."
}
}Authentication and throttle failures use a top-level detail string:
{
"detail": "Request was throttled. Expected available in 60 seconds."
}Last updated on