Skip to main content

SAHMK Python SDK

Official Source

Official distribution: GitHub (sahmk-sa) and PyPI only. Do not download binaries from third-party forks.

Official Python SDK for Sahmk — Saudi market data and richer market workflows for developers.

Use one client for live Tadawul quotes, market-level insights, company/fundamental data, financials, events, and historical series.

Features

  • Real-time quotes for 350+ Tadawul stocks
  • Batch quotes for up to 50 symbols per request
  • Historical OHLCV data with date-range support (1d, 1w, 1m, 30m, 60m)
  • Market overview with index scoping (TASI/NOMU)
  • Market depth order book snapshots (entitlement-gated)
  • Live trades recent prints and real-time tape (Pro+)
  • Company directory endpoint for symbol discovery
  • Company/fundamental data (plan-dependent fields)
  • Financials, dividends, and events endpoints (by plan)
  • WebSocket streaming for quotes, depth, and trades (Pro+/entitled)

Installation

pip install sahmk

For local development:

git clone https://github.com/sahmk-sa/sahmk-python.git
cd sahmk-python
pip install -r requirements.txt

Security

  • Use environment variables for API keys (recommended: SAHMK_API_KEY).
  • Never commit API keys to source control, notebooks, or logs.
  • If a key is exposed, rotate it immediately from your Sahmk dashboard.

Quick Start

import os
from sahmk import SahmkClient

client = SahmkClient(os.environ["SAHMK_API_KEY"])

quote = client.quote("2222")
print(f"{quote['name_en']}: {quote['price']} SAR ({quote['change_percent']}%)")

market = client.market_summary(index="TASI")
print(f"TASI: {market['index_value']} ({market['index_change_percent']}%)")

# Batch quotes are Starter+ plan.
for q in client.quotes(["2222", "1120", "7010"])["quotes"]:
    print(f"{q['symbol']}: {q['price']}")

Identifier Resolution (Quotes)

quote() and quotes() accept either traditional symbols or resolvable identifiers:

  • Symbol: "2222"
  • Arabic company name: "أرامكو السعودية"
  • English company name/alias: "Aramco"

Symbol input always works. Name/alias input requires backend identifier-resolution support. For batch quotes, the SDK first tries identifiers=..., then automatically falls back to legacy symbols=... when connected to older backends.

q1 = client.quote("2222")              # classic symbol usage
q2 = client.quote("أرامكو السعودية")   # Arabic identifier
q3 = client.quote("Aramco")            # English alias

batch = client.quotes(["2222", "الراجحي", "SABIC"])
for q in batch.quotes:
    print(q.requested_identifier, "=>", q.symbol)

if batch.ambiguous:
    print("Ambiguous:", batch.ambiguous)
if batch.unknown:
    print("Unknown:", batch.unknown)

When the backend returns resolution metadata, it is exposed on typed objects:

quote = client.quote("Aramco")
print(quote.requested_identifier)      # Aramco
print(quote.resolved_symbol)           # 2222
print(quote.resolution.matched_by)     # alias (if provided by API)

Company Directory / Symbol Discovery

Use companies() as the canonical symbol-discovery path before calling quote() or company().

# Search by symbol or name
directory = client.companies(search="aram")
for row in directory["results"]:
    print(row["symbol"], row.get("name_en") or row.get("name"))
# Filter by market (TASI / NOMU, NOMUC alias is accepted)
nomu_companies = client.companies(market="NOMUC", limit=20)
print(nomu_companies["count"])
# Pagination loop with offset
offset = 0
page_size = 100

while True:
    page = client.companies(limit=page_size, offset=offset)
    for company in page["results"]:
        print(company["symbol"])

    offset += page_size
    if offset >= page["total"]:
        break

Recommended flow:

  1. Discover valid symbols with companies().
  2. Call quote(symbol) / company(symbol) with a validated symbol.

Production Reliability

  • The client retries transient failures: HTTP 429 and 5xx errors.
  • Defaults: retries=3, backoff_factor=0.5 (0.5s, 1s, 2s).
  • Invalid symbols, authentication failures, and plan-access errors are not retryable.
from sahmk import SahmkClient

client = SahmkClient("your_api_key", retries=3, backoff_factor=0.5)

Plan Behavior

Some methods are plan-gated (for example quotes, historical, financials, dividends, events). When your plan does not include an endpoint, the API returns an error response (not retried automatically).

Historical data availability is plan-based:

  • Free: no historical access
  • Starter: 1d, 1w, 1m
  • Pro: Starter intervals + 60m up to 90 days
  • Business: Starter intervals + 30m up to 6 months + 60m up to 1 year
  • Enterprise: Business defaults plus custom retention/interval/delivery by agreement

Out-of-plan interval/range requests are returned by the API as 403 PLAN_LIMIT. The SDK does not hard-block these requests client-side; it passes the API response through.

CLI Quick Start

export SAHMK_API_KEY="your_api_key"
sahmk quote 2222
sahmk market summary --index NOMU
sahmk market gainers --limit 5 --index NOMUC
sahmk historical 2222 --from 2026-01-01 --to 2026-01-28
sahmk historical 2222 --from 2026-01-01 --to 2026-01-03 --interval 60m
sahmk company 2222
sahmk financials 2222
sahmk ratios 2222 --history latest --period annual --metrics core
sahmk compare 2222,1120 --metrics extended
sahmk dividends 2222
sahmk events --symbol 2222 --limit 5
sahmk depth 2222 --levels 5
sahmk trades 2222 --limit 20
sahmk stream 2222,1120
sahmk stream-depth 2222,1120 --levels 5
sahmk stream-trades 2222,1120

You can also pass the key directly:

sahmk quote 2222 --api-key your_api_key

Typed Responses

Most methods return typed objects with IDE autocomplete while preserving dict-style access. Analytics methods (ratios, compare) return raw API dict responses to match the production contract exactly.

quote = client.quote("2222")
print(quote.price)
print(quote.liquidity.net_value)

# Backwards-compatible dict access
print(quote["price"])
print(quote.get("volume"))
print(quote.raw)

Financials & Analytics

client.financials("1120", history="3y", result="latest")
client.ratios("1120")
client.ratios("1120", history="5y", period="quarterly", metrics="extended")
client.compare(["1120", "1180", "1010"])
client.compare(["1120", "1180", "1010", "2222"], metrics="extended")

Financials responses no longer include meta. Existing financial statement sections (income_statements, balance_sheets, cash_flows) are unchanged.

Analytics meta remains minimal and includes only:

  • period
  • metrics
  • warnings

Market Index Scoping

Supported values:

  • TASI
  • NOMU
  • NOMUC alias (normalized to NOMU)
summary = client.market_summary(index="NOMUC")
print(summary.index)       # NOMU
print(summary.is_delayed)  # True/False by entitlement

API Reference

Base URL: https://api.sahmk.sa/api/v1

Endpoint Plan Description
GET /quote/{symbol}/ Free Stock quote
GET /quotes/?symbols=... Starter+ Batch quotes (up to 50)
GET /historical/{symbol}/ Starter+ Historical OHLCV data (1d, 1w, 1m, 30m, 60m, plan-limited)
GET /market/summary/ Free Market overview
GET /market/gainers/ Free Top gainers
GET /market/losers/ Free Top losers
GET /market/volume/ Free Volume leaders
GET /market/value/ Free Value leaders
GET /market/sectors/ Free Sector performance
GET /market/depth/{symbol}/ Entitled Market depth / order book (levels 1-20). Request access
GET /market/trades/{symbol}/ Pro+ Recent live trade prints (limit 1-200)
GET /companies/ Free Company directory and symbol discovery
GET /company/{symbol}/ Free+ Company info (tiered by plan)
GET /financials/{symbol}/ Starter+ Financial statements
GET /analytics/ratios/{symbol}/ Starter+ Analytics ratios for one company
GET /analytics/compare/ Starter+ Analytics comparison across companies
GET /dividends/{symbol}/ Starter+ Dividend history and yield
GET /events/ Pro+ AI-generated stock events

All endpoints require X-API-Key.

Full docs: sahmk.sa/developers/docs

Examples

Example scripts:

Market Depth

Market depth REST and WebSocket access are entitlement-gated. Request access from the developer realtime-access dashboard.

depth = client.depth("2222", levels=5)
print(depth.best_bid, depth.best_ask, depth.spread)
for level in depth.bids:
    print(level.level, level.price, level.quantity)

CLI:

sahmk depth 2222 --levels 5
sahmk stream-depth 2222,1120 --levels 5

Live Trades (Pro+)

Recent trade prints (REST) and a real-time tape (WebSocket). Same Pro+ plan bar as Best Price–style realtime — no separate trades product request.

trades = client.trades("2222", limit=20)
print(trades.count, trades.summary.trade_value)
for event in trades.events:
    print(event.event_time, event.price, event.quantity)

CLI:

sahmk trades 2222 --limit 20
sahmk stream-trades 2222,1120

WebSocket Streaming (Pro+)

import asyncio
from sahmk import SahmkClient

client = SahmkClient("your_api_key")

async def on_quote(msg):
    print(f"{msg['symbol']}: {msg['data']['price']}")

asyncio.run(client.stream(["2222", "1120"], on_quote=on_quote))

Depth streaming uses a dedicated channel:

async def on_depth(msg):
    print(f"{msg['symbol']}: {msg['best_bid']} / {msg['best_ask']}")

asyncio.run(client.stream_depth(["2222"], on_depth=on_depth, levels=5))

Trades streaming also uses a dedicated channel:

async def on_trade(msg):
    print(f"{msg['symbol']}: {msg['price']} x {msg['quantity']}")

async def on_snapshot(msg):
    print(f"snapshot {msg['symbol']}: {msg['count']} events")

asyncio.run(
    client.stream_trades(["2222"], on_trade=on_trade, on_snapshot=on_snapshot)
)

The streaming client auto-reconnects with exponential backoff + jitter and resubscribes symbols after reconnect.

Runtime behavior (verified with backend contract):

  • Authentication close code: 4401 (non-retryable)
  • Entitlement / plan / inactive / unverified close code: 4403 (non-retryable)
  • Invalid JSON / unknown action returns type="error" while socket stays open
  • Active subscriptions are per-connection; the SDK automatically resubscribes after reconnect
  • Symbol chunking uses backend connected.limits.max_symbols_per_call when available

For a production-style long-running process (logging, graceful shutdown, reconnect visibility, and automatic resubscribe behavior), see:

Changelog: CHANGELOG.md
Roadmap: ROADMAP.md

License

MIT — see LICENSE

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

sahmk-0.15.0.tar.gz (57.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

sahmk-0.15.0-py3-none-any.whl (27.1 kB view details)

Uploaded Python 3

File details

Details for the file sahmk-0.15.0.tar.gz.

File metadata

  • Download URL: sahmk-0.15.0.tar.gz
  • Upload date:
  • Size: 57.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for sahmk-0.15.0.tar.gz
Algorithm Hash digest
SHA256 06bceefd3d7495cc472d3a8a49de645acdf2724eb69472ec44378b74a61e30cc
MD5 7dc97845284e7b1b12913cbabedc58ee
BLAKE2b-256 fa81d9edcf3fac5bbcff2b8df53cd02920d084ade1bd6c3959d7f92cec1dc29d

See more details on using hashes here.

File details

Details for the file sahmk-0.15.0-py3-none-any.whl.

File metadata

  • Download URL: sahmk-0.15.0-py3-none-any.whl
  • Upload date:
  • Size: 27.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for sahmk-0.15.0-py3-none-any.whl
Algorithm Hash digest
SHA256 69748ad448c0e59e3f5b6bc1de21aee227f01cb290a5832259fe4671e9f5a903
MD5 6284eaa8767abc0ed7845493a75d1780
BLAKE2b-256 b3b26062cd5dd3548862eaee6a5c6e8356bec92c5ddf465a6fa4cdcd97eac847

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page