shamcash
Python client for the ShamCash API, with sync and async clients for account linking, subscriptions, balances, and incoming transactions.
pip install shamcash
Python 3.8+ is supported. Money is returned as Decimal; timestamps are timezone-aware UTC datetime objects. Naive provider timestamps are interpreted in Asia/Damascus before conversion.
Read an account
from shamcash import ShamCashAPISync
with ShamCashAPISync(api_token="...") as client:
accounts = client.list_accounts()
account = client.get_account("account_id")
balances = client.get_balances(account.id)
transactions = client.list_transactions(account.id, limit=20)
When the token has exactly one linked account, account_id can be omitted from balance and transaction requests.
transactions = client.list_transactions(
account_id="account_id",
start_at="2026-01-01T00:00:00",
end_at="2026-01-31T23:59:59",
transaction_ids=[184627893, 184627894],
coin_id=1,
limit=50,
)
for tx in transactions.transactions:
print(tx.transaction_id, tx.amount, tx.currency.code, tx.occurred_at)
transaction = client.get_transaction("account_id", 184627893)
# With one linked account:
transaction = client.get_transaction(184627893)
get_transaction() returns None when the transaction is absent.
Link accounts from your application
Enable account management for the API token in the platform before creating sessions or changing accounts and subscriptions. Reading accounts, balances, transactions, and plans does not require this permission. A token without it receives APIManagementDisabledError.
from pathlib import Path
from shamcash import ShamCashAPISync
with ShamCashAPISync(api_token="...") as client:
session = client.create_link_session()
Path("link.png").write_bytes(client.get_link_qr(session.link_session_id))
# Display link.png for the user to scan in ShamCash.
linked = client.wait_for_link(session.link_session_id)
account = linked.account
print(account.id, account.name)
Creation returns qr_payload, the exact qr_text, and a qr_image PNG data URI. get_link_qr() downloads PNG bytes and needs no optional image dependencies. These are login credentials for the pending session; keep them private and avoid logging them.
For your own polling loop, call get_link_session(session_id). A pending result includes remaining_seconds, approval_received, and poll_after_seconds. Keep one poll in flight per session and respect the returned interval. wait_for_link() handles this pacing, returns a successful LinkSession, and raises LinkExpiredError or LinkFailedError for terminal failures. Its default local deadline is 780 seconds, allowing approval received before QR expiry to finish during the server's completion window. A local or transport timeout raises RequestTimeoutError; it does not establish that the session expired.
Re-link an existing account with create_link_session(target_account_id="account_id"). This replaces its provider session while preserving its platform account ID and subscription. It must be approved by the same ShamCash identity.
Plans and prepaid subscriptions
list_plans() returns each plan's duration_days and USD price for the token owner. price_usd includes the owner's discount; base_price_usd and discount_percent describe that discount. Prices are checked again when a purchase is made.
plans = client.list_plans()
plan = plans[0]
print(plan.duration_days, plan.price_usd, plan.discount_percent)
receipt = client.subscribe(
"account_id",
plan_id=plan.id,
periods=3,
idempotency_key="checkout-order-42",
)
print(receipt.charged_usd, receipt.subscription_expires_at)
The same method starts or renews paid access. periods accepts 1–120 plan periods and charges the platform USD wallet once. A 30-day plan purchased for three periods adds 90 days after existing access, or from now if it has expired. plan_id=None selects the server's default paid plan.
Choose a stable key for each intentional purchase. After a timeout or uncertain response, repeat the same account, plan, periods, and key to retrieve the original receipt without another charge. Use a new key for another purchase. Changed parameters with an existing key raise IdempotencyKeyReusedError. The client never retries a charge automatically or creates purchase keys for you.
Receipts include subscription_id, the account snapshot, purchased duration, unit and total prices, wallet balance, expiry, remaining_seconds, and auto-renew status. A replay returns the original snapshot, including its original discount and remaining time.
settings = client.set_auto_renew("account_id", False)
settings = client.set_auto_renew("account_id", True)
trial_account = client.start_trial("account_id")
deleted_id = client.delete_account("account_id")
Auto-renew is set explicitly and leaves prepaid expiry unchanged. Deleting removes the platform link and its subscription; it does not delete the ShamCash wallet or refund unused time. A free trial can be activated once per eligible account.
Async client
ShamCashAPI has the same methods and arguments as ShamCashAPISync:
import asyncio
from shamcash import ShamCashAPI
async def main():
async with ShamCashAPI(api_token="...") as client:
session = await client.create_link_session()
png = await client.get_link_qr(session.link_session_id)
# Display png to the user before waiting for approval.
linked = await client.wait_for_link(session.link_session_id)
print(linked.account_id)
asyncio.run(main())
Both clients accept an injected httpx client; the caller remains responsible for closing it. The async client's existing session= alias is also supported. Polling uses a per-request timeout of up to 120 seconds; ordinary requests use the configured HTTP client timeout, 30 seconds by default.
Methods and results
| Method | Result |
|---|---|
list_accounts() |
list[Account] |
get_account(account_id) |
Account |
get_balances(account_id=None) |
BalancesResult |
list_transactions(account_id=None, ...) |
TransactionsResult |
get_transaction(account_id, transaction_id) |
IncomingTransaction or None |
create_link_session(target_account_id=None) |
LinkSession with login QR data |
get_link_session(session_id, timeout=120) |
LinkSession |
get_link_qr(session_id) |
PNG bytes |
wait_for_link(session_id, timeout=780, poll_interval=3) |
Successful LinkSession |
list_plans() |
list[Plan] |
subscribe(account_id, idempotency_key=..., plan_id=None, periods=1) |
SubscriptionReceipt |
set_auto_renew(account_id, enabled) |
SubscriptionSettings |
start_trial(account_id) |
Account |
delete_account(account_id) |
Deleted account ID |
fetch_json_envelope(method, path, params=None, json=None, headers=None) |
Original JSON envelope, including API errors |
Optional method arguments shown after the resource ID are keyword-only, except enabled.
Errors
All SDK exceptions inherit from ShamCashAPIError and expose code, message, data, http_status, and retry_after. Unknown API codes retain these fields in the base exception. RateLimitExceededError.retry_after contains the server's delay in seconds when supplied.
| API code | Exception |
|---|---|
API_MANAGEMENT_DISABLED |
APIManagementDisabledError |
LINK_EXPIRED |
LinkExpiredError |
LINK_FAILED |
LinkFailedError |
LINK_QR_UNAVAILABLE |
LinkQRUnavailableError |
PLAN_NOT_FOUND |
PlanNotFoundError |
INSUFFICIENT_BALANCE |
InsufficientBalanceError |
TRIAL_ALREADY_USED |
TrialAlreadyUsedError |
SUBSCRIPTION_ALREADY_ACTIVE |
SubscriptionAlreadyActiveError |
IDEMPOTENCY_KEY_REQUIRED |
IdempotencyKeyRequiredError |
IDEMPOTENCY_KEY_REUSED |
IdempotencyKeyReusedError |
SHAMCASH_SESSION_EXPIRED |
ShamCashSessionExpiredError |
UPSTREAM_UNAVAILABLE |
UpstreamUnavailableError |
Existing authentication, account, subscription, rate-limit, and fetch exceptions remain available. Transport failures raise NetworkError; timeouts raise RequestTimeoutError. Invalid JSON or response shapes raise ProtocolError. Invalid local method arguments raise ValueError before a request is sent.
The HTTP API reference lists status codes, response fields, and endpoint behavior.
Receiving-address QR generation
Account address and qr_payload describe the receiving address. To render that QR locally, install the optional extra:
pip install "shamcash[qr]"
account.create_qr_code("receiving-address.png", size=1024, caption="ShamCash payment address")
from shamcash import create_qr_code
image = create_qr_code("SHM-ADDRESS", "qr.png", caption="Payment address")
The renderer returns a PIL Image and saves it when a path is supplied. Styling options include foreground, background, caption_color, logo_path, logo_enabled, and module_border_width. SVG logos require CairoSVG; the packaged logo is included by default.
Development
pip install -e ".[dev]"
pytest -q
python -m build
python -m twine check dist/*
Tests use HTTP fixtures and do not require a platform token. CI runs the suite on Python 3.8–3.14 and checks the built distribution before publishing.
Metadata
Release files for shamcash 1.3.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| shamcash-1.3.0.tar.gz | 50.7 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| shamcash-1.3.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 87.7 kB
Release files / shamcash-1.3.0.tar.gz
| Download URL | shamcash-1.3.0.tar.gz |
|---|---|
| Size | 50.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
a2595f252e0749b0a6b42ab0fd4d74df16e3731ec7a1d7aa17dda1de8150535e
|
|
BLAKE2b-256 checksum How to use checksums |
e21dddec816a88a63aea5ada2c63b75aa8610d84573696c636767315088db8ed
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Release files / shamcash-1.3.0-py3-none-any.whl
| Download URL | shamcash-1.3.0-py3-none-any.whl |
|---|---|
| Size | 37.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
9ed8470914d0fd9ae6cfa363d91e937894f95dddcc3536e1f0f4e56826146f8a
|
|
BLAKE2b-256 checksum How to use checksums |
d417e7a72a78fda6cf4ef13c1151752a41849c9affa02d019e9e345a77878eba
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Security for shamcash 1.3.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.