SDKs and CLI

Node.js SDK

@myntkaup/api: a typed client for Node.js 18 and later, with no runtime dependencies. It signs requests, retries safely and returns the API's own JSON.

Install

Shell
npm install @myntkaup/api
  • ESM, with TypeScript types included.
  • No runtime dependencies: signing uses node:crypto and requests use the global fetch.
  • Server-side only. A private key never belongs in a browser.

Configure

TypeScript
import { Myntkaup } from "@myntkaup/api";

// Reads MYNTKAUP_API_KEY_ID and MYNTKAUP_API_PRIVATE_KEY from the environment…
const client = new Myntkaup();

// …or give everything explicitly.
const explicit = new Myntkaup({
  keyId: process.env.MYNTKAUP_API_KEY_ID,
  privateKey: process.env.MYNTKAUP_API_PRIVATE_KEY,
  timeoutMs: 30_000,
  maxRetries: 2,
});
OptionDefaultMeaning
keyIdMYNTKAUP_API_KEY_IDThe key id, mk_live_… or mk_test_….
privateKeyMYNTKAUP_API_PRIVATE_KEYThe private key, mk_sk_….
baseUrlhttps://public-api.myntkaup.is; staging for a mk_test_ keyWhere requests go.
timeoutMs30000Per attempt, in milliseconds.
maxRetries2Retries after the first attempt.
fetchthe global fetchYour own fetch, for tests or proxies.

Methods

MethodEndpoint
client.status()GET /v1/status (unsigned)
client.me()GET /v1/me
client.revokeKey()POST /v1/me/key/revoke
client.claimKeyPairing({ code, publicKey, 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, quoteAmount })POST /v1/quotes
client.quotes.get(id)GET /v1/quotes/{id}
client.trades.create({ quoteId, idempotencyKey? })POST /v1/trades
client.trades.get(id)GET /v1/trades/{id}
client.trades.list(params?)GET /v1/trades, one page
client.trades.listAll(params?)every page, as an async iterator
client.transactions.list / get / listAllGET /v1/transactions[/{id}]
client.giftcards.create({ asset, amount, recipientName?, idempotencyKey? })POST /v1/giftcards
client.giftcards.list / get / listAllGET /v1/giftcards[/{id}]
client.giftcards.pdf(id)GET /v1/giftcards/{id}/pdf, as a Uint8Array

quotes.create takes quoteAmount or baseAmount, exactly one. Each endpoint's reference page has a sample.

Fields and amounts

  • Request fields are camelCase and sent as the API's snake_case: quoteAmount becomes quote_amount.
  • Responses are returned as the API sends them, in snake_case, so the JSON examples in these docs apply as they are: quote.base_amount, trade.filled_at.
  • Amounts are decimal strings, in both directions. Never pass numbers.

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.

TypeScript
const trade = await client.buy({ asset: "BTC", quoteAmount: "100.00" });
if (trade.status !== "filled") console.warn("Not filled yet:", trade.id, trade.status);

Retries and idempotency

  • Requests are retried at most maxRetries times, with exponential backoff and jitter, honouring Retry-After.
  • A GET is retried on 429, 502, 503, 504 and network errors. A POST is retried only when it has an Idempotency-Key.
  • 401, 403, 409, 422 and 429 key_suspended are never retried.
  • trades.create and giftcards.create send a random Idempotency-Key unless you pass idempotencyKey; quotes.create sends one only if you pass it. See Idempotency.

Errors

Every non-2xx answer throws an ApiError with status, code, message, requestId, and retryAfter (seconds) plus the rate-limit headers when present. Network failures throw ConnectionError.

ClassStatus
AuthenticationError401
PermissionError403
NotFoundError404
ConflictError409
ValidationError400, 422
RateLimitError429
ServiceError5xx
TypeScript
import { ApiError, NotFoundError } from "@myntkaup/api";

try {
  await client.trades.get("tr_01JB8X2Q4M7Z3V9K5T1N6R0P2W");
} catch (error) {
  if (error instanceof NotFoundError) console.log("No such trade");
  else if (error instanceof ApiError) console.error(error.code, error.requestId);
  else throw error;
}

Signing only

To use another HTTP client, the package exports its signing function. It returns the four MK-* headers:

TypeScript
import { signRequest } from "@myntkaup/api";

const body = JSON.stringify({ asset: "BTC", side: "buy", quote_amount: "100.00" });
const headers = signRequest({
  keyId: process.env.MYNTKAUP_API_KEY_ID!,
  privateKey: process.env.MYNTKAUP_API_PRIVATE_KEY!,
  method: "POST",
  pathAndQuery: "/v1/quotes",
  body,
});

Testing

Pass your own fetch to test code that uses the client without network access:

TypeScript
const client = new Myntkaup({
  keyId: "mk_test_0123456789abcdefghij",
  privateKey: "mk_sk_nWGxne_9WmC6hEr0kuwsxERJxWl7MmkZcDusAxyuf2A", // RFC 8032 test key, public
  fetch: async () =>
    Response.json({
      status: "ok",
      read_only: false,
      trading_enabled: true,
      giftcards_enabled: true,
      server_time: "2026-11-03T10:15:10Z",
    }),
});