Developer API

Build with Nonlogs

Read live markets and order books publicly, or use a signed API key to place and manage limit orders.

Manage API keys
Trading only

External swaps require explicit permission. API keys cannot change security settings or access administrator functions; this deployment does not expose account withdrawals.

Overview

Base URL https://nonlogs.io
Format application/json
Authentication HMAC-SHA256
Standard limit 5/sec · 300/min

External-wallet swaps

Create a customer swap using an API key with External swaps permission. Use the signed-request headers and signature format documented below for every endpoint. No CAPTCHA or browser session is required. Keep API secrets on your server, never in customer-side JavaScript.

The customer sends funds to the returned deposit address. This does not spend your NonLogs balance. Prices float until execution; estimates are not guaranteed. Normal trading fees, a 0.8% convenience fee and outgoing network charges apply. Your API account receives 50% of collected customer trading fees and 50% of the convenience fee, in the same coins, automatically. No referral link is needed; unfunded swaps and untraded refunds earn nothing. Provable self-referrals are excluded.

GET/api/v1/swaps/assets

Returns creation_enabled and assets with symbols, exact networks, availability and confirmation requirements. A traded coin is not necessarily eligible for external swaps. Check availability before funding.

POST/api/v1/swaps/quote
{"from":"BTC","to":"XMR","amount":"0.001","from_network":"bitcoin","to_network":"monero"}

Returns estimated receipt, itemized fees, minimum/suggested/maximum amounts, route and queued-execution terms. This allocates no address and reserves neither a price nor liquidity. Network fields may be omitted only if the symbol has exactly one catalogued network.

POST/api/v1/swaps
{"from":"BTC","to":"XMR","amount":"0.001","from_network":"bitcoin","to_network":"monero","payout_address":"CUSTOMER_MONERO_ADDRESS","refund_address":"CUSTOMER_BITCOIN_ADDRESS","client_request_id":"8b6d7b3b-342f-4c1f-86f7-b29ef302266a"}

Both addresses are required and cannot be changed after creation. Returns id, client_request_id, deposit_address, address_tag (nullable), deposit_payment_uri, swap_url, funding_status, funding_expires_at, the estimate fields and payments. The private URL uses https://nonlogs.io/swap/status#14_CHARACTER_CODE; the code remains in the fragment. Treat the whole URL as a secret and never log it or place it in analytics.

First creation returns 201; an identical retry returns 200 with the same address, terms and link. Reusing a request ID with different inputs returns 409. After a timeout or 503, retry the same request ID with a fresh signature timestamp and nonce—never allocate another ID to recover an uncertain result.

GET/api/v1/swaps/{id}

Returns the same swap projection with current per-payment confirmations, conversion progress, fee amounts, payouts and refunds. Outgoing transfers include kind, status, amount (nullable before preparation), network_fee and tx_hash. Poll about every 10 seconds while needed. No webhooks or customer cancellation endpoint are provided.

Funding-window state is separate from payment state: an expired window does not mean an earlier deposit or payout failed. Any active swap-enabled key on your account can read and recover your API swaps after key rotation. It cannot read another account's swaps or unrelated browser swaps.

Amounts are decimal strings, not JSON numbers or exponent notation. Fees are denominated in the displayed asset; trade-fee entries each carry their own symbol. API timestamps are UTC ISO 8601. Funding is open for 24 hours. Deposits are processed individually using their actual amounts; confirmed and spendable payments may wait up to 12 hours for matching. Partial execution can produce both a destination payout and a source refund. Existing approval and network safety rules still apply.

Limits are account-wide across keys: initially 10 new sessions per minute and 50 unexpired funding sessions, plus shared address-pool/backlog caps and normal API request limits. Retries do not allocate another slot. A 429 includes Retry-After; 401 indicates authentication failure, 403 missing permission, 404 inaccessible swap, 422 invalid inputs or unavailable route, and 503 a pause or temporarily uncertain outcome. Pausing new creation does not stop status access, monitoring or existing obligations.

When creating a key through the credential interface, set external_swaps: true and provide the current password and a fresh authenticator code if enabled. Existing keys do not gain this permission automatically. All keys retain their existing trading permissions; enable ordinary withdrawal access only if you separately need it.

No key required

Public market data

GET/api/markets

Returns prices, 24-hour volume, high/low, best bid, and best ask for every active market.

{"markets":{"BTC-USDT":{"last_price":"68124.12000000","high_24h":"69400.00000000","low_24h":"67250.00000000","base_volume":"12.34000000","quote_volume":"840000.000000","lowest_ask":"68130.00000000","highest_bid":"68120.00000000","percent_change":1.24}}}
GET/api/markets/{pair}

Returns the same ticker fields for one pair, such as BTC-USDT.

GET/pair/pairs?skip=0&limit=100

Lists active pairs and their base/quote asset metadata. The maximum limit is 100.

GET/pair/pairs/{pair}

Returns metadata and supported decimal precision for one active pair.

GET/order/orderbook/{pair}?depth=20

Returns aggregated bids and asks as [price, quantity] string tuples. Depth may be 1–100.

{"pair":"BTC-USDT","timestamp":"2026-08-08T08:00:00Z","bids":[["68120.00000000","0.25000000"]],"asks":[["68130.00000000","0.10000000"]]}
Signed requests

Authentication

Send all four headers below. Timestamps must be within 30 seconds of server time and every hexadecimal nonce may be used only once.

HeaderValue
X-API-KeyYour public API key
X-TimestampUnix timestamp in seconds
X-NonceUnique hexadecimal string, 16–128 characters
X-SignatureLowercase HMAC-SHA256 hexadecimal digest

Canonical payload

METHOD
PATH
TIMESTAMP
NONCE
SORTED_QUERY
BODY

Use the uppercase method, path beginning with /, query parameters sorted by key and RFC 3986 encoded, and the exact raw JSON body. Use an empty line when query or body is absent. The signature is hex(HMAC_SHA256(api_secret, payload)).

Python signing example

import hashlib, hmac, json, secrets, time
from urllib.parse import urlencode

method = "POST"
path = "/tradeuser/orders/limit"
query = {}
body = json.dumps({
    "pair": "BTC-USDT",
    "type": "LIMIT",
    "operation": "BUY",
    "quantity": "0.00100000",
    "price": "68000.00000000"
}, separators=(",", ":"))
timestamp = str(int(time.time()))
nonce = secrets.token_hex(16)
sorted_query = urlencode(sorted(query.items()), safe="~")
payload = "\n".join([method, path, timestamp, nonce, sorted_query, body])
signature = hmac.new(API_SECRET.encode(), payload.encode(), hashlib.sha256).hexdigest()
API key required

Limit orders

Use decimal strings

Price and quantity must be JSON strings. Numeric JSON values are rejected so clients cannot silently round financial values.

POST/tradeuser/orders/limit

Places a good-till-cancelled limit order and locks the required balance atomically.

{"pair":"BTC-USDT","type":"LIMIT","operation":"BUY","quantity":"0.00100000","price":"68000.00000000","client_order_id":"00000000-0000-4000-8000-000000000001"}

client_order_id is required. Generate and persist a new UUID before the first request, then reuse that UUID for every retry of the same order.

GET/tradeuser/orders?pair=BTC-USDT&state=OPEN&skip=0&limit=50

Lists the key owner's orders. State may be OPEN, FILLED, or CANCELLED; maximum limit is 50.

DELETE/tradeuser/orders/{id}

Cancels an owned open order, releases its remaining balance hold, and returns 204 No Content.

Errors and retries

Errors use a stable machine-readable code:

{"error":{"code":"invalid_signature","message":"The request signature is invalid."}}

Respect 429 and its Retry-After header. Retry read requests after temporary 503 responses. For order placement, use a persistent client_order_id UUID before retrying an uncertain client connection.