Skip to main content

Best Practices

Use markets over multiple live calls

GET /v1/rates/markets returns live rates for all 10 pairs in a single request. If your dashboard or pricing engine needs multiple pairs, prefer this over looping through individual /v1/rates/live calls.

# Inefficient -- 10 requests
for pair in pairs:
r = requests.get(f".../v1/rates/live?pairs={pair}", headers=headers)

# Efficient -- 1 request
r = requests.get(".../v1/rates/markets", headers=headers)

If you only need a subset of pairs, batch them in one call:

# 3 pairs, 1 request
curl ".../v1/rates/live?pairs=USDTNGN,BTCNGN,ETHZAR" -H "X-API-Key: your_key"

Don't poll faster than the collection interval

Data is collected at 5-minute intervals. Polling /v1/rates/live faster than once every 5 minutes returns the same data repeatedly and burns through your quota. A 60-second poll interval is a reasonable default for most use cases.

Use the freshness_seconds field in the response to confirm when the data last changed.

Use cursor pagination for large historical pulls

For date ranges that exceed a single page, use the cursor returned in next_cursor rather than splitting the range manually into smaller date chunks. Cursor pagination is more efficient and avoids edge cases around interval boundaries.

candles = []
cursor = None

while True:
params = {"pair": "USDTNGN", "start": "2025-01-01", "end": "2026-01-01", "interval": "1h"}
if cursor:
params["cursor"] = cursor

data = requests.get(".../v1/rates/historical", params=params, headers=headers).json()
candles.extend(data["candles"])

if not data["has_more"]:
break
cursor = data["next_cursor"]

Cache rates client-side

If multiple services in your stack need the same rate at the same time, fetch once and fan out internally rather than each service calling the API independently. A simple in-memory cache with a 60-second TTL is sufficient given the 5-minute collection interval.

Monitor /v1/status for collection health

For production workloads, poll /v1/status every 60 seconds. Alert when any collector status is not running or any pair freshness exceeds 600 seconds. This gives you early warning before stale data causes downstream issues.

status = requests.get(".../v1/status", headers=headers).json()

for pair in status["pairs"]:
if pair["freshness_seconds"] > 600:
alert(f"{pair['pair']} is stale: {pair['freshness_seconds']}s")

for collector in status["collectors"]:
if collector["status"] != "running":
alert(f"{collector['exchange']} collector is {collector['status']}")

Use EOD rates for accounting and NAV calculations

For daily closing prices, use GET /v1/rates/eod rather than querying historical data and picking the last candle. The EOD endpoint returns a single canonical OHLCV candle with the close fixed at 23:59:59 UTC, which is consistent across calls and suitable for audit trails.

Store API keys in environment variables

Never embed API keys in source code or client-side bundles. Use environment variables or a secrets manager.

export MOXIE_API_KEY=your_key

Use separate keys for development, staging, and production so you can rotate one without affecting others.