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, optional side)
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, event.side or "-")

event.side is optional and may be "buy", "sell", or None depending on backend availability.

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']} side={msg.get('side') or '-'}")

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

Metadata

Release files for sahmk 0.16.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for sahmk 0.16.0
File Size Uploaded
sahmk-0.16.0.tar.gz 58.2 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for sahmk 0.16.0
File Interpreter ABI Platform
sahmk-0.16.0-py3-none-any.whl Python 3 none any Details

Total release size: 85.6 kB

Release files / sahmk-0.16.0.tar.gz

Download URL sahmk-0.16.0.tar.gz
Size 58.2 kB
Tags Source
SHA-256 checksum
How to use checksums
4b9db8b93361ad1d33d95e742842da35968ea530bd6d8f6e8a8819fd2582e8de
BLAKE2b-256 checksum
How to use checksums
44acae2320e6c489a45db7544a9d8b98bd997d5a78327a5d38254eae39797cb9
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Release files / sahmk-0.16.0-py3-none-any.whl

Download URL sahmk-0.16.0-py3-none-any.whl
Size 27.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
3c4c5f9838319e9e02cc8b31251a587b1cb0e2e1985367c1d99a5465815fea6f
BLAKE2b-256 checksum
How to use checksums
a2421836a18a3cc803649a705cc4ec24c4c8006ee379dae1af401095780028ff
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Release history Release notifications | RSS feed

This release

0.16.0 This release

2 release files

0.14.0

2 release files

0.13.1

2 release files

0.13.0

2 release files

0.12.1

2 release files

0.11.0

2 release files

0.10.0

2 release files

0.9.3

2 release files

0.9.2

2 release files

0.9.1

2 release files

0.9.0

2 release files

0.8.0

2 release files

0.7.0

2 release files

0.6.2

2 release files

0.6.1

2 release files

0.6.0

2 release files

0.5.0

2 release files

0.4.0

2 release files

0.3.0

2 release files

0.2.1

2 release files

0.2.0

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page