Skip to main content

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

Project description

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)
  • Company directory endpoint for symbol discovery
  • Company/fundamental data (plan-dependent fields)
  • Financials, dividends, and events endpoints (by plan)
  • WebSocket streaming for real-time updates (Pro+)

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 dividends 2222
sahmk events --symbol 2222 --limit 5
sahmk stream 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://app.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 /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:

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))

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

Project details


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.11.0.tar.gz (48.7 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.11.0-py3-none-any.whl (23.1 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: sahmk-0.11.0.tar.gz
  • Upload date:
  • Size: 48.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for sahmk-0.11.0.tar.gz
Algorithm Hash digest
SHA256 a0a2918d3ec66050b4e6b2c474278da3d5317a409672423b0d414c91822101fa
MD5 b88e587eb75b7a2abc9e179641bef46c
BLAKE2b-256 75283ad1489e5f07b485d4afeb1b611d213d749238eef12d019ee975c5f603c2

See more details on using hashes here.

Provenance

The following attestation bundles were made for sahmk-0.11.0.tar.gz:

Publisher: publish-pypi.yml on sahmk-sa/sahmk-python

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

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

File metadata

  • Download URL: sahmk-0.11.0-py3-none-any.whl
  • Upload date:
  • Size: 23.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for sahmk-0.11.0-py3-none-any.whl
Algorithm Hash digest
SHA256 c78fe42e64aeeb632e92a896373f061189d0cddfe5094fa6b934c2c0590f3dd8
MD5 01fbc579c761709f2de30286ad363090
BLAKE2b-256 edf07cb278217fdbce6ed926d05380ccd75c0dc36c33e9f11a1adcae19a07e73

See more details on using hashes here.

Provenance

The following attestation bundles were made for sahmk-0.11.0-py3-none-any.whl:

Publisher: publish-pypi.yml on sahmk-sa/sahmk-python

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

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