SDKs and CLI
Python SDK
myntkaup: a client for Python 3.10 and later, synchronous or async, built on httpx and cryptography. It signs requests, retries safely and returns the API's own JSON.
Install
pip install myntkaup- Python 3.10 or later, typed (
py.typed). - Two dependencies:
httpxandcryptography. - Server-side only. A private key never belongs in a client-side app.
Configure
import os
from myntkaup import Myntkaup
# Reads MYNTKAUP_API_KEY_ID and MYNTKAUP_API_PRIVATE_KEY from the environment…
client = Myntkaup()
# …or give everything explicitly.
explicit = Myntkaup(
key_id=os.environ["MYNTKAUP_API_KEY_ID"],
private_key=os.environ["MYNTKAUP_API_PRIVATE_KEY"],
timeout=30.0,
max_retries=2,
)| Option | Default | Meaning |
|---|---|---|
key_id | MYNTKAUP_API_KEY_ID | The key id, mk_live_… or mk_test_…. |
private_key | MYNTKAUP_API_PRIVATE_KEY | The private key, mk_sk_…. |
base_url | https://public-api.myntkaup.is; staging for a mk_test_ key | Where requests go. |
timeout | 30.0 | Per attempt, in seconds. |
max_retries | 2 | Retries after the first attempt. |
transport | the default | An httpx transport, for tests or proxies. |
Methods
| Method | Endpoint |
|---|---|
client.status() | GET /v1/status (unsigned) |
client.me() | GET /v1/me |
client.revoke_key() | POST /v1/me/key/revoke |
client.claim_key_pairing(code=, public_key=, client=) | POST /v1/key-pairings/claim (unsigned) |
client.assets.list() | GET /v1/assets |
client.prices.list() | GET /v1/prices |
client.prices.get("BTC") | GET /v1/prices/{asset} |
client.balances.list() | GET /v1/balances |
client.limits.get() | GET /v1/limits |
client.quotes.create(asset=, side=, quote_amount=) | POST /v1/quotes |
client.quotes.get(id) | GET /v1/quotes/{id} |
client.trades.create(quote_id=, idempotency_key=None) | POST /v1/trades |
client.trades.get(id) | GET /v1/trades/{id} |
client.trades.list(**filters) | GET /v1/trades, one page |
client.trades.list_all(**filters) | every page, as an iterator |
client.transactions.list / get / list_all | GET /v1/transactions[/{id}] |
client.giftcards.create(asset=, amount=, recipient_name=None, idempotency_key=None) | POST /v1/giftcards |
client.giftcards.list / get / list_all | GET /v1/giftcards[/{id}] |
client.giftcards.pdf(id) | GET /v1/giftcards/{id}/pdf, as bytes |
quotes.create takes quote_amount or base_amount, exactly one. Each endpoint's reference page has a sample. List filters are keyword arguments named like the query parameters, except from, a Python keyword, which is from_: client.trades.list(asset="BTC", from_="2026-01-01T00:00:00Z").
Async
AsyncMyntkaup has the same methods, as coroutines, and list_all as an async iterator:
import asyncio
from myntkaup import AsyncMyntkaup
async def main():
client = AsyncMyntkaup()
balances = await client.balances.list()
async for trade in client.trades.list_all(asset="BTC"):
print(trade["id"], trade["status"])
print(balances["total_value_usdc"])
asyncio.run(main())Fields and amounts
- Arguments are snake_case, like the API's fields.
- Responses are returned as the API sends them, so the JSON examples in these docs apply as they are:
quote["base_amount"],trade["filled_at"]. - Amounts are decimal strings, in both directions. Use
decimal.Decimalfor arithmetic andstr()to send.
Helpers
client.buy() and client.sell() create a quote and accept it. If the trade comes back pending, they poll it for up to 30 seconds with backoff, and return the trade.
trade = client.buy(asset="BTC", quote_amount="100.00")
if trade["status"] != "filled":
print("Not filled yet:", trade["id"], trade["status"])Retries and idempotency
- Requests are retried at most
max_retriestimes, with exponential backoff and jitter, honouringRetry-After. - A
GETis retried on429,502,503,504and network errors. APOSTis retried only when it has anIdempotency-Key. 401,403,409,422and429 key_suspendedare never retried.trades.createandgiftcards.createsend a randomIdempotency-Keyunless you passidempotency_key;quotes.createsends one only if you pass it. See Idempotency.
Errors
Every non-2xx answer raises myntkaup.ApiError with status, code, message, request_id, and retry_after (seconds) plus the rate-limit headers when present. Network failures raise myntkaup.ConnectionError.
| Class | Status |
|---|---|
myntkaup.AuthenticationError | 401 |
myntkaup.PermissionError | 403 |
myntkaup.NotFoundError | 404 |
myntkaup.ConflictError | 409 |
myntkaup.ValidationError | 400, 422 |
myntkaup.RateLimitError | 429 |
myntkaup.ServiceError | 5xx |
PermissionError and ConnectionError share their names with Python built-ins, so refer to them through the module.
import myntkaup
try:
client.trades.get("tr_01JB8X2Q4M7Z3V9K5T1N6R0P2W")
except myntkaup.NotFoundError:
print("No such trade")
except myntkaup.ApiError as error:
print(error.code, error.request_id)Signing only
To use another HTTP client, the package exports its signing function. It returns the four MK-* headers:
import json
import os
import myntkaup
body = json.dumps({"asset": "BTC", "side": "buy", "quote_amount": "100.00"}, separators=(",", ":"))
headers = myntkaup.sign_request(
key_id=os.environ["MYNTKAUP_API_KEY_ID"],
private_key=os.environ["MYNTKAUP_API_PRIVATE_KEY"],
method="POST",
path_and_query="/v1/quotes",
body=body,
)Testing
Pass an httpx.MockTransport to test code that uses the client without network access:
import httpx
from myntkaup import Myntkaup
def handler(request: httpx.Request) -> httpx.Response:
return httpx.Response(200, json={"status": "ok", "read_only": False, "trading_enabled": True,
"giftcards_enabled": True, "server_time": "2026-11-03T10:15:10Z"})
client = Myntkaup(
key_id="mk_test_0123456789abcdefghij",
private_key="mk_sk_nWGxne_9WmC6hEr0kuwsxERJxWl7MmkZcDusAxyuf2A", # RFC 8032 test key, public
transport=httpx.MockTransport(handler),
)