Skip to main content
GET
https://api.daya.xyz
/
v1
/
rates
GET /v1/rates
curl --request GET \
  --url https://api.daya.xyz/v1/rates \
  --header 'Authorization: <authorization>'
{
  "rate_id": "rate_8x7k2mq9p",
  "from": "NGN",
  "to": "USDC",
  "rate": 1545.50,
  "inverse_rate": 0.000647,
  "fee_bps": 50,
  "min_deposit_ngn": 1500.00,
  "created_at": "2026-01-14T15:05:00Z",
  "expires_at": "2026-01-14T15:35:00Z"
}

Overview

Request firm FX quotes with guaranteed exchange rates. Each rate has a ~30-minute validity window and is identified by a unique rate_id.

Authentication

Authorization
string
required
Bearer token with your API key
Authorization: Bearer YOUR_API_KEY

Query Parameters

from
string
required
Source currency. Currently only NGN is supported.Allowed values: NGN
to
string
Destination currency. Defaults to USDC if omitted.Allowed values: USDC, USDT, USD
USD is treated as equivalent to USDC/USDT in v0.1. Depeg scenarios are not handled.

Request Examples

curl --request GET \
  --url 'https://api.daya.xyz/v1/rates?from=NGN&to=USDC' \
  --header 'Authorization: Bearer YOUR_API_KEY'

Response

rate_id
string
required
Unique identifier for this rate snapshot. Use this when creating onramps.Example: rate_8x7k2mq9p
from
string
required
Source currencyExample: NGN
to
string
required
Destination currencyExample: USDC
rate
number
required
Conversion rate from source to destination (e.g., 1 USDC = X NGN)Example: 1545.50
This rate already includes Daya’s spread/fee. You don’t need to calculate fees separately.
inverse_rate
number
required
Inverse conversion rate (e.g., 1 NGN = X USDC)Example: 0.000647
fee_bps
integer
Fee in basis points (1 bps = 0.01%)Example: 50 (0.5%)
min_deposit_ngn
number
required
Minimum NGN deposit amount for this rateExample: 1500.00 (~$1.00)
Deposits below this amount will be rejected with status FAILED.
created_at
string
required
When this rate was generated (ISO 8601 timestamp)Example: 2026-01-14T15:05:00Z
expires_at
string
required
When this rate becomes invalid (ISO 8601 timestamp)Example: 2026-01-14T15:35:00Z
Always check this before using rate_id to create an onramp. Expired rates will be rejected.

Success Response

{
  "rate_id": "rate_8x7k2mq9p",
  "from": "NGN",
  "to": "USDC",
  "rate": 1545.50,
  "inverse_rate": 0.000647,
  "fee_bps": 50,
  "min_deposit_ngn": 1500.00,
  "created_at": "2026-01-14T15:05:00Z",
  "expires_at": "2026-01-14T15:35:00Z"
}

Error Responses

{
  "error": {
    "code": "missing_parameter",
    "message": "Required parameter 'from' is missing",
    "details": "Query parameter 'from' must be provided"
  }
}

Rate Lifecycle

New rates are generated approximately every 10 minutes and expire after ~30 minutes.
Time    Rate ID         Valid Until
─────────────────────────────────────
15:05   rate_abc123     15:35
15:15   rate_def456     15:45
15:25   rate_ghi789     15:55
15:35   rate_jkl012     16:05
Request a fresh rate immediately before creating each onramp to ensure maximum validity window.

Usage Notes

Rate Guarantee

For throwaway onramps, the rate is guaranteed for deposits within the validity window:
  • Onramp created with rate_id at 15:10
  • Rate expires at 15:35
  • Deposit at 15:20 → Uses guaranteed rate ✅
  • Deposit at 15:40 → Flagged (expired) ❌

Caching Rates

You can cache rates client-side but must respect expires_at:
class RateCache {
  constructor() {
    this.rate = null;
  }
  
  async getValidRate() {
    if (!this.rate || new Date() >= new Date(this.rate.expires_at)) {
      const response = await fetch('https://api.daya.xyz/v1/rates?from=NGN');
      this.rate = await response.json();
    }
    return this.rate;
  }
}

Checking Time Remaining

Calculate remaining validity time:
function getSecondsRemaining(expiresAt) {
  const now = new Date();
  const expiry = new Date(expiresAt);
  return Math.max(0, (expiry - now) / 1000);
}

const rate = await getRates();
const remaining = getSecondsRemaining(rate.expires_at);
console.log(`Rate valid for ${remaining} seconds`);

Rate Calculation

The displayed rate includes Daya’s fee:
Displayed Rate = Market Rate × (1 - fee_bps / 10000)
Example:
  • Market rate: 1550 NGN/USDC
  • Fee: 50 bps (0.5%)
  • Displayed rate: 1550 × (1 - 0.005) = 1545.50 NGN/USDC

Common Patterns

Recommended flow:
  1. Call GET /v1/rates
  2. Display rate to user
  3. User confirms
  4. Call POST /v1/onramp with rate_id
async function createOnrampWithRate(userEmail, destinationAddress) {
  // Step 1: Get current rate
  const rate = await getRates();
  
  // Step 2: Show to user (await confirmation)
  await showRateToUser(rate);
  
  // Step 3: Create onramp with rate_id
  const onramp = await createOnramp({
    type: 'THROWAWAY',
    user: { email: userEmail },
    rate_id: rate.rate_id,
    settlement: {
      mode: 'AUTO_WITHDRAW',
      asset: 'USDC',
      chain: 'BASE',
      destination_address: destinationAddress
    }
  });
  
  return onramp;
}
If FX venue is down, gracefully handle rate_unavailable error:
async function getRatesWithRetry(maxRetries = 3) {
  for (let i = 0; i < maxRetries; i++) {
    try {
      const response = await fetch('https://api.daya.xyz/v1/rates?from=NGN');
      if (response.status === 503) {
        await new Promise(resolve => setTimeout(resolve, 5000)); // Wait 5s
        continue;
      }
      return await response.json();
    } catch (error) {
      if (i === maxRetries - 1) throw error;
    }
  }
  throw new Error('Rates unavailable after retries');
}

Rate Limits

  • 100 requests per minute per API key
  • No specific rate limit on this endpoint (non-mutating)

Next Steps