# API Root Source: https://docs.exchangeratesapi.com.au/api-reference/api-root /openapi.json get / Returns basic API information and documentation link # API Status Source: https://docs.exchangeratesapi.com.au/api-reference/api-status /openapi.json get /status Returns operational status of the API including last update time and data freshness # Currency Conversion (/convert) Source: https://docs.exchangeratesapi.com.au/api-reference/authenticated/convert-authenticated GET /convert Convert amounts between currencies, with optional historical date support (requires authentication) ## Overview The conversion endpoint converts an amount between any two supported currencies using AUD as the intermediary. It requires authentication and is subject to your plan's monthly quota. Pass an optional `date` parameter to convert using historical rates. This endpoint requires authentication. [Get your API key](/quickstart) to start making requests. ## Authentication Bearer token with your API key: `Bearer your_api_key_here` ## Query Parameters Source currency code. All supported currencies are available for conversion, including cross-currency pairs (e.g., USD to EUR, JPY to GBP). Examples: `AUD`, `USD`, `EUR`, `GBP`, `JPY` Target currency code (case-insensitive). Must be one of the [supported currencies](/api-reference/public/symbols). Examples: `USD`, `EUR`, `GBP`, `JPY` TWI (Trade-Weighted Index) is not allowed for conversion and will return a 400 error. Amount to convert. Must be a positive number. Examples: `100`, `1500.50`, `0.01` Historical date for conversion in YYYY-MM-DD format. If omitted, uses latest available rates. Examples: `2025-08-31`, `2024-12-31`, `2023-06-15` Historical dates: Free plan has no historical access. Starter plan has 30 days access. Professional/Business plans have full historical access. ## Request ```bash cURL - Latest Rates (AUD to USD) theme={null} curl "https://api.exchangeratesapi.com.au/convert?from=AUD&to=USD&amount=100" \ -H "Authorization: Bearer your_api_key_here" ``` ```bash cURL - Cross Currency (USD to EUR) theme={null} curl "https://api.exchangeratesapi.com.au/convert?from=USD&to=EUR&amount=100" \ -H "Authorization: Bearer your_api_key_here" ``` ```bash cURL - Historical Conversion theme={null} curl "https://api.exchangeratesapi.com.au/convert?from=EUR&to=GBP&amount=100&date=2025-08-31" \ -H "Authorization: Bearer your_api_key_here" ``` ```javascript JavaScript theme={null} // Latest conversion const params = new URLSearchParams({ from: 'AUD', to: 'USD', amount: '100' }); const response = await fetch(`https://api.exchangeratesapi.com.au/convert?${params}`, { headers: { 'Authorization': 'Bearer your_api_key_here' } }); // Historical conversion const historicalParams = new URLSearchParams({ from: 'AUD', to: 'USD', amount: '100', date: '2025-08-31' }); const historicalResponse = await fetch(`https://api.exchangeratesapi.com.au/convert?${historicalParams}`, { headers: { 'Authorization': 'Bearer your_api_key_here' } }); ``` ```python Python theme={null} import requests # Latest conversion url = "https://api.exchangeratesapi.com.au/convert" headers = { "Authorization": "Bearer your_api_key_here" } params = { "from": "AUD", "to": "USD", "amount": 100 } response = requests.get(url, headers=headers, params=params) data = response.json() # Historical conversion historical_params = { "from": "AUD", "to": "USD", "amount": 100, "date": "2025-08-31" } historical_response = requests.get(url, headers=headers, params=historical_params) historical_data = historical_response.json() ``` ```php PHP theme={null} 'AUD', 'to' => 'USD', 'amount' => 100 ]); $curl = curl_init(); curl_setopt_array($curl, array( CURLOPT_URL => "https://api.exchangeratesapi.com.au/convert?{$params}", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => array( 'Authorization: Bearer your_api_key_here' ), )); $response = curl_exec($curl); curl_close($curl); // Historical conversion $historical_params = http_build_query([ 'from' => 'AUD', 'to' => 'USD', 'amount' => 100, 'date' => '2025-08-31' ]); $historical_curl = curl_init(); curl_setopt_array($historical_curl, array( CURLOPT_URL => "https://api.exchangeratesapi.com.au/convert?{$historical_params}", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => array( 'Authorization: Bearer your_api_key_here' ), )); $historical_response = curl_exec($historical_curl); curl_close($historical_curl); ?> ``` ## Response ```json 200 OK - Latest Conversion theme={null} { "success": true, "query": { "from": "AUD", "to": "USD", "amount": 100 }, "info": { "timestamp": 1725148800, "rate": 0.643512 }, "date": "2025-09-01", "result": 64.3512 } ``` ```json 200 OK - Historical Conversion theme={null} { "success": true, "query": { "from": "AUD", "to": "USD", "amount": 100 }, "info": { "timestamp": 1725062400, "rate": 0.643512 }, "date": "2025-08-31", "result": 64.3512 } ``` ### Response Fields Indicates if the request was successful Echo of the conversion parameters from the request Source currency code from request Target currency code from request Amount to convert from request Information about the exchange rate used Unix timestamp for the rate date (midnight UTC) Exchange rate used for conversion (AUD per unit of foreign currency) with up to 6 decimal places Date of the exchange rate used in YYYY-MM-DD format Conversion result with 4 decimal places ## Response Headers * `Cache-Control: no-store` - Response should not be cached * `X-Request-Id: ` - Unique request identifier for debugging * `X-Data-Stale: true` - Present only when serving fallback data (latest rates only) * `X-RBA-Source-Date: YYYY-MM-DD` - Present only when serving fallback data (latest rates only) ## Error Responses ```json 400 Bad Request - Missing Parameters theme={null} { "success": false, "error": { "code": 400, "type": "bad_request", "info": "Missing required parameters: from, to, amount" } } ``` ```json 400 Bad Request - Invalid Amount theme={null} { "success": false, "error": { "code": 400, "type": "bad_request", "info": "Invalid amount. Must be a positive number" } } ``` ```json 400 Bad Request - Invalid Currency theme={null} { "success": false, "error": { "code": 400, "type": "bad_request", "info": "Unsupported currency 'XYZ'" } } ``` ```json 400 Bad Request - TWI Not Allowed theme={null} { "success": false, "error": { "code": 400, "type": "bad_request", "info": "TWI is not allowed for conversion" } } ``` ```json 400 Bad Request - Unsupported Currency theme={null} { "success": false, "error": { "code": 400, "type": "bad_request", "info": "Unsupported currency 'XYZ'" } } ``` ```json 400 Bad Request - Invalid Date Format theme={null} { "success": false, "error": { "code": 400, "type": "bad_request", "info": "Invalid date format. Use YYYY-MM-DD" } } ``` ```json 404 Not Found - Historical Data theme={null} { "success": false, "error": { "code": 404, "type": "not_found", "info": "No data available for date 2025-12-25" } } ``` ```json 401 Unauthorized theme={null} { "success": false, "error": { "code": 401, "type": "unauthorized", "info": "Invalid or missing API key" } } ``` ```json 503 Service Unavailable theme={null} { "success": false, "error": { "code": 503, "type": "unavailable", "info": "Rates temporarily unavailable" } } ``` ## Rate Limits This endpoint is subject to your plan's monthly request limits: | Plan | Monthly Requests | Historical Access | Rate Limiting | | ------------ | ---------------- | -------------------- | ---------------------- | | Free | 150 | No historical data | No per-endpoint limits | | Starter | 5,000 | Last 30 days | No per-endpoint limits | | Professional | 50,000 | Full history (2018+) | No per-endpoint limits | | Business | 500,000 | Full history (2018+) | No per-endpoint limits | Conversion has no additional per-endpoint rate limiting beyond your plan's monthly quota. ## Current Limitations **Current API limitations:** * **TWI restriction**: Trade-Weighted Index cannot be used for conversions * **Date format**: Historical dates must be in YYYY-MM-DD format * **Date availability**: Weekend and holiday dates (when RBA doesn't publish) will return 404 * **Rate precision**: Some currencies (IDR, VND) are provided as whole numbers by the RBA source data ## Precision & Calculation * **Rate precision**: Exchange rates provided with up to 6 decimal places (varies by currency) * **Currency-specific precision**: IDR and VND are provided as whole numbers by the Reserve Bank of Australia * **Result precision**: Conversion results rounded to 4 decimal places * **Calculation method**: `result = amount × rate` using decimal-safe arithmetic * **Storage format**: Rates stored as micro-units (rate × 1e6) for precision where applicable * **Rounding**: Only applied at response boundary, not during calculations ### Currency Precision Table | Currency | Example Rate | Decimal Places | Source | | ---------------- | ------------ | ----------------- | ------ | | USD, EUR, GBP | 0.6566 | Up to 4 | RBA | | JPY, KRW | 97.26 | Up to 2 | RBA | | IDR, VND | 10739 | 0 (whole numbers) | RBA | | Other currencies | Varies | Up to 6 | RBA | ## Use Cases * **Invoice Processing**: Convert AUD amounts to foreign currencies for international transactions * **E-commerce**: Real-time price conversion for international customers * **Financial Analysis**: Historical conversion analysis with exact dated rates * **Accounting Integration**: Automated conversion with official RBA rates * **Compliance Reporting**: ATO-compliant currency conversion using official rates * **Contract Settlements**: Apply exact historical rates to dated agreements ## Historical Conversion by Plan | Plan | Monthly Requests | Historical Dates | | ------------ | ---------------- | ---------------------- | | Free | 150 | ❌ Not available | | Starter | 5,000 | ✅ Last 30 days | | Professional | 50,000 | ✅ Full history (2018+) | | Business | 500,000 | ✅ Full history (2018+) | # Historical Rates for Date (/{date}) Source: https://docs.exchangeratesapi.com.au/api-reference/authenticated/historical-date GET /{date} Get all exchange rates for a specific historical date from the Reserve Bank of Australia ## Overview The historical date endpoint returns all available exchange rates for a specific date in the past. This is perfect when you need all currency rates for a particular day, such as for invoice calculations, reporting, or compliance purposes. This endpoint requires authentication. [Get your API key](/quickstart) to start making requests. ## Authentication Bearer token with your API key: `Bearer your_api_key_here` ## Path Parameters Date in YYYY-MM-DD format. Must be a valid date from 2018-01-01 onwards and not a weekend or Australian public holiday (when RBA doesn't publish rates). Examples: `2025-08-31`, `2024-12-31`, `2023-06-15` ## Request ```bash cURL theme={null} curl https://api.exchangeratesapi.com.au/2025-08-31 \ -H "Authorization: Bearer your_api_key_here" ``` ```javascript JavaScript theme={null} const date = '2025-08-31'; const response = await fetch(`https://api.exchangeratesapi.com.au/${date}`, { headers: { 'Authorization': 'Bearer your_api_key_here' } }); const data = await response.json(); console.log(data); ``` ```python Python theme={null} import requests date = "2025-08-31" url = f"https://api.exchangeratesapi.com.au/{date}" headers = { "Authorization": "Bearer your_api_key_here" } response = requests.get(url, headers=headers) data = response.json() print(data) ``` ```php PHP theme={null} "https://api.exchangeratesapi.com.au/{$date}", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => array( 'Authorization: Bearer your_api_key_here' ), )); $response = curl_exec($curl); curl_close($curl); $data = json_decode($response, true); print_r($data); ?> ``` ## Response ```json 200 OK theme={null} { "success": true, "timestamp": 1725062400, "base": "AUD", "date": "2025-08-31", "rates": { "USD": 0.643512, "EUR": 0.562934, "GBP": 0.487421, "JPY": 96.832100, "CNY": 4.612340, "KRW": 859.123000, "INR": 54.123400, "SGD": 0.867890, "NZD": 1.094200, "THB": 22.567800, "MYR": 2.987650, "IDR": 9876.543000, "VND": 15678.900000, "HKD": 5.012340, "PHP": 36.789000, "CAD": 0.876540, "CHF": 0.567890, "TWD": 20.456700, "TWI": 60.500000, "SDR": 0.487650 } } ``` ### Response Fields Indicates if the request was successful Unix timestamp for the specified date (midnight UTC) Base currency (always "AUD") The requested date in YYYY-MM-DD format Object containing all available currency codes and their exchange rates for the specified date Exchange rate (AUD per unit of foreign currency) with precision varying by currency (up to 6 decimal places for most, whole numbers for IDR/VND as provided by RBA) ## Response Headers * `Cache-Control: no-store` - Response should not be cached * `X-Request-Id: ` - Unique request identifier for debugging ## Error Responses ```json 400 Bad Request - Invalid Date Format theme={null} { "success": false, "error": { "code": 400, "type": "bad_request", "info": "Invalid date format. Use YYYY-MM-DD" } } ``` ```json 404 Not Found - No Data Available theme={null} { "success": false, "error": { "code": 404, "type": "not_found", "info": "No data available for date 2025-12-25. RBA data may not be available for this date. Try /latest for current rates." } } ``` ```json 404 Not Found - Date Too Early theme={null} { "success": false, "error": { "code": 404, "type": "not_found", "info": "No data available for date 2017-12-31" } } ``` ```json 401 Unauthorized theme={null} { "success": false, "error": { "code": 401, "type": "unauthorized", "info": "Invalid or missing API key" } } ``` ## Rate Limits This endpoint is subject to your plan's monthly request limits: | Plan | Monthly Requests | Historical Access | | ------------ | ---------------- | -------------------- | | Free | 150 | No historical data | | Starter | 5,000 | Last 30 days | | Professional | 50,000 | Full history (2018+) | | Business | 500,000 | Full history (2018+) | **Free Plan**: No access to historical data\ **Starter Plan**: Access to last 30 days of historical data\ **Paid Plans**: Full access to all historical data from 2018 onwards ## Historical Data Availability ### Date Range * **Available from**: January 1, 2018 * **Updated daily**: New rates published at 4:00 PM AEST by the RBA ### Missing Dates The RBA doesn't publish exchange rates on: * **Weekends**: Saturday and Sunday * **Australian Public Holidays**: New Year's Day, Australia Day, Good Friday, Easter Monday, Anzac Day, Queen's Birthday, Christmas Day, Boxing Day, and other NSW state-specific holidays Requests for these dates will return a 404 Not Found error. ### Plan-Specific Access * **Free Plan**: No historical data access * **Starter Plan**: Last 30 days of historical data * **Professional & Business Plans**: Full historical archive (2018 onwards) ## Use Cases * **Invoice Calculations**: Get official RBA rates for specific transaction dates * **Compliance Reporting**: ATO reporting with exact historical rates * **Audit Requirements**: Verify exchange rates used in past transactions * **Financial Analysis**: Compare rates from specific dates * **Accounting Integration**: Import historical rates for month-end/quarter-end processing * **Regulatory Compliance**: Meet Australian financial reporting standards ## Precision & Standards * **Precision**: Rates provided with up to 6 decimal places (varies by currency) * **Currency-specific precision**: IDR and VND are provided as whole numbers by the RBA source data * **Source**: Official Reserve Bank of Australia daily rates * **Rounding**: Rates are stored as micro-units (rate × 1e6) and converted for display where applicable * **Standards**: Complies with Australian financial reporting requirements # Historical Rate for Currency and Date (/{date}/{currency}) Source: https://docs.exchangeratesapi.com.au/api-reference/authenticated/historical-date-currency GET /{date}/{currency} Get the exchange rate for a specific currency on a specific historical date ## Overview The historical date-currency endpoint returns the exchange rate for a single currency on a specific historical date. This is the most precise endpoint when you need a specific rate for exact calculations, invoice processing, or compliance reporting. This endpoint requires authentication. [Get your API key](/quickstart) to start making requests. ## Authentication Bearer token with your API key: `Bearer your_api_key_here` ## Path Parameters Date in YYYY-MM-DD format. Must be a valid date from 2018-01-01 onwards and not a weekend or Australian public holiday. Examples: `2025-08-31`, `2024-12-31`, `2023-06-15` Three-letter currency code (case-insensitive). Must be one of the [supported currencies](/api-reference/public/symbols). Examples: `USD`, `EUR`, `GBP`, `JPY`, `NZD` ## Request ```bash cURL theme={null} curl https://api.exchangeratesapi.com.au/2025-08-31/USD \ -H "Authorization: Bearer your_api_key_here" ``` ```javascript JavaScript theme={null} const date = '2025-08-31'; const currency = 'USD'; const response = await fetch(`https://api.exchangeratesapi.com.au/${date}/${currency}`, { headers: { 'Authorization': 'Bearer your_api_key_here' } }); const data = await response.json(); console.log(data); ``` ```python Python theme={null} import requests date = "2025-08-31" currency = "USD" url = f"https://api.exchangeratesapi.com.au/{date}/{currency}" headers = { "Authorization": "Bearer your_api_key_here" } response = requests.get(url, headers=headers) data = response.json() print(data) ``` ```php PHP theme={null} "https://api.exchangeratesapi.com.au/{$date}/{$currency}", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => array( 'Authorization: Bearer your_api_key_here' ), )); $response = curl_exec($curl); curl_close($curl); $data = json_decode($response, true); print_r($data); ?> ``` ## Response ```json 200 OK theme={null} { "success": true, "timestamp": 1725062400, "base": "AUD", "date": "2025-08-31", "rates": { "USD": 0.643512 } } ``` ### Response Fields Indicates if the request was successful Unix timestamp for the specified date (midnight UTC) Base currency (always "AUD") The requested date in YYYY-MM-DD format Object containing the requested currency code and its exchange rate Exchange rate (AUD per unit of foreign currency) with precision varying by currency (up to 6 decimal places for most, whole numbers for IDR/VND as provided by RBA) ## Response Headers * `Cache-Control: no-store` - Response should not be cached * `X-Request-Id: ` - Unique request identifier for debugging ## Error Responses ```json 400 Bad Request - Invalid Date Format theme={null} { "success": false, "error": { "code": 400, "type": "bad_request", "info": "Invalid date format. Use YYYY-MM-DD" } } ``` ```json 400 Bad Request - Invalid Currency Format theme={null} { "success": false, "error": { "code": 400, "type": "bad_request", "info": "Invalid currency format. Use 3-letter currency code (e.g., USD)" } } ``` ```json 404 Not Found - No Rate Available theme={null} { "success": false, "error": { "code": 404, "type": "not_found", "info": "No USD rate available for date 2025-08-31. RBA data may not be available for this date. Try /latest for current rates." } } ``` ```json 404 Not Found - No Data for Date theme={null} { "success": false, "error": { "code": 404, "type": "not_found", "info": "No data available for date 2025-12-25" } } ``` ```json 401 Unauthorized theme={null} { "success": false, "error": { "code": 401, "type": "unauthorized", "info": "Invalid or missing API key" } } ``` ## Rate Limits This endpoint is subject to your plan's monthly request limits: | Plan | Monthly Requests | Historical Access | | ------------ | ---------------- | -------------------- | | Free | 150 | No historical data | | Starter | 5,000 | Last 30 days | | Professional | 50,000 | Full history (2018+) | | Business | 500,000 | Full history (2018+) | **Free Plan**: No access to historical data\ **Starter Plan**: Access to last 30 days of historical data\ **Paid Plans**: Full access to all historical data from 2018 onwards ## Historical Data Availability ### Date Range * **Available from**: January 1, 2018 * **Updated daily**: New rates published at 4:00 PM AEST by the RBA ### Missing Dates The RBA doesn't publish exchange rates on: * **Weekends**: Saturday and Sunday * **Australian Public Holidays**: New Year's Day, Australia Day, Good Friday, Easter Monday, Anzac Day, Queen's Birthday, Christmas Day, Boxing Day, and other NSW state-specific holidays Requests for these dates will return a 404 Not Found error. ### Plan-Specific Access * **Free Plan**: No historical data access * **Starter Plan**: Last 30 days of historical data * **Professional & Business Plans**: Full historical archive (2018 onwards) ## Currency Availability Not all currencies may be available for every historical date. The RBA occasionally: * **Adds new currencies** to their daily publication * **Removes currencies** that are no longer actively tracked * **Skips currencies** on certain dates due to market conditions If a specific currency rate is not available for the requested date, you'll receive a 404 error with details. ## Use Cases * **Precise Invoice Calculations**: Get the exact rate for a specific transaction date and currency * **Audit Trail**: Verify specific rates used in historical transactions * **Compliance Verification**: ATO reporting with exact RBA rates * **Contract Settlements**: Apply correct rates for dated financial agreements * **Accounting Reconciliation**: Match rates to specific accounting periods * **Regulatory Documentation**: Support compliance with exact rate citations ## Best Practices * **Error Handling**: Always check for 404 responses, especially for weekend dates or holidays * **Currency Validation**: Verify currency codes against the [supported currencies](/api-reference/public/symbols) list * **Date Validation**: Ensure dates are in correct format and within available range * **Caching**: Cache responses locally as historical rates don't change * **Batch Requests**: For multiple currencies on the same date, consider using the [historical date endpoint](/api-reference/authenticated/historical-date) ## Precision & Standards * **Precision**: Rates provided with up to 6 decimal places (varies by currency) * **Currency-specific precision**: IDR and VND are provided as whole numbers by the RBA source data * **Source**: Official Reserve Bank of Australia daily rates * **Rounding**: Rates stored as micro-units (rate × 1e6) and converted for display where applicable * **Standards**: Complies with Australian financial reporting requirements # Latest Exchange Rates (/latest) Source: https://docs.exchangeratesapi.com.au/api-reference/authenticated/latest GET /latest Get the most recent exchange rates for all supported currencies from the Reserve Bank of Australia ## Overview The latest rates endpoint returns current exchange rates for all currencies supported by the Reserve Bank of Australia. Rates are updated daily at 4:00 PM AEST and cached for optimal performance. This endpoint requires authentication. [Get your API key](/quickstart) to start making requests. ## Authentication Bearer token with your API key: `Bearer your_api_key_here` ## Query Parameters Comma-separated list of currency codes to filter results (case-insensitive). If omitted, all available currencies are returned. Examples: `USD,EUR,GBP`, `JPY,CAD,NZD` Base currency for the rates. Currently only AUD is supported. Default: `AUD` ## Request ```bash cURL - All Currencies theme={null} curl https://api.exchangeratesapi.com.au/latest \ -H "Authorization: Bearer your_api_key_here" ``` ```bash cURL - Specific Currencies theme={null} curl "https://api.exchangeratesapi.com.au/latest?symbols=USD,EUR,GBP" \ -H "Authorization: Bearer your_api_key_here" ``` ```javascript JavaScript - All Currencies theme={null} const response = await fetch('https://api.exchangeratesapi.com.au/latest', { headers: { 'Authorization': 'Bearer your_api_key_here' } }); const data = await response.json(); if (data.success) { console.log('Current USD rate:', data.rates.USD); console.log('Data from:', data.date); console.log('All rates:', data.rates); } else { console.error('API Error:', data.error); } ``` ```javascript JavaScript - Filtered Currencies theme={null} const symbols = 'USD,EUR,GBP'; const response = await fetch(`https://api.exchangeratesapi.com.au/latest?symbols=${symbols}`, { headers: { 'Authorization': 'Bearer your_api_key_here' } }); const data = await response.json(); if (data.success) { console.log('Filtered rates:', data.rates); console.log('Number of currencies:', Object.keys(data.rates).length); } else { console.error('API Error:', data.error); } ``` ```python Python theme={null} import requests headers = { 'Authorization': 'Bearer your_api_key_here' } response = requests.get( 'https://api.exchangeratesapi.com.au/latest', headers=headers ) data = response.json() if data['success']: print(f"Current USD rate: {data['rates']['USD']}") print(f"Data from: {data['date']}") print(f"Total currencies: {len(data['rates'])}") else: print(f"API Error: {data['error']['info']}") ``` ```php PHP theme={null} 'https://api.exchangeratesapi.com.au/latest', CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ 'Authorization: Bearer ' . $api_key ], ]); $response = curl_exec($curl); curl_close($curl); $data = json_decode($response, true); if ($data['success']) { echo "Current USD rate: " . $data['rates']['USD'] . "\n"; echo "Data from: " . $data['date'] . "\n"; } else { echo "API Error: " . $data['error']['info'] . "\n"; } ?> ``` ## Response ```json Success Response theme={null} { "success": true, "timestamp": 1725080400, "base": "AUD", "date": "2025-08-31", "rates": { "USD": 0.643512, "EUR": 0.562934, "GBP": 0.487421, "JPY": 96.832100, "NZD": 1.094200, "CAD": 0.893421, "CHF": 0.578234, "CNY": 4.623451, "KRW": 854.721000, "SGD": 0.856789, "INR": 54.123456, "THB": 22.567890, "MYR": 2.987654, "IDR": 9876.543210, "VND": 15432.109876, "HKD": 5.012345, "PHP": 36.789012, "TWD": 20.456789, "TWI": 60.500000, "SDR": 0.476543 } } ``` Always `true` for successful responses Unix timestamp when the rates were published by RBA Base currency for all rates (always "AUD") Date when the rates were published (YYYY-MM-DD format) Object containing exchange rates keyed by currency code Exchange rate from AUD to the specified currency, with precision varying by currency (up to 6 decimal places for most, whole numbers for IDR/VND) ## Understanding Exchange Rates ### Rate Interpretation All rates are quoted as **AUD to foreign currency**: * `"USD": 0.643512` means 1 AUD = 0.643512 USD * `"EUR": 0.562934` means 1 AUD = 0.562934 EUR * `"JPY": 96.832100` means 1 AUD = 96.832100 JPY ### Precision * Rates are stored with micro-unit precision internally * API responses show up to 6 decimal places (varies by currency) * Actual precision varies by currency based on RBA source data: * Most currencies: Up to 6 decimal places (e.g., USD: 0.6566) * Some currencies: 2 decimal places (e.g., JPY: 97.26) * IDR and VND: Whole numbers only (e.g., IDR: 10739, VND: 17338) as provided by RBA ### Special Currencies The TWI represents the value of the AUD against a basket of currencies weighted by trade volumes. * **Included** in latest rates * **Not available** for conversion operations * Used by economists and analysts for AUD strength analysis SDRs are international reserve assets created by the International Monetary Fund. * Basket currency including USD, EUR, CNY, JPY, GBP * Used for international accounting and reserves * Available for conversion operations ## Rate Update Schedule ### RBA Update Times * **Daily**: Monday to Friday (excludes Australian public holidays) * **Time**: 4:00 PM Australian Eastern Time (AEST/AEDT) * **Source**: Reserve Bank of Australia official rates ### API Update Process 1. **4:00 PM AEST**: RBA publishes new rates 2. **4:05-4:15 PM AEST**: Our system fetches and validates data 3. **4:15 PM AEST**: New rates available via API 4. **Fallback**: If RBA is unavailable, previous day's rates are served with stale data headers ### Staleness Detection When RBA data is unavailable, responses include: ```http theme={null} X-Data-Stale: true X-RBA-Source-Date: 2025-08-30 ``` ```json theme={null} { "success": true, "timestamp": 1725080400, "base": "AUD", "date": "2025-08-30", "rates": { ... } } ``` ## Response Headers Successful responses include useful metadata: ```http theme={null} HTTP/2 200 OK Content-Type: application/json Cache-Control: no-store X-Request-Id: 123e4567-e89b-12d3-a456-426614174000 X-RateLimit-Limit-Monthly: 5000 X-RateLimit-Remaining-Monthly: 4847 X-RateLimit-Reset: 2025-10-01T00:00:00Z X-Response-Time: 45ms ``` Authenticated endpoints return `Cache-Control: no-store` to prevent caching of user-specific responses and ensure quota tracking accuracy. ## Common Use Cases ### 1. **Real-time Rate Display** ```javascript theme={null} async function updateRateDisplay() { try { const response = await fetch('https://api.exchangeratesapi.com.au/latest', { headers: { 'Authorization': 'Bearer your_api_key_here' } }); const data = await response.json(); if (data.success) { // Update UI elements document.getElementById('usd-rate').textContent = data.rates.USD.toFixed(4); document.getElementById('eur-rate').textContent = data.rates.EUR.toFixed(4); document.getElementById('last-updated').textContent = new Date(data.timestamp * 1000).toLocaleString(); // Check for stale data if (data.stale) { document.getElementById('stale-warning').style.display = 'block'; } } else { console.error('Failed to fetch rates:', data.error); } } catch (error) { console.error('Network error:', error); } } // Update rates every 30 minutes setInterval(updateRateDisplay, 30 * 60 * 1000); updateRateDisplay(); // Initial load ``` ### 2. **Bulk Currency Calculations** ```python theme={null} import requests def calculate_portfolio_values(aud_amounts, api_key): """Convert AUD amounts to multiple currencies""" headers = {'Authorization': f'Bearer {api_key}'} response = requests.get('https://api.exchangeratesapi.com.au/latest', headers=headers) data = response.json() if not data['success']: raise Exception(f"API Error: {data['error']['info']}") rates = data['rates'] results = {} for currency_code, aud_amount in aud_amounts.items(): if currency_code in rates: converted_amount = aud_amount * rates[currency_code] results[currency_code] = { 'aud_amount': aud_amount, 'converted_amount': round(converted_amount, 2), 'rate': rates[currency_code], 'date': data['date'] } return results # Example usage portfolio = { 'USD': 10000, # 10k AUD to convert to USD 'EUR': 5000, # 5k AUD to convert to EUR 'JPY': 2000 # 2k AUD to convert to JPY } results = calculate_portfolio_values(portfolio, 'your_api_key_here') for currency, info in results.items(): print(f"{info['aud_amount']} AUD = {info['converted_amount']} {currency}") ``` ### 3. **Rate Comparison & Alerting** ```javascript theme={null} class RateMonitor { constructor(apiKey) { this.apiKey = apiKey; this.previousRates = {}; this.thresholds = {}; } setThreshold(currency, threshold) { this.thresholds[currency] = threshold; } async checkRates() { const response = await fetch('https://api.exchangeratesapi.com.au/latest', { headers: { 'Authorization': `Bearer ${this.apiKey}` } }); const data = await response.json(); if (!data.success) { console.error('Rate check failed:', data.error); return; } // Compare with previous rates for (const [currency, currentRate] of Object.entries(data.rates)) { const previousRate = this.previousRates[currency]; if (previousRate) { const changePercent = ((currentRate - previousRate) / previousRate) * 100; if (Math.abs(changePercent) >= (this.thresholds[currency] || 1)) { this.sendAlert(currency, previousRate, currentRate, changePercent); } } } this.previousRates = { ...data.rates }; } sendAlert(currency, oldRate, newRate, changePercent) { const direction = changePercent > 0 ? 'increased' : 'decreased'; console.log(`🚨 Alert: ${currency} ${direction} by ${Math.abs(changePercent).toFixed(2)}%`); console.log(` ${oldRate} → ${newRate}`); } } // Example usage const monitor = new RateMonitor('your_api_key_here'); monitor.setThreshold('USD', 0.5); // Alert on 0.5% change monitor.setThreshold('EUR', 0.5); // Check rates every 10 minutes during market hours setInterval(() => monitor.checkRates(), 10 * 60 * 1000); ``` ### 4. **Caching for Performance** ```php theme={null} apiKey = $apiKey; } public function getLatestRates() { // Check cache first if ($this->isCacheValid()) { return json_decode(file_get_contents($this->cacheFile), true); } // Fetch fresh data $rates = $this->fetchFromAPI(); if ($rates) { $this->saveToCache($rates); return $rates; } // Fallback to cache if API fails if (file_exists($this->cacheFile)) { return json_decode(file_get_contents($this->cacheFile), true); } throw new Exception('Unable to fetch exchange rates'); } private function fetchFromAPI() { $curl = curl_init(); curl_setopt_array($curl, [ CURLOPT_URL => 'https://api.exchangeratesapi.com.au/latest', CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 10, CURLOPT_HTTPHEADER => [ 'Authorization: Bearer ' . $this->apiKey ], ]); $response = curl_exec($curl); $httpCode = curl_getinfo($curl, CURLINFO_HTTP_CODE); curl_close($curl); if ($httpCode === 200) { $data = json_decode($response, true); return $data['success'] ? $data : null; } return null; } private function isCacheValid() { if (!file_exists($this->cacheFile)) { return false; } return (time() - filemtime($this->cacheFile)) < $this->cacheExpiry; } private function saveToCache($data) { file_put_contents($this->cacheFile, json_encode($data)); } } // Usage $cache = new ExchangeRateCache('your_api_key_here'); $rates = $cache->getLatestRates(); echo "USD Rate: " . $rates['rates']['USD'] . "\n"; echo "Data from: " . $rates['date'] . "\n"; ?> ``` ## Error Responses ```json theme={null} { "success": false, "error": { "code": 401, "type": "invalid_api_key", "info": "Invalid or missing API key." } } ``` ```json theme={null} { "success": false, "error": { "code": 429, "type": "rate_limit_exceeded", "info": "Monthly quota of 5000 requests exceeded. Quota resets on your next billing cycle." } } ``` ```json theme={null} { "success": false, "error": { "code": 503, "type": "service_unavailable", "info": "Exchange rates temporarily unavailable. Please try again later." } } ``` ## Next Steps Get rate for a specific currency only Convert amounts between currencies Access rates from any date since 2018 Get rates for date ranges # Latest Rate for Currency (/latest/{currency}) Source: https://docs.exchangeratesapi.com.au/api-reference/authenticated/latest-currency GET /latest/{currency} Get the current exchange rate for a specific currency from the Reserve Bank of Australia ## Overview The latest currency endpoint returns the current exchange rate for a single specified currency from the Reserve Bank of Australia. This is ideal when you only need the rate for one currency and want to reduce response size and processing time. This endpoint requires authentication. [Get your API key](/quickstart) to start making requests. ## Authentication Bearer token with your API key: `Bearer your_api_key_here` ## Path Parameters Three-letter currency code (case-insensitive). Must be one of the [supported currencies](/api-reference/public/symbols). Examples: `USD`, `EUR`, `GBP`, `JPY`, `NZD` ## Request ```bash cURL theme={null} curl https://api.exchangeratesapi.com.au/latest/USD \ -H "Authorization: Bearer your_api_key_here" ``` ```javascript JavaScript theme={null} const response = await fetch('https://api.exchangeratesapi.com.au/latest/USD', { headers: { 'Authorization': 'Bearer your_api_key_here' } }); const data = await response.json(); console.log(data); ``` ```python Python theme={null} import requests url = "https://api.exchangeratesapi.com.au/latest/USD" headers = { "Authorization": "Bearer your_api_key_here" } response = requests.get(url, headers=headers) data = response.json() print(data) ``` ```php PHP theme={null} 'https://api.exchangeratesapi.com.au/latest/USD', CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => array( 'Authorization: Bearer your_api_key_here' ), )); $response = curl_exec($curl); curl_close($curl); $data = json_decode($response, true); print_r($data); ?> ``` ## Response ```json 200 OK theme={null} { "success": true, "timestamp": 1725148800, "base": "AUD", "date": "2025-09-01", "rates": { "USD": 0.643512 } } ``` ### Response Fields Indicates if the request was successful Unix timestamp for the rate date (midnight UTC) Base currency (always "AUD") Date of the exchange rate in YYYY-MM-DD format Object containing the requested currency code and its exchange rate Exchange rate (AUD per unit of foreign currency) with precision varying by currency (up to 6 decimal places for most, whole numbers for IDR/VND) ## Response Headers * `Cache-Control: no-store` - Response should not be cached * `X-Request-Id: ` - Unique request identifier for debugging * `X-Data-Stale: true` - Present only when serving fallback data * `X-RBA-Source-Date: YYYY-MM-DD` - Present only when serving fallback data ## Error Responses ```json 400 Bad Request - Invalid Currency theme={null} { "success": false, "error": { "code": 400, "type": "bad_request", "info": "Unsupported currency 'XYZ'" } } ``` ```json 401 Unauthorized - Invalid API Key theme={null} { "success": false, "error": { "code": 401, "type": "unauthorized", "info": "Invalid or missing API key" } } ``` ```json 503 Service Unavailable - Rates Unavailable theme={null} { "success": false, "error": { "code": 503, "type": "unavailable", "info": "Rates temporarily unavailable" } } ``` ## Rate Limits This endpoint is subject to your plan's monthly request limits: | Plan | Monthly Requests | Historical Access | | ------------ | ---------------- | -------------------- | | Free | 150 | No historical data | | Starter | 5,000 | Last 30 days | | Professional | 50,000 | Full history (2018+) | | Business | 500,000 | Full history (2018+) | ## Data Freshness * Rates are updated daily at 4:00 PM AEST by the Reserve Bank of Australia * Our system fetches new data within 30 minutes of RBA publication * If fresh data is unavailable, we serve the most recent available rates with staleness headers ## Use Cases * **Single Currency Monitoring**: Track one specific currency without fetching all rates * **Reduced Bandwidth**: Minimize response size for mobile applications * **Focused Analytics**: Build dashboards for specific currency pairs * **Real-time Updates**: Monitor critical currency rates with lower latency # Historical Exchange Rates (/timeseries) Source: https://docs.exchangeratesapi.com.au/api-reference/authenticated/timeseries GET /timeseries Get exchange rates for a date range with optional currency filtering (maximum 365 days) ## Overview The timeseries endpoint returns historical exchange rates for a specified date range. You can optionally filter for specific currencies and limit the date range to a maximum of 365 days. This is perfect for building charts, analytics, and historical analysis. This endpoint requires authentication. [Get your API key](/quickstart) to start making requests. ## Authentication Bearer token with your API key: `Bearer your_api_key_here` ## Query Parameters Start date in YYYY-MM-DD format. Must be a valid date from 2018-01-01 onwards. **Free Plan**: No historical access\ **Starter Plan**: Limited to last 30 days\ **Paid Plans**: Full access from 2018-01-01 onwards Example: `2025-08-01` End date in YYYY-MM-DD format. Must be after start\_date. **Free Plan**: No access to timeseries (blocked)\ **Starter Plan**: Maximum 7 days range\ **Professional Plan**: Maximum 90 days range\ **Business/Enterprise Plans**: Maximum 365 days range Example: `2025-08-31` Comma-separated list of currency codes to filter results (case-insensitive). If omitted, all available currencies are returned. Example: `USD,EUR,GBP` Base currency for the rates. Currently only AUD is supported. Default: `AUD` ## Request ```bash cURL - All Currencies theme={null} curl "https://api.exchangeratesapi.com.au/timeseries?start_date=2025-08-01&end_date=2025-08-31" \ -H "Authorization: Bearer your_api_key_here" ``` ```bash cURL - Specific Currencies theme={null} curl "https://api.exchangeratesapi.com.au/timeseries?start_date=2025-08-01&end_date=2025-08-31&symbols=USD,EUR,GBP" \ -H "Authorization: Bearer your_api_key_here" ``` ```javascript JavaScript theme={null} const params = new URLSearchParams({ start_date: '2025-08-01', end_date: '2025-08-31', symbols: 'USD,EUR,GBP' }); const response = await fetch(`https://api.exchangeratesapi.com.au/timeseries?${params}`, { headers: { 'Authorization': 'Bearer your_api_key_here' } }); const data = await response.json(); console.log(data); ``` ```python Python theme={null} import requests url = "https://api.exchangeratesapi.com.au/timeseries" headers = { "Authorization": "Bearer your_api_key_here" } params = { "start_date": "2025-08-01", "end_date": "2025-08-31", "symbols": "USD,EUR,GBP" } response = requests.get(url, headers=headers, params=params) data = response.json() print(data) ``` ```php PHP theme={null} '2025-08-01', 'end_date' => '2025-08-31', 'symbols' => 'USD,EUR,GBP' ]); $curl = curl_init(); curl_setopt_array($curl, array( CURLOPT_URL => "https://api.exchangeratesapi.com.au/timeseries?{$params}", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => array( 'Authorization: Bearer your_api_key_here' ), )); $response = curl_exec($curl); curl_close($curl); $data = json_decode($response, true); print_r($data); ?> ``` ## Response ```json 200 OK theme={null} { "success": true, "timeseries": true, "start_date": "2025-08-01", "end_date": "2025-08-31", "base": "AUD", "rates": { "2025-08-01": { "USD": 0.642100, "EUR": 0.561800, "GBP": 0.486200 }, "2025-08-02": { "USD": 0.644000, "EUR": 0.563100, "GBP": 0.487500 }, "2025-08-05": { "USD": 0.643800, "EUR": 0.562700, "GBP": 0.487100 } } } ``` ### Response Fields Indicates if the request was successful Always true for timeseries responses Start date from the request in YYYY-MM-DD format End date from the request in YYYY-MM-DD format Base currency (always "AUD") Object with dates as keys and rate objects as values Each date key contains an object with currency codes and their rates Exchange rate (AUD per unit of foreign currency) with precision varying by currency (up to 6 decimal places for most, whole numbers for IDR/VND as provided by RBA) ## Response Headers * `Cache-Control: no-store` - Response should not be cached * `X-Request-Id: ` - Unique request identifier for debugging ## Error Responses ```json 400 Bad Request - Missing Parameters theme={null} { "success": false, "error": { "code": 400, "type": "bad_request", "info": "Missing required parameters: start_date, end_date" } } ``` ```json 400 Bad Request - Invalid Date Format theme={null} { "success": false, "error": { "code": 400, "type": "bad_request", "info": "Invalid date format. Use YYYY-MM-DD" } } ``` ```json 400 Bad Request - Date Range Too Large theme={null} { "success": false, "error": { "code": 400, "type": "bad_request", "info": "Date range cannot exceed 365 days" } } ``` ```json 400 Bad Request - Invalid Date Order theme={null} { "success": false, "error": { "code": 400, "type": "bad_request", "info": "end_date must be after start_date" } } ``` ```json 400 Bad Request - Unsupported Currency theme={null} { "success": false, "error": { "code": 400, "type": "bad_request", "info": "Unsupported currency 'XYZ'" } } ``` ```json 400 Bad Request - Invalid Base Currency theme={null} { "success": false, "error": { "code": 400, "type": "bad_request", "info": "Only AUD is supported as base currency in MVP" } } ``` ```json 404 Not Found - No Data Available theme={null} { "success": false, "error": { "code": 404, "type": "not_found", "info": "No data available for date range 2017-01-01 to 2017-12-31" } } ``` ```json 401 Unauthorized theme={null} { "success": false, "error": { "code": 401, "type": "unauthorized", "info": "Invalid or missing API key" } } ``` ## Rate Limits This endpoint is subject to your plan's monthly request limits: | Plan | Monthly Requests | Historical Access | | ------------ | ---------------- | -------------------- | | Free | 150 | Blocked (no access) | | Starter | 5,000 | Last 30 days | | Professional | 50,000 | Full history (2018+) | | Business | 500,000 | Full history (2018+) | **Free Plan**: No access to timeseries endpoint (blocked)\ **Starter Plan**: 30 days access with 7-day maximum range\ **Professional Plan**: Full history (2018+) with 90-day maximum range\ **Business/Enterprise Plans**: Full history (2018+) with 365-day maximum range ## Important Notes * **Weekend & Holiday Gaps**: The RBA doesn't publish rates on weekends or Australian public holidays. Missing dates are not included in the response. * **Maximum Range**: Date ranges cannot exceed 365 days * **Data Availability**: Historical data is available from January 1, 2018 onwards * **Currency Filtering**: Use the `symbols` parameter to reduce response size and focus on specific currencies * **Precision**: Rates are provided with up to 6 decimal places (varies by currency; IDR and VND are whole numbers from RBA source data) ## Use Cases * **Historical Analysis**: Analyze currency trends over time * **Chart Generation**: Build time-series charts and graphs * **Backtesting**: Test trading strategies with historical data * **Reporting**: Generate monthly/quarterly currency reports * **Research**: Academic and financial research projects * **Compliance**: ATO reporting with official RBA historical rates # Create Webhook Subscription Source: https://docs.exchangeratesapi.com.au/api-reference/create-webhook-subscription /openapi.json post /api/webhook-subscriptions Create a webhook subscription (Professional plan or higher). Returns the signing secret once. Authenticate with your Bearer API key (or manage from your dashboard). # Currency Conversion Source: https://docs.exchangeratesapi.com.au/api-reference/currency-conversion /openapi.json get /convert Convert amounts between currencies. Requires authentication. Free tier: 150, no historical data. Starter: 5,000/month, 30 days history. Professional: 50,000/month, full history. Business: 500,000/month, full history. # Delete Webhook Subscription Source: https://docs.exchangeratesapi.com.au/api-reference/delete-webhook-subscription /openapi.json delete /api/webhook-subscriptions/{id} Delete one of your webhook subscriptions. # Historical Exchange Rates Source: https://docs.exchangeratesapi.com.au/api-reference/historical-exchange-rates /openapi.json get /timeseries Get exchange rates for a date range. Historical access: Free=blocked, Starter=30 days, Professional/Business=full history. Date range limits: Starter=7 days, Professional=90 days, Business=365 days. # Historical Rate for Currency and Date Source: https://docs.exchangeratesapi.com.au/api-reference/historical-rate-for-currency-and-date /openapi.json get /{date}/{currency} Get the exchange rate for a specific currency on a specific historical date. Historical access: Free=none, Starter=30 days, Professional/Business=full history (2018+). # Historical Rates for Date Source: https://docs.exchangeratesapi.com.au/api-reference/historical-rates-for-date /openapi.json get /{date} Get all exchange rates for a specific historical date. Historical access: Free=none, Starter=30 days, Professional/Business=full history (2018+). # Latest Exchange Rates Source: https://docs.exchangeratesapi.com.au/api-reference/latest-exchange-rates /openapi.json get /latest Get all current exchange rates from the Reserve Bank of Australia with optional filtering. Quota limits: Free=150 (one-time trial), Starter=5,000/month, Professional=50,000/month, Business=500,000/month. # Latest Rate for Currency Source: https://docs.exchangeratesapi.com.au/api-reference/latest-rate-for-currency /openapi.json get /latest/{currency} Get the current exchange rate for a specific currency. Quota limits: Free=150 (one-time trial), Starter=5,000/month, Professional=50,000/month, Business=500,000/month. # List Webhook Subscriptions Source: https://docs.exchangeratesapi.com.au/api-reference/list-webhook-subscriptions /openapi.json get /api/webhook-subscriptions List your webhook subscriptions. The signing secret is never returned. # Status (/status) Source: https://docs.exchangeratesapi.com.au/api-reference/public/status GET /status Check the operational status of the Exchange Rates API and when data was last updated ## Overview The status endpoint provides real-time information about the API's operational status and data freshness. This endpoint is **public** and doesn't require authentication. This endpoint is perfect for health checks and monitoring integrations. It's unlimited and cached at the edge for optimal performance. ## Request ```bash cURL theme={null} curl https://api.exchangeratesapi.com.au/status ``` ```javascript JavaScript theme={null} const response = await fetch('https://api.exchangeratesapi.com.au/status'); const data = await response.json(); console.log('API Status:', data.status); ``` ```python Python theme={null} import requests response = requests.get('https://api.exchangeratesapi.com.au/status') data = response.json() print(f"API Status: {data['status']}") ``` ```php PHP theme={null} ``` ## Response ```json Success Response theme={null} { "status": "operational", "last_update": "2025-09-01T06:17:41.202Z", "stale": false, "next_update_expected": "2025-09-02T06:00:00.000Z" } ``` Current operational status of the API **Possible values:** * `operational` - API is fully functional * `degraded` - API is experiencing issues but functional * `outage` - API is experiencing a full outage ISO 8601 timestamp of when RBA data was last successfully fetched and processed Indicates if the current data is stale (older than expected) * `false` - Data is fresh (within expected timeframe) * `true` - Data is stale (RBA update may have failed) ISO 8601 timestamp of when the next RBA data update is expected (4 PM AEST next business day) ## Status Codes | Status | Description | | ------------- | ------------------------------------------- | | `operational` | All systems functioning normally | | `degraded` | Minor issues detected, API still functional | | `outage` | Major issues, API may not be functioning | ## Data Freshness The Exchange Rates API fetches RBA data daily at **4:00 PM AEST**. The `stale` field helps you understand data freshness: ### Fresh Data (`stale: false`) * Last update was within expected timeframe (within 26 hours of last business day 4 PM AEST) * RBA data fetch was successful * All systems operating normally ### Stale Data (`stale: true`) * Last update was longer ago than expected (over 26 hours) * May indicate RBA website issues or weekend/holiday delay * Data is still valid but older than usual * Additional headers will be present on rate endpoints: * `X-Data-Stale: true` * `X-RBA-Source-Date: YYYY-MM-DD` ## Monitoring Integration ### Health Check Example ```javascript theme={null} async function healthCheck() { try { const response = await fetch('https://api.exchangeratesapi.com.au/status'); const data = await response.json(); if (data.status === 'operational' && !data.stale) { return { healthy: true, message: 'API operational with fresh data' }; } else if (data.status === 'operational' && data.stale) { return { healthy: true, warning: 'API operational but data may be stale', lastUpdate: data.last_update }; } else { return { healthy: false, error: `API status: ${data.status}`, lastUpdate: data.last_update }; } } catch (error) { return { healthy: false, error: 'Failed to reach API' }; } } ``` ### Nagios/Zabbix Integration ```bash theme={null} #!/bin/bash # Health check script for monitoring systems STATUS=$(curl -s https://api.exchangeratesapi.com.au/status | jq -r '.status') case $STATUS in "operational") echo "OK - Exchange Rates API is operational" exit 0 ;; "degraded") echo "WARNING - Exchange Rates API is degraded" exit 1 ;; "outage") echo "CRITICAL - Exchange Rates API is experiencing an outage" exit 2 ;; *) echo "UNKNOWN - Could not determine API status" exit 3 ;; esac ``` ## Response Headers ```http theme={null} HTTP/2 200 OK Content-Type: application/json Cache-Control: no-store X-Request-Id: 123e4567-e89b-12d3-a456-426614174000 X-Response-Time: 45ms CF-Cache-Status: MISS CF-Ray: 8a1b2c3d4e5f6789-SYD ``` The status endpoint returns `Cache-Control: no-store` to ensure you always get current status information rather than cached responses. ## Use Cases ### Application Health Checks Monitor your application's dependency on the Exchange Rates API: ```javascript theme={null} // Check API health before making rate requests const statusCheck = await fetch('https://api.exchangeratesapi.com.au/status'); const status = await statusCheck.json(); if (status.status !== 'operational') { console.warn('Exchange Rates API is not operational, using cached rates'); return getCachedRates(); } ``` ### Status Page Integration Display API status on your own status page: ```javascript theme={null} async function getAPIStatus() { const response = await fetch('https://api.exchangeratesapi.com.au/status'); const data = await response.json(); return { service: 'Exchange Rates API', status: data.status, lastUpdate: data.last_update, isStale: data.stale, nextUpdate: data.next_update_expected }; } ``` ### Scheduled Monitoring Set up automated monitoring to track API reliability: ```python theme={null} import requests import time from datetime import datetime def monitor_api(): try: response = requests.get('https://api.exchangeratesapi.com.au/status', timeout=30) data = response.json() log_entry = { 'timestamp': datetime.now().isoformat(), 'status': data['status'], 'stale': data['stale'], 'response_time': response.elapsed.total_seconds(), 'next_update': data['next_update_expected'] } # Log to your monitoring system print(f"API Status: {data['status']} | Stale: {data['stale']} | Response: {response.elapsed.total_seconds()}s") except Exception as e: print(f"Failed to check API status: {e}") # Run every 5 minutes while True: monitor_api() time.sleep(300) ``` ## Error Responses While the status endpoint is highly reliable, it may occasionally return errors: ```json theme={null} { "success": false, "error": { "code": 503, "type": "service_unavailable", "info": "Status service temporarily unavailable" } } ``` In such cases, you should assume the API may be experiencing issues and implement appropriate fallback logic in your applications. # Supported Currencies (/symbols) Source: https://docs.exchangeratesapi.com.au/api-reference/public/symbols GET /symbols Get a complete list of all supported currencies with names, symbols, and country information ## Overview The symbols endpoint returns detailed information about all currencies supported by the Exchange Rates API. This includes currency names, symbols, country information, and notes about special currencies. This endpoint is **public** and doesn't require authentication. It's perfect for building currency selection dropdowns and validation logic. ## Request ```bash cURL theme={null} curl https://api.exchangeratesapi.com.au/symbols ``` ```javascript JavaScript theme={null} const response = await fetch('https://api.exchangeratesapi.com.au/symbols'); const data = await response.json(); console.log('Supported currencies:', Object.keys(data.symbols)); ``` ```python Python theme={null} import requests response = requests.get('https://api.exchangeratesapi.com.au/symbols') data = response.json() print(f"Total currencies: {data['count']}") for code, info in data['symbols'].items(): print(f"{code}: {info['name']}") ``` ```php PHP theme={null} $info) { echo "$code: " . $info['name'] . "\n"; } ?> ``` ## Response ```json Success Response theme={null} { "success": true, "symbols": { "AUD": { "name": "Australian Dollar", "symbol": "$", "country": "Australia" }, "USD": { "name": "US Dollar", "symbol": "$", "country": "United States" }, "EUR": { "name": "Euro", "symbol": "€", "country": "European Union" }, "GBP": { "name": "British Pound Sterling", "symbol": "£", "country": "United Kingdom" }, "JPY": { "name": "Japanese Yen", "symbol": "¥", "country": "Japan" }, "TWI": { "name": "Trade-Weighted Index", "symbol": "TWI", "country": "Australia" }, "SDR": { "name": "Special Drawing Rights", "symbol": "SDR", "country": "IMF" } }, "count": 21, "base": "AUD", "note": "All rates are quoted as AUD per unit of foreign currency from the Reserve Bank of Australia" } ``` Always `true` for successful responses Object containing currency information keyed by ISO 4217 currency code Full name of the currency Currency symbol or abbreviation used for display Country or region where the currency is used Total number of supported currencies Base currency for all exchange rates (always "AUD") General information about how rates are quoted ## Supported Currencies ### Major Global Currencies | Code | Name | Symbol | Country | | ------- | ---------------------- | ------ | -------------- | | **USD** | US Dollar | \$ | United States | | **EUR** | Euro | € | European Union | | **GBP** | British Pound Sterling | £ | United Kingdom | | **JPY** | Japanese Yen | ¥ | Japan | | **CHF** | Swiss Franc | CHF | Switzerland | | **CAD** | Canadian Dollar | \$ | Canada | ### Asia-Pacific Region | Code | Name | Symbol | Country | | ------- | ------------------ | ------ | ----------- | | **CNY** | Chinese Renminbi | ¥ | China | | **KRW** | South Korean Won | ₩ | South Korea | | **SGD** | Singapore Dollar | \$ | Singapore | | **NZD** | New Zealand Dollar | \$ | New Zealand | | **HKD** | Hong Kong Dollar | \$ | Hong Kong | | **TWD** | Taiwan New Dollar | NT\$ | Taiwan | | **INR** | Indian Rupee | ₹ | India | | **THB** | Thai Baht | ฿ | Thailand | | **MYR** | Malaysian Ringgit | RM | Malaysia | | **IDR** | Indonesian Rupiah¹ | Rp | Indonesia | | **VND** | Vietnamese Dong¹ | ₫ | Vietnam | | **PHP** | Philippine Peso | ₱ | Philippines | ¹ **IDR and VND Precision**: Indonesian Rupiah and Vietnamese Dong are provided as whole numbers (no decimal places) by the Reserve Bank of Australia source data. ### Special Currencies | Code | Name | Symbol | Notes | | ------- | ---------------------- | ------ | ------------------------------------------------------------------ | | **AUD** | Australian Dollar | \$ | Base currency | | **TWI** | Trade-Weighted Index | TWI | Australia's trade-weighted index; **not available for conversion** | | **SDR** | Special Drawing Rights | SDR | International Monetary Fund reserve asset | **TWI Conversion Restriction**: The Trade-Weighted Index (TWI) is included in rate listings but cannot be used in conversion operations (`/convert` endpoint). Using TWI as `from` or `to` parameter will return a 400 error. ## Building Currency Dropdowns Use the symbols endpoint to populate currency selection interfaces: ```javascript React Example theme={null} import { useState, useEffect } from 'react'; function CurrencySelect({ onSelect, excludeTWI = false }) { const [currencies, setCurrencies] = useState([]); const [loading, setLoading] = useState(true); useEffect(() => { async function fetchCurrencies() { try { const response = await fetch('https://api.exchangeratesapi.com.au/symbols'); const data = await response.json(); let currencyList = Object.entries(data.symbols).map(([code, info]) => ({ code, name: info.name, symbol: info.symbol, country: info.country, hasNote: !!info.note })); // Optionally exclude TWI for conversion interfaces if (excludeTWI) { currencyList = currencyList.filter(c => c.code !== 'TWI'); } // Sort alphabetically by code currencyList.sort((a, b) => a.code.localeCompare(b.code)); setCurrencies(currencyList); } catch (error) { console.error('Failed to fetch currencies:', error); } finally { setLoading(false); } } fetchCurrencies(); }, [excludeTWI]); if (loading) return
Loading currencies...
; return ( ); } ``` ```python Flask Example theme={null} from flask import Flask, render_template import requests app = Flask(__name__) @app.route('/currency-converter') def currency_converter(): try: response = requests.get('https://api.exchangeratesapi.com.au/symbols') data = response.json() # Exclude TWI for conversion interface currencies = { code: info for code, info in data['symbols'].items() if code != 'TWI' } return render_template('converter.html', currencies=currencies) except Exception as e: return f"Error fetching currencies: {e}", 500 ``` ```php Laravel Example theme={null} json(); // Sort by currency code for consistent ordering ksort($data['symbols']); return $data['symbols']; } catch (Exception $e) { // Return fallback list if API is unavailable return $this->getFallbackCurrencies(); } }); } private function getFallbackCurrencies() { return [ 'AUD' => ['name' => 'Australian Dollar', 'symbol' => '$'], 'USD' => ['name' => 'US Dollar', 'symbol' => '$'], 'EUR' => ['name' => 'Euro', 'symbol' => '€'], 'GBP' => ['name' => 'British Pound Sterling', 'symbol' => '£'], 'JPY' => ['name' => 'Japanese Yen', 'symbol' => '¥'], ]; } } ?> ```
## Validation Logic Use the symbols data for input validation: ```javascript theme={null} class CurrencyValidator { constructor() { this.supportedCurrencies = new Set(); this.conversionBlacklist = new Set(['TWI']); // Currencies not allowed for conversion this.loadCurrencies(); } async loadCurrencies() { try { const response = await fetch('https://api.exchangeratesapi.com.au/symbols'); const data = await response.json(); this.supportedCurrencies = new Set(Object.keys(data.symbols)); } catch (error) { console.error('Failed to load currency symbols:', error); // Fallback to hardcoded list this.supportedCurrencies = new Set(['AUD', 'USD', 'EUR', 'GBP', 'JPY']); } } isValidCurrency(code) { return this.supportedCurrencies.has(code.toUpperCase()); } canConvert(fromCode, toCode) { const from = fromCode.toUpperCase(); const to = toCode.toUpperCase(); return this.isValidCurrency(from) && this.isValidCurrency(to) && !this.conversionBlacklist.has(from) && !this.conversionBlacklist.has(to); } } // Usage const validator = new CurrencyValidator(); // Validate user input if (!validator.isValidCurrency('XYZ')) { console.error('XYZ is not a supported currency'); } // Check conversion compatibility if (!validator.canConvert('AUD', 'TWI')) { console.error('TWI cannot be used for conversion operations'); } ``` ## Response Headers ```http theme={null} HTTP/2 200 OK Content-Type: application/json Cache-Control: public, max-age=86400 X-Request-Id: 123e4567-e89b-12d3-a456-426614174000 X-Response-Time: 12ms ``` The symbols endpoint is cached for 24 hours (`max-age=86400`) since supported currencies rarely change. This helps improve performance and reduces unnecessary requests. ## Common Use Cases ### 1. Currency Selection Interface Build dropdown menus or search interfaces for currency selection. ### 2. Input Validation Validate user-provided currency codes before making API requests. ### 3. Display Formatting Use currency symbols and names for user-friendly display of rates and amounts. ### 4. Feature Detection Check if specific currencies are supported before implementing features. ### 5. Conversion Compatibility Determine which currencies can be used with the `/convert` endpoint (excluding TWI). ## Error Responses The symbols endpoint is highly reliable, but may occasionally return errors: ```json theme={null} { "success": false, "error": { "code": 503, "type": "service_unavailable", "info": "Symbols service temporarily unavailable" } } ``` In such cases, implement fallback logic with a hardcoded list of major currencies to maintain functionality. # Send Test Webhook Source: https://docs.exchangeratesapi.com.au/api-reference/send-test-webhook /openapi.json post /api/webhook-subscriptions/{id}/test Send a sample rates.test delivery to the subscription's URL so you can verify your endpoint and signature handling. # Supported Currencies Source: https://docs.exchangeratesapi.com.au/api-reference/supported-currencies /openapi.json get /symbols List all supported currencies with their names, symbols, and countries # Create Webhook (POST /api/webhook-subscriptions) Source: https://docs.exchangeratesapi.com.au/api-reference/webhooks/create POST /api/webhook-subscriptions Register a URL to receive signed rate-published notifications (Professional plan and above). ## Overview Registers a webhook subscription. The response includes a **signing secret shown only once** - store it securely; you'll need it to [verify deliveries](/api-reference/webhooks/overview#verifying-signatures). Authenticate with your API key - the same `Authorization: Bearer YOUR_API_KEY` you use for the rest of the API. You can also create and manage webhooks from your dashboard. Professional plan or higher is required. `currencies` is optional - omit it to receive every supported currency, or pass a list (e.g. `["USD","JPY"]`) to receive only those. The `url` must be a public **HTTPS** endpoint. # Delete Webhook (DELETE /api/webhook-subscriptions/{id}) Source: https://docs.exchangeratesapi.com.au/api-reference/webhooks/delete DELETE /api/webhook-subscriptions/{id} Remove a webhook subscription. ## Overview Deletes one of your subscriptions. You can only delete subscriptions you own; deleting an unknown or other-account id returns `404`. Authenticate with your API key - the same `Authorization: Bearer YOUR_API_KEY` you use for the rest of the API. You can also manage webhooks from your dashboard. # List Webhooks (GET /api/webhook-subscriptions) Source: https://docs.exchangeratesapi.com.au/api-reference/webhooks/list GET /api/webhook-subscriptions List your webhook subscriptions and their health. ## Overview Returns your subscriptions, including `isActive` and `failureCount` so you can spot a subscription that has been auto-deactivated after repeated delivery failures. The signing secret is **never** returned - it is shown only once at creation. Authenticate with your API key - the same `Authorization: Bearer YOUR_API_KEY` you use for the rest of the API. You can also manage webhooks from your dashboard. # Webhooks Overview Source: https://docs.exchangeratesapi.com.au/api-reference/webhooks/overview Get notified the moment each business day's RBA exchange rates are published - no polling required. ## What webhooks do Instead of polling `/latest` after 4 PM hoping today's rates have landed, a webhook **pushes** them to you. When the RBA publishes the daily rates, we send a signed HTTP `POST` to the URL you register - typically within ten minutes of publication. Webhooks are available on the **Professional plan and above**. You can register up to 3 (Professional), 10 (Business), or 50 (Enterprise) subscriptions. Manage webhooks with your API key (`Authorization: Bearer YOUR_API_KEY`) - the same key you use for the rest of the API - or from your dashboard. Your existing `/latest` and other REST endpoints are unchanged; webhooks are an addition, not a replacement. ## Timing The RBA targets \~4:00 PM Australian Eastern Time on business days, but the exact publication time drifts (often 4–5 PM, occasionally later), and it does not publish on weekends or NSW public holidays. The webhook fires once per business day, shortly after we detect the new rates. ## The delivery request We `POST` JSON to your URL with these headers: | Header | Value | | --------------------------- | ------------------------------------------------------------------- | | `Content-Type` | `application/json` | | `X-ExchangeRates-Event` | `rates.published` (or `rates.test` for test sends) | | `X-ExchangeRates-Signature` | `sha256=` - see [Verifying signatures](#verifying-signatures) | | `X-ExchangeRates-Delivery` | A unique delivery UUID | | `User-Agent` | `ExchangeRatesAPI-Webhook/1.0` | Body: ```json theme={null} { "event": "rates.published", "date": "2026-06-23", "base": "AUD", "rates": { "USD": 0.7004, "JPY": 113.25 }, "delivery_id": "0b9d8f60-a5c3-e1d9-b2f4-a6c83f9c4e2a", "timestamp": 1782086400 } ``` `rates` contains every supported currency, or only the currencies you subscribed to. Respond with any `2xx` status to acknowledge receipt. ## Verifying signatures Every delivery is signed so you can confirm it came from us. We compute `HMAC-SHA256` over the **raw request body** using your subscription's signing secret (shown once when you create the subscription), hex-encode it, and send it as `X-ExchangeRates-Signature: sha256=`. Recompute the HMAC over the exact bytes you received and compare. Use a constant-time comparison. ```javascript Node.js theme={null} import crypto from 'node:crypto' // rawBody: the exact string/Buffer received (do NOT re-serialize parsed JSON) function verify(rawBody, signatureHeader, signingSecret) { const expected = 'sha256=' + crypto .createHmac('sha256', signingSecret) .update(rawBody) .digest('hex') const a = Buffer.from(signatureHeader) const b = Buffer.from(expected) return a.length === b.length && crypto.timingSafeEqual(a, b) } ``` ```python Python theme={null} import hashlib import hmac def verify(raw_body: bytes, signature_header: str, signing_secret: str) -> bool: expected = "sha256=" + hmac.new( signing_secret.encode(), raw_body, hashlib.sha256 ).hexdigest() return hmac.compare_digest(expected, signature_header) ``` ```php PHP theme={null} Verify against the **raw** request body bytes, not a re-serialized JSON object - re-serialization can change byte order or whitespace and break the signature. ## Retries and failures If your endpoint does not return a `2xx`, we retry the delivery several times with backoff. Deliveries that never succeed are dropped after the retry budget is exhausted. If a subscription fails on **7 consecutive** publications, we automatically deactivate it; it will then show as inactive when you [list your subscriptions](/api-reference/webhooks/list). Re-create it once your endpoint is healthy. ## Testing your endpoint Use the [test endpoint](/api-reference/webhooks/test) to send a sample `rates.test` delivery on demand - it is signed exactly like a real one - so you can validate your receiver and signature check without waiting for the next publication. # Test Webhook (POST /api/webhook-subscriptions/{id}/test) Source: https://docs.exchangeratesapi.com.au/api-reference/webhooks/test POST /api/webhook-subscriptions/{id}/test Send a sample signed delivery to verify your endpoint. ## Overview Sends a one-off `rates.test` delivery to the subscription's URL, signed exactly like a real `rates.published` delivery. The response tells you the HTTP status your endpoint returned, so you can confirm your receiver and [signature verification](/api-reference/webhooks/overview#verifying-signatures) work before the next publication. Authenticate with your API key - the same `Authorization: Bearer YOUR_API_KEY` you use for the rest of the API. You can also manage webhooks from your dashboard. # Authentication Source: https://docs.exchangeratesapi.com.au/authentication Learn how to authenticate with the Exchange Rates API using bearer tokens and API keys ## API Key Overview The Exchange Rates API uses **Bearer Token authentication** with unique API keys. All authenticated endpoints require an API key passed in the `Authorization` header. API keys are unique to your account and should be kept secure. Never share your API key or commit it to version control. ## Getting Your API Key ### 1. Create Account Sign up at [app.exchangeratesapi.com.au](https://app.exchangeratesapi.com.au) using your email address. ### 2. Verify Email Click the magic link in your email to verify your account and access the dashboard. ### 3. Generate API Key In the dashboard, click **"Generate New API Key"** to create your unique API key. Your API key is only displayed once for security reasons. Make sure to copy and store it securely immediately after generation. ## API Key Format API keys follow this format: `{suburb}_{unique_identifier}` ``` buderim_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0u1v2w3x4y5z6 ``` * **Suburb prefix**: Each key gets a unique Australian suburb name (e.g., `buderim`, `montville`, `noosa`) * **Unique identifier**: 56-character alphanumeric string * **Total length**: \~65 characters ## Authentication Methods ### Bearer Token (Recommended) Pass your API key in the `Authorization` header using the Bearer scheme: ```bash cURL theme={null} curl https://api.exchangeratesapi.com.au/latest \ -H "Authorization: Bearer your_api_key_here" ``` ```javascript JavaScript theme={null} const response = await fetch('https://api.exchangeratesapi.com.au/latest', { headers: { 'Authorization': 'Bearer your_api_key_here' } }); ``` ```python Python theme={null} import requests headers = { 'Authorization': 'Bearer your_api_key_here' } response = requests.get( 'https://api.exchangeratesapi.com.au/latest', headers=headers ) ``` ```php PHP theme={null} 'https://api.exchangeratesapi.com.au/latest', CURLOPT_HTTPHEADER => $headers, CURLOPT_RETURNTRANSFER => true ]); $response = curl_exec($curl); ?> ``` ## Security Best Practices ### Environment Variables Store your API key in environment variables, never hardcode it: ```javascript JavaScript theme={null} // ✅ Good - Use environment variables const API_KEY = process.env.EXCHANGE_RATES_API_KEY; const response = await fetch('https://api.exchangeratesapi.com.au/latest', { headers: { 'Authorization': `Bearer ${API_KEY}` } }); ``` ```python Python theme={null} # ✅ Good - Use environment variables import os import requests api_key = os.getenv('EXCHANGE_RATES_API_KEY') headers = { 'Authorization': f'Bearer {api_key}' } response = requests.get( 'https://api.exchangeratesapi.com.au/latest', headers=headers ) ``` ```php PHP theme={null} ``` ### Server-Side Only API keys should only be used in server-side applications. **Never expose API keys in:** * Frontend JavaScript code * Mobile applications * Client-side frameworks (React, Vue, Angular) * Browser developer tools * Version control systems ```javascript theme={null} // ❌ Bad - Never do this in frontend code const API_KEY = 'your_api_key_here'; // Exposed to users! // ✅ Good - Call your backend API instead const response = await fetch('/api/exchange-rates'); ``` ### HTTPS Only All API requests must use HTTPS. HTTP requests will be rejected: ```bash theme={null} # ❌ Bad - HTTP not allowed curl http://api.exchangeratesapi.com.au/latest # ✅ Good - HTTPS required curl https://api.exchangeratesapi.com.au/latest ``` ## Managing API Keys ### Key Status API keys can have the following statuses: * **Active**: Key is valid and can make requests * **Revoked**: Key has been disabled and cannot make requests * **Suspended**: Account is suspended (billing issues, etc.) ### Revoking Keys If your API key is compromised: 1. Log into your [dashboard](https://app.exchangeratesapi.com.au) 2. Find your API key in the list 3. Click **"Revoke"** to immediately disable it 4. Generate a new API key 5. Update your applications with the new key ### Key Rotation For security, we recommend rotating your API keys periodically. The overlapping rotation below requires a plan that supports more than one active key (Professional and above). On the **Free** and **Starter** plans your account holds a single key, and generating a new one immediately invalidates the old one - see [Single-Key Plan Rotation](#single-key-plan-rotation) below. #### Multi-Key Rotation (Professional and above) Create a new API key in your dashboard while keeping the old one active Deploy your applications with the new API key Ensure all applications are using the new key successfully Once confirmed, revoke the old API key to complete the rotation #### Single-Key Plan Rotation On Free and Starter there is no overlap window: the moment you generate a replacement, every application still using the previous key will receive `401` responses. Rotate during a planned maintenance window: Stage your applications so the new key can be rolled out immediately - for example, by reading the key from an environment variable or secret store you can update in one step Generate the new key in your dashboard. The previous key stops working at this point Roll the new key out to all applications and confirm requests are succeeding If you cannot tolerate a cutover gap, or you need separate keys per team or environment, upgrade to Professional (5 keys) or Business (10 keys). See [Rate Limits](/guides/rate-limits#api-keys-per-plan) for per-plan key allowances. ## Authentication Errors ### Invalid API Key (401) ```json theme={null} { "success": false, "error": { "code": 401, "type": "invalid_api_key", "info": "Invalid or missing API key." } } ``` **Common causes:** * Missing `Authorization` header * Incorrect Bearer token format * API key has been revoked * Typo in the API key ### Account Suspended (401) ```json theme={null} { "success": false, "error": { "code": 401, "type": "account_suspended", "info": "Account is suspended. Please contact support." } } ``` **Common causes:** * Billing issues (overdue payments) * Terms of service violations * Suspicious activity detected ### Rate Limit Exceeded (429) ```json theme={null} { "success": false, "error": { "code": 429, "type": "rate_limit_exceeded", "info": "Daily quota exceeded. Upgrade your plan or try again tomorrow." } } ``` ## Public Endpoints (No Auth Required) Some endpoints don't require authentication: | Endpoint | Description | Rate Limit | | -------------- | ------------------------- | ---------- | | `GET /status` | API operational status | Unlimited | | `GET /symbols` | List supported currencies | Unlimited | ```bash theme={null} # No authentication needed curl https://api.exchangeratesapi.com.au/status curl https://api.exchangeratesapi.com.au/symbols ``` ## Testing Your Authentication Use this simple test to verify your API key works: ```bash cURL Test theme={null} curl -i https://api.exchangeratesapi.com.au/latest \ -H "Authorization: Bearer your_api_key_here" ``` ```javascript JavaScript Test theme={null} async function testAuth() { try { const response = await fetch('https://api.exchangeratesapi.com.au/latest', { headers: { 'Authorization': 'Bearer your_api_key_here' } }); const data = await response.json(); if (data.success) { console.log('✅ Authentication successful'); console.log(`Plan: ${data.plan || 'free'}`); } else { console.log('❌ Authentication failed:', data.error.info); } } catch (error) { console.log('❌ Request failed:', error.message); } } testAuth(); ``` ```python Python Test theme={null} import requests def test_auth(): try: response = requests.get( 'https://api.exchangeratesapi.com.au/latest', headers={'Authorization': 'Bearer your_api_key_here'} ) data = response.json() if data.get('success'): print('✅ Authentication successful') print(f"Plan: {data.get('plan', 'free')}") else: print(f'❌ Authentication failed: {data["error"]["info"]}') except Exception as error: print(f'❌ Request failed: {error}') test_auth() ``` ## Next Steps Understand your plan's quotas and limits Explore all available endpoints Manage your API keys and monitor usage Learn security and performance tips # Best Practices Source: https://docs.exchangeratesapi.com.au/guides/best-practices Learn how to optimize performance, handle errors gracefully, and implement robust currency conversion applications ## API Key Security ### Environment Variables Never hardcode API keys in your source code. Use environment variables instead: ```javascript Node.js theme={null} // ✅ Good - Environment variable const API_KEY = process.env.EXCHANGE_RATES_API_KEY; // ❌ Bad - Hardcoded (never do this!) const API_KEY = 'buderim_abc123...'; ``` ```python Python theme={null} import os # ✅ Good - Environment variable API_KEY = os.getenv('EXCHANGE_RATES_API_KEY') # ❌ Bad - Hardcoded (never do this!) API_KEY = 'buderim_abc123...' ``` ```php PHP theme={null} ``` ### Server-Side Only API keys should only be used in server-side applications: ```javascript theme={null} // ❌ Bad - Frontend JavaScript (exposed to users!) const API_KEY = process.env.REACT_APP_EXCHANGE_RATES_API_KEY; // ✅ Good - Create a backend proxy endpoint app.get('/api/exchange-rates', async (req, res) => { const response = await fetch('https://api.exchangeratesapi.com.au/latest', { headers: { 'Authorization': `Bearer ${process.env.EXCHANGE_RATES_API_KEY}` } }); const data = await response.json(); res.json(data); }); ``` and always add .env files to .gitignore. ### Key Rotation Implement regular key rotation for enhanced security: ```javascript theme={null} class APIKeyManager { constructor() { this.currentKey = process.env.EXCHANGE_RATES_API_KEY; this.fallbackKey = process.env.EXCHANGE_RATES_API_KEY_BACKUP; } async makeRequest(endpoint) { try { return await this.requestWithKey(endpoint, this.currentKey); } catch (error) { if (error.code === 401 && this.fallbackKey) { console.warn('Primary key failed, trying fallback key'); return await this.requestWithKey(endpoint, this.fallbackKey); } throw error; } } async requestWithKey(endpoint, key) { const response = await fetch(`https://api.exchangeratesapi.com.au${endpoint}`, { headers: { 'Authorization': `Bearer ${key}` } }); const data = await response.json(); if (!data.success) { const error = new Error(data.error.info); error.code = data.error.code; throw error; } return data; } } ``` ## Knowing When Rates Update RBA publishes daily, but the exact time drifts (often 4–5 PM AET, occasionally later). There are two ways to stay current: * **Webhooks (preferred for freshness)** - on the Professional plan and above, register a [webhook](/api-reference/webhooks/overview) and we push you a signed notification within minutes of publication. You fetch exactly once, when the data is actually ready, instead of polling and guessing. * **Polling with smart caching** - if you poll, cache aggressively until the next expected update (below) to avoid wasted requests. Webhooks and caching are complementary: use a webhook to learn *when* new rates land, and caching to serve the data efficiently between updates. ## Caching Strategies ### Smart Caching Based on RBA Schedule RBA publishes rates daily around 4 PM AET (the exact time varies). Cache aggressively until the next expected update: ```javascript theme={null} class SmartCache { constructor() { this.cache = new Map(); this.cacheExpiry = new Map(); } getNextRBAUpdate() { const now = new Date(); const nextUpdate = new Date(); // Set to 4:15 PM AEST (15 minutes after RBA update) nextUpdate.setHours(16, 15, 0, 0); // If it's already past 4:15 PM today, use tomorrow if (now > nextUpdate) { nextUpdate.setDate(nextUpdate.getDate() + 1); } // Skip weekends (RBA doesn't update on weekends) while (nextUpdate.getDay() === 0 || nextUpdate.getDay() === 6) { nextUpdate.setDate(nextUpdate.getDate() + 1); } return nextUpdate; } async getLatestRates(apiClient) { const cacheKey = 'latest_rates'; const now = Date.now(); // Check if cached data is still valid if (this.cache.has(cacheKey) && this.cacheExpiry.get(cacheKey) > now) { return this.cache.get(cacheKey); } // Fetch fresh data const data = await apiClient.getLatestRates(); // Cache until next expected RBA update const nextUpdate = this.getNextRBAUpdate(); this.cache.set(cacheKey, data); this.cacheExpiry.set(cacheKey, nextUpdate.getTime()); console.log(`Cached rates until ${nextUpdate.toISOString()}`); return data; } } ``` ### Historical Data Caching Historical rates never change, so cache them indefinitely: ```python theme={null} import requests import pickle import os from datetime import datetime, timedelta class HistoricalRateCache: def __init__(self, api_key, cache_dir='./rate_cache'): self.api_key = api_key self.cache_dir = cache_dir os.makedirs(cache_dir, exist_ok=True) def get_cache_file(self, date): return os.path.join(self.cache_dir, f'rates_{date}.pkl') def get_historical_rates(self, date): cache_file = self.get_cache_file(date) # Historical data never changes, so cache indefinitely if os.path.exists(cache_file): with open(cache_file, 'rb') as f: return pickle.load(f) # Fetch from API headers = {'Authorization': f'Bearer {self.api_key}'} response = requests.get(f'https://api.exchangeratesapi.com.au/{date}', headers=headers) data = response.json() if not data['success']: raise Exception(f"API Error: {data['error']['info']}") # Cache the result with open(cache_file, 'wb') as f: pickle.dump(data, f) return data def get_date_range(self, start_date, end_date): """Get rates for a date range using cached data where possible""" current_date = datetime.strptime(start_date, '%Y-%m-%d') end_date_obj = datetime.strptime(end_date, '%Y-%m-%d') results = {} while current_date <= end_date_obj: date_str = current_date.strftime('%Y-%m-%d') # Skip weekends (RBA doesn't publish rates) if current_date.weekday() < 5: # Monday=0, Sunday=6 try: rates = self.get_historical_rates(date_str) results[date_str] = rates except Exception as e: print(f"Failed to get rates for {date_str}: {e}") current_date += timedelta(days=1) return results ``` ### Redis Caching for High-Traffic Applications ```javascript theme={null} const redis = require('redis'); const client = redis.createClient(); class RedisRateCache { constructor(apiKey) { this.apiKey = apiKey; this.client = client; } async getLatestRates() { const cacheKey = 'exchange_rates:latest'; try { // Try cache first const cached = await this.client.get(cacheKey); if (cached) { return JSON.parse(cached); } } catch (error) { console.warn('Redis cache error, fetching from API:', error); } // Fetch from API const response = await fetch('https://api.exchangeratesapi.com.au/latest', { headers: { 'Authorization': `Bearer ${this.apiKey}` } }); const data = await response.json(); if (!data.success) { throw new Error(data.error.info); } // Cache for 30 minutes try { await this.client.setex(cacheKey, 1800, JSON.stringify(data)); } catch (error) { console.warn('Failed to cache rates:', error); } return data; } async getHistoricalRates(date) { const cacheKey = `exchange_rates:historical:${date}`; try { const cached = await this.client.get(cacheKey); if (cached) { return JSON.parse(cached); } } catch (error) { console.warn('Redis cache error:', error); } // Fetch from API const response = await fetch(`https://api.exchangeratesapi.com.au/${date}`, { headers: { 'Authorization': `Bearer ${this.apiKey}` } }); const data = await response.json(); if (!data.success) { throw new Error(data.error.info); } // Historical data never changes - cache indefinitely try { await this.client.set(cacheKey, JSON.stringify(data)); } catch (error) { console.warn('Failed to cache historical rates:', error); } return data; } } ``` ## Error Handling ### Comprehensive Error Handling Implement robust error handling for all API scenarios: ```javascript theme={null} class RobustExchangeRatesAPI { constructor(apiKey) { this.apiKey = apiKey; this.baseURL = 'https://api.exchangeratesapi.com.au'; this.maxRetries = 3; this.backoffMultiplier = 1.5; } async makeRequest(endpoint, retryCount = 0) { try { const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), 10000); // 10s timeout const response = await fetch(`${this.baseURL}${endpoint}`, { headers: { 'Authorization': `Bearer ${this.apiKey}` }, signal: controller.signal }); clearTimeout(timeoutId); if (!response.ok) { throw new Error(`HTTP ${response.status}: ${response.statusText}`); } const data = await response.json(); if (!data.success) { const error = new Error(data.error.info); error.code = data.error.code; error.type = data.error.type; throw error; } return data; } catch (error) { console.warn(`Request attempt ${retryCount + 1} failed:`, error.message); // Don't retry client errors (4xx) or authentication issues if (error.code >= 400 && error.code < 500) { throw error; } // Retry network errors and server errors (5xx) if (retryCount < this.maxRetries) { const delay = Math.pow(this.backoffMultiplier, retryCount) * 1000; console.log(`Retrying in ${delay}ms...`); await new Promise(resolve => setTimeout(resolve, delay)); return this.makeRequest(endpoint, retryCount + 1); } throw error; } } async getLatestRatesWithFallback() { try { return await this.makeRequest('/latest'); } catch (error) { console.error('All retry attempts failed:', error.message); // Try to return cached/fallback data const fallbackData = await this.getFallbackData(); if (fallbackData) { console.warn('Using fallback data due to API failure'); return { ...fallbackData, fallback: true }; } throw new Error('Unable to fetch exchange rates and no fallback data available'); } } async getFallbackData() { // Implement fallback logic (cache, database, static file, etc.) try { const cached = localStorage.getItem('last_known_rates'); if (cached) { const data = JSON.parse(cached); // Only use if less than 24 hours old if (Date.now() - data.timestamp < 86400000) { return data; } } } catch (error) { console.warn('Failed to load fallback data:', error); } return null; } } ``` ### Graceful Degradation Handle API failures gracefully in user interfaces: ```javascript theme={null} class CurrencyDisplayComponent { constructor(apiKey) { this.api = new RobustExchangeRatesAPI(apiKey); this.state = { rates: null, loading: false, error: null, fallbackMode: false }; } async loadRates() { this.setState({ loading: true, error: null }); try { const data = await this.api.getLatestRatesWithFallback(); this.setState({ rates: data, loading: false, fallbackMode: data.fallback || false }); // Cache successful responses if (!data.fallback) { localStorage.setItem('last_known_rates', JSON.stringify({ ...data, timestamp: Date.now() })); } } catch (error) { this.setState({ loading: false, error: error.message, // Keep existing rates if available rates: this.state.rates }); } } render() { const { rates, loading, error, fallbackMode } = this.state; if (loading) { return '
Loading exchange rates...
'; } if (error && !rates) { return `

Unable to load exchange rates: ${error}

`; } if (!rates) { return '
No exchange rate data available
'; } const warningBanner = fallbackMode ? `
⚠️ Showing cached data due to API issues. Rates may not be current.
` : ''; const staleWarning = rates.stale ? `
⚠️ Rate data is stale. RBA may not have updated as expected.
` : ''; return ` ${warningBanner} ${staleWarning}

Exchange Rates (${rates.date})

${Object.entries(rates.rates).map(([currency, rate]) => `
${currency} ${rate.toFixed(4)}
`).join('')}
`; } setState(newState) { this.state = { ...this.state, ...newState }; this.updateDOM(); } updateDOM() { const container = document.getElementById('currency-display'); if (container) { container.innerHTML = this.render(); } } } ``` ## Rate Limiting Management ### Quota Monitoring Track and manage your API quota usage: ```javascript theme={null} class QuotaManager { constructor(apiKey) { this.apiKey = apiKey; this.quotaInfo = { limit: null, remaining: null, reset: null }; } async makeRequest(endpoint) { // Check quota before making request if (this.quotaInfo.remaining !== null && this.quotaInfo.remaining <= 0) { const resetTime = new Date(this.quotaInfo.reset); if (new Date() < resetTime) { throw new Error(`Monthly quota exceeded. Resets at ${resetTime.toLocaleString()}`); } } const response = await fetch(`https://api.exchangeratesapi.com.au${endpoint}`, { headers: { 'Authorization': `Bearer ${this.apiKey}` } }); // Update quota info from response headers this.updateQuotaInfo(response.headers); if (!response.ok) { throw new Error(`HTTP ${response.status}: ${response.statusText}`); } const data = await response.json(); if (!data.success) { throw new Error(data.error.info); } return data; } updateQuotaInfo(headers) { // Quotas are monthly. The API returns X-RateLimit-*-Monthly plus a reset timestamp. const limit = headers.get('X-RateLimit-Limit-Monthly'); const remaining = headers.get('X-RateLimit-Remaining-Monthly'); const reset = headers.get('X-RateLimit-Reset'); if (limit) this.quotaInfo.limit = parseInt(limit); if (remaining) this.quotaInfo.remaining = parseInt(remaining); if (reset) this.quotaInfo.reset = reset; } getQuotaStatus() { return { ...this.quotaInfo, usagePercentage: this.quotaInfo.limit && this.quotaInfo.remaining ? ((this.quotaInfo.limit - this.quotaInfo.remaining) / this.quotaInfo.limit * 100).toFixed(1) : null }; } shouldWarnUser() { return this.quotaInfo.remaining !== null && this.quotaInfo.remaining < 10; } } // Usage const quotaManager = new QuotaManager('your_api_key_here'); try { const rates = await quotaManager.makeRequest('/latest'); console.log('Current rates:', rates); const quotaStatus = quotaManager.getQuotaStatus(); console.log(`Quota: ${quotaStatus.remaining}/${quotaStatus.limit} (${quotaStatus.usagePercentage}% used)`); if (quotaManager.shouldWarnUser()) { console.warn('Warning: Low quota remaining. Consider upgrading your plan.'); } } catch (error) { console.error('Request failed:', error.message); } ``` ### Request Batching Minimize API calls by batching multiple operations: ```javascript theme={null} class BatchProcessor { constructor(apiKey) { this.apiKey = apiKey; this.batchQueue = []; this.batchTimeout = null; this.batchDelay = 100; // ms } async getRate(currency, date = null) { return new Promise((resolve, reject) => { this.batchQueue.push({ type: 'rate', currency, date, resolve, reject }); this.scheduleBatch(); }); } scheduleBatch() { if (this.batchTimeout) return; this.batchTimeout = setTimeout(() => { this.processBatch(); this.batchTimeout = null; }, this.batchDelay); } async processBatch() { const batch = [...this.batchQueue]; this.batchQueue = []; // Group requests by type and date const groups = this.groupRequests(batch); for (const group of groups) { try { await this.processGroup(group); } catch (error) { group.requests.forEach(req => req.reject(error)); } } } groupRequests(batch) { const groups = new Map(); for (const request of batch) { const key = `${request.type}:${request.date || 'latest'}`; if (!groups.has(key)) { groups.set(key, { type: request.type, date: request.date, requests: [] }); } groups.get(key).requests.push(request); } return Array.from(groups.values()); } async processGroup(group) { let endpoint = group.date ? `/${group.date}` : '/latest'; const response = await fetch(`https://api.exchangeratesapi.com.au${endpoint}`, { headers: { 'Authorization': `Bearer ${this.apiKey}` } }); const data = await response.json(); if (!data.success) { throw new Error(data.error.info); } // Resolve all requests in the group group.requests.forEach(request => { const rate = data.rates[request.currency]; request.resolve({ currency: request.currency, rate, date: data.date, base: data.base }); }); } } // Usage const batcher = new BatchProcessor('your_api_key_here'); // These will be batched into a single API call const promises = [ batcher.getRate('USD'), batcher.getRate('EUR'), batcher.getRate('GBP'), batcher.getRate('JPY') ]; const results = await Promise.all(promises); console.log('All rates:', results); ``` ## Performance Optimization ### Connection Pooling For high-volume applications, use connection pooling: ```javascript theme={null} const https = require('https'); class PerformantAPIClient { constructor(apiKey) { this.apiKey = apiKey; // Create HTTPS agent with connection pooling this.agent = new https.Agent({ keepAlive: true, maxSockets: 10, maxFreeSockets: 5, timeout: 10000 }); } async makeRequest(endpoint) { return new Promise((resolve, reject) => { const options = { hostname: 'api.exchangeratesapi.com.au', port: 443, path: endpoint, method: 'GET', agent: this.agent, headers: { 'Authorization': `Bearer ${this.apiKey}`, 'User-Agent': 'MyApp/1.0' } }; const req = https.request(options, (res) => { let data = ''; res.on('data', chunk => data += chunk); res.on('end', () => { try { const parsed = JSON.parse(data); resolve(parsed); } catch (error) { reject(new Error('Invalid JSON response')); } }); }); req.on('error', reject); req.setTimeout(10000, () => { req.destroy(); reject(new Error('Request timeout')); }); req.end(); }); } destroy() { this.agent.destroy(); } } ``` ### Compression Enable gzip compression for large responses: ```javascript theme={null} const response = await fetch('https://api.exchangeratesapi.com.au/timeseries?start_date=2024-01-01&end_date=2024-12-31', { headers: { 'Authorization': `Bearer ${apiKey}`, 'Accept-Encoding': 'gzip, deflate' } }); ``` ## Monitoring and Logging ### Request Monitoring Track API performance and reliability: ```javascript theme={null} class APIMonitor { constructor(apiKey) { this.apiKey = apiKey; this.metrics = { totalRequests: 0, successfulRequests: 0, failedRequests: 0, averageResponseTime: 0, responseTimes: [] }; } async monitoredRequest(endpoint) { const startTime = Date.now(); this.metrics.totalRequests++; try { const response = await fetch(`https://api.exchangeratesapi.com.au${endpoint}`, { headers: { 'Authorization': `Bearer ${this.apiKey}` } }); const responseTime = Date.now() - startTime; this.recordResponseTime(responseTime); const data = await response.json(); if (data.success) { this.metrics.successfulRequests++; this.logSuccess(endpoint, responseTime); } else { this.metrics.failedRequests++; this.logError(endpoint, data.error, responseTime); } return data; } catch (error) { const responseTime = Date.now() - startTime; this.metrics.failedRequests++; this.recordResponseTime(responseTime); this.logError(endpoint, error, responseTime); throw error; } } recordResponseTime(time) { this.metrics.responseTimes.push(time); // Keep only last 100 response times for rolling average if (this.metrics.responseTimes.length > 100) { this.metrics.responseTimes.shift(); } this.metrics.averageResponseTime = this.metrics.responseTimes.reduce((a, b) => a + b, 0) / this.metrics.responseTimes.length; } logSuccess(endpoint, responseTime) { console.log(`✅ ${endpoint} - ${responseTime}ms`); } logError(endpoint, error, responseTime) { console.error(`❌ ${endpoint} - ${responseTime}ms - ${error.info || error.message}`); } getHealthReport() { const successRate = (this.metrics.successfulRequests / this.metrics.totalRequests * 100).toFixed(2); return { ...this.metrics, successRate: `${successRate}%`, averageResponseTime: `${this.metrics.averageResponseTime.toFixed(0)}ms` }; } } // Usage const monitor = new APIMonitor('your_api_key_here'); // Monitor requests const rates = await monitor.monitoredRequest('/latest'); // Generate health report const report = monitor.getHealthReport(); console.log('API Health Report:', report); ``` ## Testing Strategies ### Unit Testing Mock the API for reliable unit tests: ```javascript theme={null} // mock-exchange-rates.js class MockExchangeRatesAPI { constructor() { this.mockData = { latest: { success: true, timestamp: 1725080400, base: 'AUD', date: '2025-08-31', rates: { USD: 0.643512, EUR: 0.562934, GBP: 0.487421 } } }; } async getLatestRates() { return this.mockData.latest; } setMockData(data) { this.mockData.latest = data; } simulateError(error) { this.shouldError = error; } async convert(from, to, amount) { if (this.shouldError) { throw this.shouldError; } const rate = this.mockData.latest.rates[to]; return { success: true, query: { from, to, amount }, info: { rate }, result: amount * rate }; } } // tests/currency-converter.test.js const assert = require('assert'); const CurrencyConverter = require('../src/currency-converter'); const MockExchangeRatesAPI = require('./mock-exchange-rates'); describe('Currency Converter', () => { let converter, mockAPI; beforeEach(() => { mockAPI = new MockExchangeRatesAPI(); converter = new CurrencyConverter(mockAPI); }); it('should convert AUD to USD correctly', async () => { const result = await converter.convert('AUD', 'USD', 100); assert.strictEqual(result.result, 64.3512); }); it('should handle API errors gracefully', async () => { mockAPI.simulateError(new Error('Rate limit exceeded')); try { await converter.convert('AUD', 'USD', 100); assert.fail('Should have thrown an error'); } catch (error) { assert.strictEqual(error.message, 'Rate limit exceeded'); } }); it('should use cached rates when API fails', async () => { // Set up cache converter.setCachedRate('USD', 0.65); // Simulate API failure mockAPI.simulateError(new Error('Network error')); const result = await converter.convertWithFallback('AUD', 'USD', 100); assert.strictEqual(result.result, 65); assert.strictEqual(result.fromCache, true); }); }); ``` This comprehensive best practices guide covers all the essential aspects of building robust, performant, and secure applications with the Exchange Rates API. Following these patterns will help you create reliable currency conversion systems that handle edge cases gracefully and provide excellent user experiences. # Rate Limits Source: https://docs.exchangeratesapi.com.au/guides/rate-limits Understanding API quotas, rate limits, and plan tiers for the Exchange Rates API ## Rate Limit Overview The Exchange Rates API uses a **monthly quota system** with different limits based on your subscription plan. Rate limits ensure fair usage and optimal performance for all users. All rate limits are calculated based on your **billing cycle** and reset on your monthly renewal date. Exception: the Free tier's 150 requests are a one-time trial allowance and do not reset - upgrade to a paid plan for a recurring monthly quota. ## Plan Comparison | Plan | Price (AUD/month) | Monthly Requests | API Keys | Historical Data | Timeseries Max Range | Support | | ---------------- | ----------------- | ---------------- | -------- | --------------- | -------------------- | -------------- | | **Free** | \$0 | 150 (one-time) | 1 | None | No access | Community | | **Starter** | \$29 | 5,000 | 1 | Last 30 days | 7 days | Email (48h) | | **Professional** | \$79 | 50,000 | 5 | Full (2018+) | 90 days | Email (24h) | | **Business** | \$299 | 500,000 | 10 | Full (2018+) | 365 days | Phone (4h) | | **Enterprise** | Custom | 5,000,000 | 20 | Full (2018+) | 365 days | Dedicated (1h) | > The Free tier's 150 requests are a one-time trial allowance (no monthly reset). Paid plans reset monthly on your billing date. ### Free Tier Details The free tier is perfect for: * Testing and development * Personal projects * Small applications with low traffic * Proof of concept implementations **Limitations:** * 150 requests total (one-time trial allowance, does not reset monthly) * No historical data access * No timeseries endpoint access * Single API key only * Community support only * No SLA guarantees ### Paid Plans Benefits All paid plans include: * Higher monthly quotas * Historical data access * Priority support * 99.9% uptime SLA * Advanced features (webhooks, CSV exports)\* \*Some features coming soon ## API Keys per Plan Free and Starter are **single-key plans**: your account holds one active API key at a time, and generating a new key replaces the existing one. Professional and above support multiple concurrent keys, which lets you issue a separate key per team, application, or environment and revoke any one of them independently. On a single-key plan, generating a new key immediately invalidates the old one. Plan for a brief cutover, or upgrade to Professional if you need overlapping keys for zero-downtime rotation. See [Key Rotation](/authentication#key-rotation). ## Webhook Limits Webhooks are available on Professional and above. Each plan allows a maximum number of active webhook subscriptions: | Plan | Webhook Subscriptions | | ---------------- | --------------------- | | **Free** | Not available | | **Starter** | Not available | | **Professional** | 3 | | **Business** | 10 | | **Enterprise** | 50 | See the [Webhooks overview](/api-reference/webhooks/overview) for setup and payload details. ## Rate Limit Headers Every API response includes rate limit information in the headers: ```bash theme={null} curl -i https://api.exchangeratesapi.com.au/latest \ -H "Authorization: Bearer your_api_key_here" ``` ```http theme={null} HTTP/2 200 X-RateLimit-Limit-Monthly: 5000 X-RateLimit-Remaining-Monthly: 4847 X-RateLimit-Reset: 2025-10-01T00:00:00Z X-Request-Id: 123e4567-e89b-12d3-a456-426614174000 ``` | Header | Description | | ------------------------------- | ------------------------------------------ | | `X-RateLimit-Limit-Monthly` | Total monthly quota for your plan | | `X-RateLimit-Remaining-Monthly` | Remaining requests this month | | `X-RateLimit-Reset` | When the quota resets (next billing cycle) | | `X-Request-Id` | Unique identifier for debugging | ## Free Endpoint Rate Limits Some endpoints don't require authentication but have IP-based rate limits: | Endpoint | Limit | Reset Period | | -------------- | --------- | ------------ | | `GET /status` | Unlimited | - | | `GET /symbols` | Unlimited | - | ## Rate Limit Exceeded Responses ### Monthly Quota Exceeded (Authenticated) ```json theme={null} { "success": false, "error": { "code": 429, "type": "rate_limit_exceeded", "info": "Monthly quota of 5000 requests exceeded. Quota resets on your next billing cycle." } } ``` ### IP Rate Limit Exceeded (Free Endpoints) ```json theme={null} { "success": false, "error": { "code": 429, "type": "rate_limit_exceeded", "info": "Rate limit exceeded. Try again in 3456 seconds." } } ``` ## Monitoring Your Usage ### Dashboard Analytics Track your API usage in the [dashboard](https://app.exchangeratesapi.com.au): * **Monthly Usage**: Requests made this billing cycle * **Historical Usage**: 12-month usage chart * **Top Endpoints**: Most frequently used endpoints * **Response Times**: Average API performance * **Error Rates**: Success vs error rates ## Rate Limit Best Practices ### 1. Implement Client-Side Throttling ```javascript JavaScript theme={null} class ExchangeRatesAPI { constructor(apiKey) { this.apiKey = apiKey; this.baseURL = 'https://api.exchangeratesapi.com.au'; this.remainingRequests = null; this.quotaResetTime = null; } async makeRequest(endpoint) { // Check if we have quota remaining if (this.remainingRequests !== null && this.remainingRequests <= 0) { const now = new Date(); if (now < new Date(this.quotaResetTime)) { throw new Error('Monthly quota exceeded. Try again after your billing cycle resets.'); } } const response = await fetch(`${this.baseURL}${endpoint}`, { headers: { 'Authorization': `Bearer ${this.apiKey}` } }); // Update rate limit info from headers this.remainingRequests = parseInt( response.headers.get('X-RateLimit-Remaining-Monthly') ); this.quotaResetTime = response.headers.get('X-RateLimit-Reset'); if (!response.ok) { const error = await response.json(); throw new Error(error.error.info); } return response.json(); } } ``` ```python Python theme={null} import requests import time from datetime import datetime class ExchangeRatesAPI: def __init__(self, api_key): self.api_key = api_key self.base_url = 'https://api.exchangeratesapi.com.au' self.remaining_requests = None self.quota_reset_time = None self.headers = {'Authorization': f'Bearer {api_key}'} def make_request(self, endpoint): # Check quota before making request if (self.remaining_requests is not None and self.remaining_requests <= 0): if datetime.now() < datetime.fromisoformat( self.quota_reset_time.replace('Z', '+00:00') ): raise Exception('Monthly quota exceeded. Try again after your billing cycle resets.') response = requests.get(f'{self.base_url}{endpoint}', headers=self.headers) # Update rate limit info self.remaining_requests = int( response.headers.get('X-RateLimit-Remaining-Monthly', 0) ) self.quota_reset_time = response.headers.get('X-RateLimit-Reset') if not response.ok: error_data = response.json() raise Exception(error_data['error']['info']) return response.json() ``` ### 2. Cache Responses Intelligently RBA data updates once daily at 4 PM AEST, so cache aggressively to minimize monthly quota usage: ```javascript theme={null} class CachedExchangeRates { constructor(apiKey) { this.api = new ExchangeRatesAPI(apiKey); this.cache = new Map(); this.cacheExpiry = new Map(); } async getLatestRates() { const cacheKey = 'latest'; const now = Date.now(); // Check if cached data is still fresh if (this.cache.has(cacheKey) && this.cacheExpiry.get(cacheKey) > now) { return this.cache.get(cacheKey); } // Fetch fresh data const data = await this.api.makeRequest('/latest'); // Cache until next RBA update (4 PM AEST next day) const tomorrow4PM = new Date(); tomorrow4PM.setDate(tomorrow4PM.getDate() + 1); tomorrow4PM.setHours(16, 0, 0, 0); // 4 PM this.cache.set(cacheKey, data); this.cacheExpiry.set(cacheKey, tomorrow4PM.getTime()); return data; } } ``` ### 3. Handle Rate Limit Errors Gracefully ```javascript theme={null} async function robustApiCall(apiClient, endpoint) { try { return await apiClient.makeRequest(endpoint); } catch (error) { if (error.message.includes('quota exceeded')) { // Log the issue and return cached data if available console.warn('Monthly API quota exceeded, using cached data'); return getCachedData(endpoint); } if (error.message.includes('rate limit')) { // Wait and retry for temporary rate limits await sleep(60000); // Wait 1 minute return apiClient.makeRequest(endpoint); } throw error; // Re-throw other errors } } ``` ### 4. Batch Requests Efficiently Instead of multiple single-currency requests, use batch endpoints: ```javascript theme={null} // Bad - Multiple requests const usdRate = await api.makeRequest('/latest/USD'); // 1 request const eurRate = await api.makeRequest('/latest/EUR'); // 1 request const gbpRate = await api.makeRequest('/latest/GBP'); // 1 request // Total: 3 requests // Good - Single batch request const allRates = await api.makeRequest('/latest'); // 1 request const usdRate = allRates.rates.USD; const eurRate = allRates.rates.EUR; const gbpRate = allRates.rates.GBP; // Total: 1 request ``` ## Upgrade Options ### When to Upgrade Consider upgrading when you: * Consistently hit monthly quotas * Need historical data access * Require faster support response * Want SLA guarantees * Need advanced features ### Upgrade Process Compare plans on our [pricing page](https://www.exchangeratesapi.com.au/pricing) Visit your [dashboard](https://app.exchangeratesapi.com.au) and click "Upgrade" New quotas take effect immediately after payment Your existing API keys continue working with higher limits # Troubleshooting Source: https://docs.exchangeratesapi.com.au/guides/troubleshooting Common issues and solutions for the Exchange Rates API ## Authentication Errors ### Invalid API Key (401) **Error Response:** ```json theme={null} { "success": false, "error": { "code": 401, "type": "invalid_api_key", "info": "Invalid or missing API key." } } ``` **Fix:** 1. Check API key format: `buderim_abc123...` 2. Use correct header: `Authorization: Bearer your_api_key` 3. Verify key is active in dashboard ### Account Suspended (401) **Error Response:** ```json theme={null} { "success": false, "error": { "code": 401, "type": "account_suspended", "info": "Account is suspended. Please contact support." } } ``` **Causes:** * Overdue payment * Terms violation * Billing issues **Fix:** Check dashboard and contact support if needed. ## Rate Limits ### Monthly Quota Exceeded (429) **Error Response:** ```json theme={null} { "success": false, "error": { "code": 429, "type": "rate_limit_exceeded", "info": "Monthly quota of 5000 requests exceeded. Quota resets on your next billing cycle." } } ``` **Fix:** * Wait until next billing cycle for reset * Cache responses (rates update once daily at 4 PM AEST) * Upgrade plan for higher quota ### Free Plan Limit (429) The free plan allows 150 requests across authenticated endpoints. **Fix:** * Wait until the next monthly reset * Upgrade for higher limits ## Data Issues ### Stale Data **Indicators:** * `X-Data-Stale: true` header * `"stale": true` in response **Cause:** RBA hasn't updated (weekends, holidays, technical issues) **Action:** No action needed - system recovers when RBA updates. ### Historical Data Not Found (404) **Error Response:** ```json theme={null} { "success": false, "error": { "code": 404, "type": "not_found", "info": "No data available for date 2017-12-31" } } ``` **Causes:** * Date before 2018-01-01 * Weekend (RBA publishes Monday-Friday only) * Australian public holiday * Future date ## Technical Issues ### Network Timeouts **Fix:** 1. Check internet connection 2. Test: `curl -I https://api.exchangeratesapi.com.au/status` 3. Check firewall allows HTTPS (port 443) 4. Implement retry with exponential backoff ### CORS Errors **Cause:** Browser security blocks direct API calls with authentication. **Fix:** * Use a backend proxy for API calls * Never put API keys in frontend code * For quick checks, the public `/status` and `/symbols` endpoints require no key ## Quick Diagnostics ### 1. Test Status ```bash theme={null} curl https://api.exchangeratesapi.com.au/status ``` Should return `"status": "operational"` ### 2. Test Authentication ```bash theme={null} curl -H "Authorization: Bearer your_api_key" \ https://api.exchangeratesapi.com.au/latest ``` Should return rate data ### 3. Check Rate Limits Look for headers: ``` X-RateLimit-Limit-Monthly: 5000 X-RateLimit-Remaining-Monthly: 4847 ``` ## Contacting Support Include: * Email address and plan type * Exact error message * Request ID from `X-Request-Id` header * Sample cURL command (API key redacted) Contact us at [https://www.exchangeratesapi.com.au/contact](https://www.exchangeratesapi.com.au/contact) # Exchange Rates API Source: https://docs.exchangeratesapi.com.au/introduction Welcome to the Exchange Rates API - programmatic access to Official Reserve Bank of Australia exchange rate data through REST API endpoints Background Light Background Dark The Exchange Rates API is a developer-friendly data service providing **official Reserve Bank of Australia (RBA) exchange rate data** through clean, modern REST API endpoints and webhooks. We transform publicly available RBA data (currently only accessible via HTML/RSS) into instant JSON responses. Direct from the Reserve Bank of Australia, updated daily Clean JSON responses with comprehensive error handling Complete history from 2018 onwards with 30,000+ data points 150 requests to get started (one-time trial), no credit card required ## Why Exchange Rates API? The Reserve Bank of Australia publishes official exchange rates daily for free, but only provides this data in: * HTML tables requiring web scraping * RSS/XML feeds with complex RDF namespaces * Excel files for historical data Australian businesses need this **official RBA data** for: * Tax compliance and ATO reporting * Invoice calculations for imports/exports * Financial reporting and auditing * E-commerce currency conversion * Accounting software integrations All data is sourced from the Reserve Bank of Australia. We are not affiliated with or endorsed by the RBA. ## Supported Currencies We support all currencies published by the RBA: * **USD** - US Dollar * **EUR** - Euro * **GBP** - British Pound Sterling * **JPY** - Japanese Yen * **CHF** - Swiss Franc * **CAD** - Canadian Dollar * **CNY** - Chinese Renminbi * **KRW** - South Korean Won * **SGD** - Singapore Dollar * **NZD** - New Zealand Dollar * **HKD** - Hong Kong Dollar * **TWD** - Taiwan New Dollar * **INR** - Indian Rupee * **THB** - Thai Baht * **MYR** - Malaysian Ringgit * **IDR** - Indonesian Rupiah * **VND** - Vietnamese Dong * **PHP** - Philippine Peso ## Quick Example ```bash cURL theme={null} curl https://api.exchangeratesapi.com.au/latest \ -H "Authorization: Bearer your_api_key_here" ``` ```javascript JavaScript theme={null} const response = await fetch('https://api.exchangeratesapi.com.au/latest', { headers: { 'Authorization': 'Bearer your_api_key_here' } }); const data = await response.json(); console.log(`1 AUD = ${data.rates.USD} USD`); ``` ```python Python theme={null} import requests response = requests.get( 'https://api.exchangeratesapi.com.au/latest', headers={'Authorization': 'Bearer your_api_key_here'} ) data = response.json() print(f"1 AUD = {data['rates']['USD']} USD") ``` ```php PHP theme={null} 'https://api.exchangeratesapi.com.au/latest', CURLOPT_HTTPHEADER => array( 'Authorization: Bearer your_api_key_here' ), )); $response = curl_exec($curl); $data = json_decode($response, true); echo "1 AUD = " . $data['rates']['USD'] . " USD"; ?> ``` ## Response Format ```json theme={null} { "success": true, "timestamp": 1725080400, "base": "AUD", "date": "2025-08-31", "rates": { "USD": 0.643512, "EUR": 0.562934, "GBP": 0.487421, "JPY": 96.832100, "NZD": 1.094200, "TWI": 60.500000 } } ``` ## Ready to Get Started? Sign up for free and start making API calls in under 2 minutes Get pushed a signed notification the moment each day's rates publish - no polling # Quickstart Source: https://docs.exchangeratesapi.com.au/quickstart Get up and running with the Exchange Rates API in under 2 minutes ## Get Your API Key Visit [app.exchangeratesapi.com.au](https://app.exchangeratesapi.com.au) and sign up using your email address. No credit card required for the free tier (150 requests to get started) Check your email inbox and click the magic link to verify your account. This will automatically log you into the dashboard. In the dashboard, click **"Generate New API Key"**. Your API key will have a unique prefix (e.g., `buderim_abc123...`). Copy your API key immediately - it's only shown once for security reasons Test your API key with a simple request to get the latest exchange rates: ```bash theme={null} curl https://api.exchangeratesapi.com.au/latest \ -H "Authorization: Bearer your_api_key_here" ``` ## Your First API Call Let's start with the most common use case - getting current exchange rates: ```bash cURL theme={null} curl https://api.exchangeratesapi.com.au/latest \ -H "Authorization: Bearer your_api_key_here" ``` ```javascript JavaScript theme={null} const response = await fetch('https://api.exchangeratesapi.com.au/latest', { headers: { 'Authorization': 'Bearer your_api_key_here' } }); const data = await response.json(); if (data.success) { console.log(`Current USD rate: ${data.rates.USD}`); console.log(`Data from: ${data.date}`); } else { console.error('API Error:', data.error); } ``` ```python Python theme={null} import requests headers = { 'Authorization': 'Bearer your_api_key_here' } response = requests.get( 'https://api.exchangeratesapi.com.au/latest', headers=headers ) data = response.json() if data['success']: print(f"Current USD rate: {data['rates']['USD']}") print(f"Data from: {data['date']}") else: print(f"API Error: {data['error']['info']}") ``` ```php PHP theme={null} 'https://api.exchangeratesapi.com.au/latest', CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ 'Authorization: Bearer ' . $api_key ], ]); $response = curl_exec($curl); curl_close($curl); $data = json_decode($response, true); if ($data['success']) { echo "Current USD rate: " . $data['rates']['USD'] . "\n"; echo "Data from: " . $data['date'] . "\n"; } else { echo "API Error: " . $data['error']['info'] . "\n"; } ?> ``` ### Expected Response ```json theme={null} { "success": true, "timestamp": 1725080400, "base": "AUD", "date": "2025-08-31", "rates": { "USD": 0.643512, "EUR": 0.562934, "GBP": 0.487421, "JPY": 96.832100, "NZD": 1.094200, "CAD": 0.893421, "CHF": 0.578234, "CNY": 4.623451, "TWI": 60.500000 } } ``` ## Currency Conversion Convert amounts between currencies: ```bash cURL theme={null} curl "https://api.exchangeratesapi.com.au/convert?from=AUD&to=USD&amount=100" \ -H "Authorization: Bearer your_api_key_here" ``` ```javascript JavaScript theme={null} const amount = 100; const from = 'AUD'; const to = 'USD'; const response = await fetch( `https://api.exchangeratesapi.com.au/convert?from=${from}&to=${to}&amount=${amount}`, { headers: { 'Authorization': 'Bearer your_api_key_here' } } ); const data = await response.json(); console.log(`${amount} ${from} = ${data.result} ${to}`); ``` ```python Python theme={null} import requests params = { 'from': 'AUD', 'to': 'USD', 'amount': 100 } headers = { 'Authorization': 'Bearer your_api_key_here' } response = requests.get( 'https://api.exchangeratesapi.com.au/convert', params=params, headers=headers ) data = response.json() print(f"{params['amount']} {params['from']} = {data['result']} {params['to']}") ``` ```php PHP theme={null} 'AUD', 'to' => 'USD', 'amount' => 100 ]); $curl = curl_init(); curl_setopt_array($curl, [ CURLOPT_URL => 'https://api.exchangeratesapi.com.au/convert?' . $params, CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ 'Authorization: Bearer your_api_key_here' ], ]); $response = curl_exec($curl); $data = json_decode($response, true); echo "100 AUD = " . $data['result'] . " USD"; ?> ``` ### Conversion Response ```json theme={null} { "success": true, "query": { "from": "AUD", "to": "USD", "amount": 100 }, "info": { "timestamp": 1725080400, "rate": 0.643512 }, "date": "2025-08-31", "result": 64.3512 } ``` ## Historical Data Get exchange rates from any date since 2018: ```bash cURL theme={null} curl https://api.exchangeratesapi.com.au/2024-12-01 \ -H "Authorization: Bearer your_api_key_here" ``` ```javascript JavaScript theme={null} const historicalDate = '2024-12-01'; const response = await fetch( `https://api.exchangeratesapi.com.au/${historicalDate}`, { headers: { 'Authorization': 'Bearer your_api_key_here' } } ); const data = await response.json(); console.log(`USD rate on ${historicalDate}: ${data.rates.USD}`); ``` ```python Python theme={null} import requests historical_date = '2024-12-01' headers = { 'Authorization': 'Bearer your_api_key_here' } response = requests.get( f'https://api.exchangeratesapi.com.au/{historical_date}', headers=headers ) data = response.json() print(f"USD rate on {historical_date}: {data['rates']['USD']}") ``` ## Error Handling Always check the `success` field in responses: ```javascript theme={null} const response = await fetch('https://api.exchangeratesapi.com.au/latest', { headers: { 'Authorization': 'Bearer your_api_key_here' } }); const data = await response.json(); if (!data.success) { switch (data.error.code) { case 401: console.error('Invalid API key'); break; case 429: console.error('Rate limit exceeded'); break; case 400: console.error('Bad request:', data.error.info); break; default: console.error('API error:', data.error.info); } } else { // Process successful response console.log('Current rates:', data.rates); } ``` ## Free Tier Testing Want to try the API at no cost? Sign up for a [free API key](https://app.exchangeratesapi.com.au/login) (150 requests) and use it on any endpoint, including `/convert`: ```bash theme={null} curl "https://api.exchangeratesapi.com.au/convert?from=AUD&to=USD&amount=100" \ -H "Authorization: Bearer your_api_key_here" ``` ## Next Steps Explore all available endpoints and parameters Learn about API key security and best practices Understand plan limits and upgrade options Use our official SDKs for popular languages # cURL Examples Source: https://docs.exchangeratesapi.com.au/sdks/curl Command-line examples and shell scripts for the Exchange Rates API using cURL ## Quick Examples ```bash Basic Request theme={null} curl -H "Authorization: Bearer your_api_key_here" \ https://api.exchangeratesapi.com.au/latest ``` ```bash With Headers theme={null} curl -v -H "Authorization: Bearer your_api_key_here" \ -H "User-Agent: MyApp/1.0" \ https://api.exchangeratesapi.com.au/latest ``` ```bash Status Check theme={null} curl https://api.exchangeratesapi.com.au/status ``` The official cURL SDK/wrapper is coming soon! For now, use the native cURL examples below or create your own shell scripts. ## Authentication ### Bearer Token ```bash theme={null} # Set your API key as an environment variable export EXCHANGE_RATES_API_KEY="buderim_your_api_key_here" # Use in requests curl -H "Authorization: Bearer $EXCHANGE_RATES_API_KEY" \ https://api.exchangeratesapi.com.au/latest ``` ### Response Format ```json theme={null} { "success": true, "timestamp": 1725148800, "base": "AUD", "date": "2025-09-01", "rates": { "USD": 0.643512, "EUR": 0.582341, "GBP": 0.492847 } } ``` ## Endpoints ### Latest Rates ```bash theme={null} # All currencies curl -H "Authorization: Bearer $EXCHANGE_RATES_API_KEY" \ https://api.exchangeratesapi.com.au/latest # Specific currency curl -H "Authorization: Bearer $EXCHANGE_RATES_API_KEY" \ https://api.exchangeratesapi.com.au/latest/USD ``` ### Currency Conversion ```bash theme={null} # Convert AUD to USD curl -H "Authorization: Bearer $EXCHANGE_RATES_API_KEY" \ "https://api.exchangeratesapi.com.au/convert?from=AUD&to=USD&amount=100" # Historical conversion curl -H "Authorization: Bearer $EXCHANGE_RATES_API_KEY" \ "https://api.exchangeratesapi.com.au/convert?from=AUD&to=USD&amount=100&date=2024-01-01" ``` ### Historical Rates ```bash theme={null} # All currencies for specific date curl -H "Authorization: Bearer $EXCHANGE_RATES_API_KEY" \ https://api.exchangeratesapi.com.au/2024-01-01 # Specific currency for specific date curl -H "Authorization: Bearer $EXCHANGE_RATES_API_KEY" \ https://api.exchangeratesapi.com.au/2024-01-01/USD ``` ### Time Series Data ```bash theme={null} # All currencies over date range curl -H "Authorization: Bearer $EXCHANGE_RATES_API_KEY" \ "https://api.exchangeratesapi.com.au/timeseries?start_date=2024-01-01&end_date=2024-01-31" # Specific currencies curl -H "Authorization: Bearer $EXCHANGE_RATES_API_KEY" \ "https://api.exchangeratesapi.com.au/timeseries?start_date=2024-01-01&end_date=2024-01-31&symbols=USD,EUR,GBP" ``` ## Shell Scripts ### Rate Checker Script ```bash theme={null} #!/bin/bash # rate-checker.sh - Check current exchange rates set -e API_KEY="${EXCHANGE_RATES_API_KEY:-}" BASE_URL="https://api.exchangeratesapi.com.au" if [ -z "$API_KEY" ]; then echo "Error: EXCHANGE_RATES_API_KEY environment variable not set" exit 1 fi # Function to make API requests make_request() { local endpoint="$1" curl -s -H "Authorization: Bearer $API_KEY" \ -H "User-Agent: RateChecker/1.0" \ "$BASE_URL$endpoint" } # Check API status echo "Checking API status..." status_response=$(make_request "/status") echo "$status_response" | jq -r '.status // "unknown"' # Get latest rates echo -e "\nFetching latest rates..." rates_response=$(make_request "/latest") if echo "$rates_response" | jq -e '.success' > /dev/null; then echo "Base currency: $(echo "$rates_response" | jq -r '.base')" echo "Date: $(echo "$rates_response" | jq -r '.date')" echo -e "\nMajor currencies:" for currency in USD EUR GBP JPY; do rate=$(echo "$rates_response" | jq -r ".rates.$currency") printf " %-3s: %s\n" "$currency" "$rate" done else echo "Error: $(echo "$rates_response" | jq -r '.error.info')" exit 1 fi ``` ### Currency Converter Script ```bash theme={null} #!/bin/bash # converter.sh - Convert between currencies set -e API_KEY="${EXCHANGE_RATES_API_KEY:-}" BASE_URL="https://api.exchangeratesapi.com.au" if [ "$#" -ne 3 ]; then echo "Usage: $0 " echo "Example: $0 AUD USD 100" exit 1 fi FROM_CURRENCY="$1" TO_CURRENCY="$2" AMOUNT="$3" if [ -z "$API_KEY" ]; then echo "Error: EXCHANGE_RATES_API_KEY environment variable not set" exit 1 fi # Validate amount is numeric if ! [[ "$AMOUNT" =~ ^[0-9]+(\.[0-9]+)?$ ]]; then echo "Error: Amount must be a number" exit 1 fi # Make conversion request response=$(curl -s -H "Authorization: Bearer $API_KEY" \ -H "User-Agent: CurrencyConverter/1.0" \ "$BASE_URL/convert?from=$FROM_CURRENCY&to=$TO_CURRENCY&amount=$AMOUNT") if echo "$response" | jq -e '.success' > /dev/null; then result=$(echo "$response" | jq -r '.result') rate=$(echo "$response" | jq -r '.info.rate') date=$(echo "$response" | jq -r '.date') echo "Conversion Result:" echo " $AMOUNT $FROM_CURRENCY = $result $TO_CURRENCY" echo " Exchange Rate: $rate (as of $date)" else error_info=$(echo "$response" | jq -r '.error.info') echo "Error: $error_info" exit 1 fi ``` ### Daily Rate Logger ```bash theme={null} #!/bin/bash # daily-logger.sh - Log daily exchange rates to CSV set -e API_KEY="${EXCHANGE_RATES_API_KEY:-}" BASE_URL="https://api.exchangeratesapi.com.au" LOG_FILE="${1:-exchange_rates.csv}" if [ -z "$API_KEY" ]; then echo "Error: EXCHANGE_RATES_API_KEY environment variable not set" exit 1 fi # Create CSV header if file doesn't exist if [ ! -f "$LOG_FILE" ]; then echo "Date,USD,EUR,GBP,JPY,CNY,Timestamp" > "$LOG_FILE" fi # Fetch latest rates response=$(curl -s -H "Authorization: Bearer $API_KEY" \ -H "User-Agent: DailyLogger/1.0" \ "$BASE_URL/latest") if echo "$response" | jq -e '.success' > /dev/null; then date=$(echo "$response" | jq -r '.date') timestamp=$(echo "$response" | jq -r '.timestamp') usd=$(echo "$response" | jq -r '.rates.USD') eur=$(echo "$response" | jq -r '.rates.EUR') gbp=$(echo "$response" | jq -r '.rates.GBP') jpy=$(echo "$response" | jq -r '.rates.JPY') cny=$(echo "$response" | jq -r '.rates.CNY') # Append to CSV echo "$date,$usd,$eur,$gbp,$jpy,$cny,$timestamp" >> "$LOG_FILE" echo "Logged rates for $date to $LOG_FILE" else error_info=$(echo "$response" | jq -r '.error.info') echo "Error: $error_info" exit 1 fi ``` ## Error Handling ### Check Response Status ```bash theme={null} # Function to check if response is successful check_response() { local response="$1" if echo "$response" | jq -e '.success' > /dev/null; then return 0 # Success else local error_code=$(echo "$response" | jq -r '.error.code // 0') local error_info=$(echo "$response" | jq -r '.error.info // "Unknown error"') echo "API Error $error_code: $error_info" >&2 return 1 # Failure fi } # Usage response=$(curl -s -H "Authorization: Bearer $API_KEY" \ "$BASE_URL/latest") if check_response "$response"; then echo "Request successful!" # Process response... else echo "Request failed" exit 1 fi ``` ### Rate Limit Headers ```bash theme={null} # Check rate limit headers check_rate_limits() { curl -i -H "Authorization: Bearer $API_KEY" \ "$BASE_URL/latest" | head -20 } # Extract specific headers get_remaining_requests() { curl -s -I -H "Authorization: Bearer $API_KEY" \ "$BASE_URL/latest" | \ grep -i "x-ratelimit-remaining-daily" | \ cut -d: -f2 | tr -d ' \r' } echo "Remaining requests today: $(get_remaining_requests)" ``` ## Advanced Usage ### Batch Processing ```bash theme={null} #!/bin/bash # batch-historical.sh - Get historical rates for multiple dates API_KEY="${EXCHANGE_RATES_API_KEY:-}" BASE_URL="https://api.exchangeratesapi.com.au" # Array of dates to fetch dates=( "2024-01-01" "2024-01-15" "2024-01-31" ) currencies=("USD" "EUR" "GBP") echo "Date,Currency,Rate" for date in "${dates[@]}"; do response=$(curl -s -H "Authorization: Bearer $API_KEY" \ "$BASE_URL/$date") if echo "$response" | jq -e '.success' > /dev/null; then for currency in "${currencies[@]}"; do rate=$(echo "$response" | jq -r ".rates.$currency") echo "$date,$currency,$rate" done else echo "Error fetching $date: $(echo "$response" | jq -r '.error.info')" >&2 fi # Be nice to the API sleep 0.1 done ``` ### JSON Processing ```bash theme={null} # Pretty print JSON response curl -H "Authorization: Bearer $API_KEY" \ https://api.exchangeratesapi.com.au/latest | jq '.' # Extract specific values USD_RATE=$(curl -s -H "Authorization: Bearer $API_KEY" \ https://api.exchangeratesapi.com.au/latest | \ jq -r '.rates.USD') echo "Current USD rate: $USD_RATE" # Filter currencies curl -s -H "Authorization: Bearer $API_KEY" \ https://api.exchangeratesapi.com.au/latest | \ jq '{date: .date, major_currencies: {USD: .rates.USD, EUR: .rates.EUR, GBP: .rates.GBP}}' ``` ## Next Steps Python examples and best practices PHP examples and Laravel integration JavaScript/Node.js examples and React hooks Complete API documentation # JavaScript SDK Source: https://docs.exchangeratesapi.com.au/sdks/javascript Official JavaScript/TypeScript SDK for the Exchange Rates API with examples for Node.js and browser environments ## Installation ```bash npm theme={null} npm install @exchangeratesapi/javascript ``` ```bash yarn theme={null} yarn add @exchangeratesapi/javascript ``` ```bash pnpm theme={null} pnpm add @exchangeratesapi/javascript ``` The official JavaScript SDK is coming soon! For now, use the native fetch examples below or create your own wrapper class. ## Quick Start ### Node.js ```javascript theme={null} // Using ES modules import fetch from 'node-fetch'; // npm install node-fetch const API_KEY = process.env.EXCHANGE_RATES_API_KEY; const BASE_URL = 'https://api.exchangeratesapi.com.au'; class ExchangeRatesAPI { constructor(apiKey) { this.apiKey = apiKey; this.baseURL = BASE_URL; } async getLatestRates() { const response = await fetch(`${this.baseURL}/latest`, { headers: { 'Authorization': `Bearer ${this.apiKey}` } }); if (!response.ok) { throw new Error(`HTTP ${response.status}: ${response.statusText}`); } return response.json(); } async convert(from, to, amount, date = null) { const params = new URLSearchParams({ from, to, amount: amount.toString() }); if (date) params.append('date', date); const response = await fetch(`${this.baseURL}/convert?${params}`, { headers: { 'Authorization': `Bearer ${this.apiKey}` } }); if (!response.ok) { throw new Error(`HTTP ${response.status}: ${response.statusText}`); } return response.json(); } async getHistoricalRates(date) { const response = await fetch(`${this.baseURL}/${date}`, { headers: { 'Authorization': `Bearer ${this.apiKey}` } }); if (!response.ok) { throw new Error(`HTTP ${response.status}: ${response.statusText}`); } return response.json(); } async getTimeSeries(startDate, endDate, symbols = null) { const params = new URLSearchParams({ start_date: startDate, end_date: endDate }); if (symbols) { params.append('symbols', Array.isArray(symbols) ? symbols.join(',') : symbols); } const response = await fetch(`${this.baseURL}/timeseries?${params}`, { headers: { 'Authorization': `Bearer ${this.apiKey}` } }); if (!response.ok) { throw new Error(`HTTP ${response.status}: ${response.statusText}`); } return response.json(); } } // Usage const api = new ExchangeRatesAPI(API_KEY); try { const rates = await api.getLatestRates(); console.log('Current USD rate:', rates.rates.USD); const conversion = await api.convert('AUD', 'USD', 100); console.log(`100 AUD = ${conversion.result} USD`); } catch (error) { console.error('API Error:', error.message); } ``` ### Browser (Modern) ```html theme={null} Exchange Rates Demo

AUD Exchange Rates

``` ## TypeScript Support ```typescript theme={null} // types/exchange-rates.ts export interface ExchangeRateResponse { success: boolean; timestamp: number; base: string; date: string; rates: Record; stale?: boolean; } export interface ConversionResponse { success: boolean; query: { from: string; to: string; amount: number; }; info: { timestamp: number; rate: number; }; date: string; result: number; } export interface TimeSeriesResponse { success: boolean; timeseries: boolean; start_date: string; end_date: string; base: string; rates: Record>; } export interface APIError { success: false; error: { code: number; type: string; info: string; }; } export type Currency = 'AUD' | 'USD' | 'EUR' | 'GBP' | 'JPY' | 'CNY' | 'KRW' | 'SGD' | 'NZD' | 'INR' | 'THB' | 'MYR' | 'IDR' | 'VND' | 'HKD' | 'PHP' | 'CAD' | 'CHF' | 'TWD' | 'TWI' | 'SDR'; ``` ```typescript theme={null} // exchange-rates-client.ts import type { ExchangeRateResponse, ConversionResponse, TimeSeriesResponse, APIError, Currency } from './types/exchange-rates'; export class ExchangeRatesClient { private apiKey: string; private baseURL: string = 'https://api.exchangeratesapi.com.au'; constructor(apiKey: string) { this.apiKey = apiKey; } private async makeRequest(endpoint: string): Promise { const response = await fetch(`${this.baseURL}${endpoint}`, { headers: { 'Authorization': `Bearer ${this.apiKey}`, 'Content-Type': 'application/json' } }); const data = await response.json(); if (!data.success) { const error = data as APIError; throw new Error(`API Error ${error.error.code}: ${error.error.info}`); } return data as T; } async getLatestRates(): Promise { return this.makeRequest('/latest'); } async getLatestRate(currency: Currency): Promise { return this.makeRequest(`/latest/${currency}`); } async convert( from: Currency, to: Currency, amount: number, date?: string ): Promise { const params = new URLSearchParams({ from, to, amount: amount.toString() }); if (date) params.append('date', date); return this.makeRequest(`/convert?${params}`); } async getHistoricalRates(date: string): Promise { return this.makeRequest(`/${date}`); } async getHistoricalRate(date: string, currency: Currency): Promise { return this.makeRequest(`/${date}/${currency}`); } async getTimeSeries( startDate: string, endDate: string, symbols?: Currency[] | string ): Promise { const params = new URLSearchParams({ start_date: startDate, end_date: endDate }); if (symbols) { const symbolsStr = Array.isArray(symbols) ? symbols.join(',') : symbols; params.append('symbols', symbolsStr); } return this.makeRequest(`/timeseries?${params}`); } async getStatus(): Promise<{ status: string; last_update: string; stale: boolean; next_update_expected: string; }> { // Status endpoint doesn't require authentication const response = await fetch(`${this.baseURL}/status`); return response.json(); } } // Usage const client = new ExchangeRatesClient(process.env.EXCHANGE_RATES_API_KEY!); async function example() { try { const rates = await client.getLatestRates(); console.log('USD Rate:', rates.rates.USD); const conversion = await client.convert('AUD', 'USD', 100); console.log(`100 AUD = ${conversion.result} USD`); const historicalRates = await client.getHistoricalRates('2024-01-01'); console.log('USD Rate on Jan 1, 2024:', historicalRates.rates.USD); const timeSeries = await client.getTimeSeries( '2024-01-01', '2024-01-31', ['USD', 'EUR'] ); console.log('January 2024 USD rates:', Object.entries(timeSeries.rates).map(([date, rates]) => `${date}: ${rates.USD}` ) ); } catch (error) { console.error('Error:', error); } } ``` ## React Hooks ```typescript theme={null} // hooks/useExchangeRates.ts import { useState, useEffect } from 'react'; import { ExchangeRatesClient } from '../exchange-rates-client'; import type { ExchangeRateResponse } from '../types/exchange-rates'; export function useExchangeRates(apiKey: string) { const [client] = useState(() => new ExchangeRatesClient(apiKey)); const [rates, setRates] = useState(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); useEffect(() => { let mounted = true; async function fetchRates() { try { setLoading(true); setError(null); const data = await client.getLatestRates(); if (mounted) { setRates(data); } } catch (err) { if (mounted) { setError(err instanceof Error ? err.message : 'Failed to fetch rates'); } } finally { if (mounted) { setLoading(false); } } } fetchRates(); // Refresh every 30 minutes const interval = setInterval(fetchRates, 30 * 60 * 1000); return () => { mounted = false; clearInterval(interval); }; }, [client]); const refresh = async () => { try { setLoading(true); setError(null); const data = await client.getLatestRates(); setRates(data); } catch (err) { setError(err instanceof Error ? err.message : 'Failed to refresh rates'); } finally { setLoading(false); } }; return { rates, loading, error, refresh, client }; } // Usage in component import React from 'react'; import { useExchangeRates } from './hooks/useExchangeRates'; export function ExchangeRateDisplay() { const { rates, loading, error, refresh } = useExchangeRates( process.env.REACT_APP_EXCHANGE_RATES_API_KEY! ); if (loading) return
Loading exchange rates...
; if (error) return
Error: {error}
; if (!rates) return
No data available
; const majorCurrencies = ['USD', 'EUR', 'GBP', 'JPY']; return (

Current Exchange Rates

Last updated: {new Date(rates.timestamp * 1000).toLocaleString()}

{rates.stale && (
⚠️ Data may be stale. RBA rates are typically updated at 4 PM AEST.
)}
{majorCurrencies.map(currency => (

{currency}

{rates.rates[currency].toFixed(4)}

1 AUD = {rates.rates[currency].toFixed(4)} {currency}

))}
); } ``` ## Error Handling Best Practices ```javascript theme={null} class ExchangeRatesAPIWithRetry { constructor(apiKey, options = {}) { this.apiKey = apiKey; this.baseURL = 'https://api.exchangeratesapi.com.au'; this.maxRetries = options.maxRetries || 3; this.retryDelay = options.retryDelay || 1000; // ms } async makeRequestWithRetry(endpoint, attempt = 1) { try { const response = await fetch(`${this.baseURL}${endpoint}`, { headers: { 'Authorization': `Bearer ${this.apiKey}` }, timeout: 10000 // 10 second timeout }); const data = await response.json(); if (!data.success) { const error = new Error(data.error.info); error.code = data.error.code; error.type = data.error.type; throw error; } return data; } catch (error) { console.warn(`Attempt ${attempt} failed:`, error.message); // Don't retry authentication or client errors if (error.code >= 400 && error.code < 500) { throw error; } // Retry network errors and server errors if (attempt < this.maxRetries) { await this.delay(this.retryDelay * attempt); return this.makeRequestWithRetry(endpoint, attempt + 1); } throw error; } } delay(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } async getLatestRatesSafe() { try { return await this.makeRequestWithRetry('/latest'); } catch (error) { console.error('Failed to fetch latest rates after retries:', error); // Return cached data if available const cached = this.getCachedRates(); if (cached) { console.warn('Using cached rates due to API failure'); return { ...cached, fromCache: true }; } // Final fallback throw new Error('Unable to fetch exchange rates. Please try again later.'); } } getCachedRates() { // Implement your caching logic here // Could use localStorage, Redis, file system, etc. try { const cached = localStorage.getItem('exchange_rates_cache'); if (cached) { const data = JSON.parse(cached); // Check if cache is less than 1 hour old if (Date.now() - data.cachedAt < 3600000) { return data.rates; } } } catch (error) { console.warn('Failed to load cached rates:', error); } return null; } setCachedRates(rates) { try { const cacheData = { rates, cachedAt: Date.now() }; localStorage.setItem('exchange_rates_cache', JSON.stringify(cacheData)); } catch (error) { console.warn('Failed to cache rates:', error); } } } ``` ## Next Steps Python examples and best practices PHP examples and Laravel integration Command-line examples and shell scripts Complete API documentation # PHP SDK Source: https://docs.exchangeratesapi.com.au/sdks/php Official PHP SDK for the Exchange Rates API with examples for Laravel, Symfony and vanilla PHP ## Installation ```bash composer theme={null} composer require exchangeratesapi/php ``` ```bash Manual Download theme={null} # Download and include manually wget https://github.com/exchangeratesapi/php/releases/latest/download/exchange-rates-api.phar ``` The official PHP SDK is coming soon! For now, use the native cURL/Guzzle examples below or create your own wrapper class. ## Quick Start ### Basic PHP Class ```php theme={null} apiKey = $apiKey; } private function makeRequest(string $endpoint): array { $ch = curl_init(); curl_setopt_array($ch, [ CURLOPT_URL => $this->baseURL . $endpoint, CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ 'Authorization: Bearer ' . $this->apiKey, 'Content-Type: application/json', 'User-Agent: PHP-ExchangeRatesAPI/1.0' ], CURLOPT_TIMEOUT => 30, CURLOPT_SSL_VERIFYPEER => true ]); $response = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); if (curl_error($ch)) { throw new Exception('cURL Error: ' . curl_error($ch)); } curl_close($ch); $data = json_decode($response, true); if ($httpCode >= 400 || !$data['success']) { throw new Exception('API Error: ' . ($data['error']['info'] ?? 'Unknown error')); } return $data; } public function getLatestRates(): array { return $this->makeRequest('/latest'); } public function getLatestRate(string $currency): array { return $this->makeRequest('/latest/' . $currency); } public function convert(string $from, string $to, float $amount, ?string $date = null): array { $params = http_build_query([ 'from' => $from, 'to' => $to, 'amount' => $amount, 'date' => $date ]); return $this->makeRequest('/convert?' . $params); } public function getHistoricalRates(string $date): array { return $this->makeRequest('/' . $date); } public function getHistoricalRate(string $date, string $currency): array { return $this->makeRequest('/' . $date . '/' . $currency); } public function getTimeSeries(string $startDate, string $endDate, ?array $symbols = null): array { $params = [ 'start_date' => $startDate, 'end_date' => $endDate ]; if ($symbols) { $params['symbols'] = implode(',', $symbols); } return $this->makeRequest('/timeseries?' . http_build_query($params)); } public function getStatus(): array { $ch = curl_init(); curl_setopt_array($ch, [ CURLOPT_URL => $this->baseURL . '/status', CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 10 ]); $response = curl_exec($ch); curl_close($ch); return json_decode($response, true); } } // Usage $api = new ExchangeRatesAPI($_ENV['EXCHANGE_RATES_API_KEY']); try { $rates = $api->getLatestRates(); echo "USD Rate: " . $rates['rates']['USD'] . "\n"; $conversion = $api->convert('AUD', 'USD', 100); echo "100 AUD = " . $conversion['result'] . " USD\n"; } catch (Exception $e) { echo "Error: " . $e->getMessage() . "\n"; } ``` ### Using Guzzle HTTP ```php theme={null} apiKey = $apiKey; $this->client = new Client([ 'base_uri' => $this->baseURL, 'timeout' => 30, 'headers' => [ 'Authorization' => 'Bearer ' . $this->apiKey, 'Content-Type' => 'application/json', 'User-Agent' => 'PHP-Guzzle-ExchangeRatesAPI/1.0' ] ]); } private function makeRequest(string $endpoint): array { try { $response = $this->client->get($endpoint); $data = json_decode($response->getBody()->getContents(), true); if (!$data['success']) { throw new Exception('API Error: ' . $data['error']['info']); } return $data; } catch (RequestException $e) { if ($e->hasResponse()) { $response = json_decode($e->getResponse()->getBody()->getContents(), true); throw new Exception('API Error: ' . ($response['error']['info'] ?? $e->getMessage())); } throw new Exception('Network Error: ' . $e->getMessage()); } } public function getLatestRates(): array { return $this->makeRequest('/latest'); } public function convert(string $from, string $to, float $amount, ?string $date = null): array { $query = http_build_query(array_filter([ 'from' => $from, 'to' => $to, 'amount' => $amount, 'date' => $date ])); return $this->makeRequest('/convert?' . $query); } public function getTimeSeries(string $startDate, string $endDate, ?array $symbols = null): array { $query = http_build_query(array_filter([ 'start_date' => $startDate, 'end_date' => $endDate, 'symbols' => $symbols ? implode(',', $symbols) : null ])); return $this->makeRequest('/timeseries?' . $query); } } // Usage $api = new ExchangeRatesAPIClient($_ENV['EXCHANGE_RATES_API_KEY']); try { $rates = $api->getLatestRates(); print_r($rates); } catch (Exception $e) { echo "Error: " . $e->getMessage() . "\n"; } ``` ## Laravel Integration ### Service Provider ```php theme={null} app->singleton(ExchangeRatesService::class, function ($app) { return new ExchangeRatesService(config('services.exchange_rates.api_key')); }); } public function boot() { // } } ``` ### Configuration ```php theme={null} [ 'api_key' => env('EXCHANGE_RATES_API_KEY'), 'base_url' => env('EXCHANGE_RATES_BASE_URL', 'https://api.exchangeratesapi.com.au'), 'timeout' => env('EXCHANGE_RATES_TIMEOUT', 30), ], ]; ``` ### Service Class ```php theme={null} apiKey = $apiKey; $this->baseURL = config('services.exchange_rates.base_url'); $this->timeout = config('services.exchange_rates.timeout'); } public function getLatestRates(bool $useCache = true): array { $cacheKey = 'exchange_rates_latest'; if ($useCache && Cache::has($cacheKey)) { return Cache::get($cacheKey); } try { $response = Http::timeout($this->timeout) ->withHeaders(['Authorization' => 'Bearer ' . $this->apiKey]) ->get($this->baseURL . '/latest'); $data = $response->json(); if (!$data['success']) { throw new \Exception('API Error: ' . $data['error']['info']); } // Cache for 30 minutes (RBA updates daily at 4 PM AEST) Cache::put($cacheKey, $data, now()->addMinutes(30)); return $data; } catch (\Exception $e) { Log::error('Exchange Rates API Error: ' . $e->getMessage()); // Return cached data if available if (Cache::has($cacheKey)) { Log::info('Returning cached exchange rates due to API failure'); return Cache::get($cacheKey); } throw $e; } } public function convert(string $from, string $to, float $amount, ?string $date = null): array { $params = [ 'from' => $from, 'to' => $to, 'amount' => $amount ]; if ($date) { $params['date'] = $date; } $response = Http::timeout($this->timeout) ->withHeaders(['Authorization' => 'Bearer ' . $this->apiKey]) ->get($this->baseURL . '/convert', $params); $data = $response->json(); if (!$data['success']) { throw new \Exception('API Error: ' . $data['error']['info']); } return $data; } public function getRate(string $currency, bool $useCache = true): ?float { try { $rates = $this->getLatestRates($useCache); return $rates['rates'][$currency] ?? null; } catch (\Exception $e) { Log::error('Failed to get rate for ' . $currency . ': ' . $e->getMessage()); return null; } } public function convertAmount(float $amount, string $from, string $to): ?float { try { $conversion = $this->convert($from, $to, $amount); return $conversion['result']; } catch (\Exception $e) { Log::error('Failed to convert ' . $amount . ' ' . $from . ' to ' . $to . ': ' . $e->getMessage()); return null; } } } ``` ### Controller Usage ```php theme={null} exchangeRatesService = $exchangeRatesService; } public function index() { try { $rates = $this->exchangeRatesService->getLatestRates(); return response()->json([ 'success' => true, 'data' => $rates ]); } catch (\Exception $e) { return response()->json([ 'success' => false, 'error' => $e->getMessage() ], 500); } } public function convert(Request $request) { $request->validate([ 'from' => 'required|string|size:3', 'to' => 'required|string|size:3', 'amount' => 'required|numeric|min:0' ]); try { $result = $this->exchangeRatesService->convert( $request->input('from'), $request->input('to'), $request->input('amount') ); return response()->json([ 'success' => true, 'data' => $result ]); } catch (\Exception $e) { return response()->json([ 'success' => false, 'error' => $e->getMessage() ], 500); } } } ``` ### Error Handling & Logging ```php theme={null} apiKey = $apiKey; $this->baseURL = 'https://api.exchangeratesapi.com.au'; $this->maxRetries = $maxRetries; $this->retryDelay = $retryDelay; } private function makeRequestWithRetry(string $endpoint, int $attempt = 1): array { try { $ch = curl_init(); curl_setopt_array($ch, [ CURLOPT_URL => $this->baseURL . $endpoint, CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ 'Authorization: Bearer ' . $this->apiKey, 'Content-Type: application/json' ], CURLOPT_TIMEOUT => 30, CURLOPT_CONNECTTIMEOUT => 10 ]); $response = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); $error = curl_error($ch); curl_close($ch); if ($error) { throw new Exception('cURL Error: ' . $error); } $data = json_decode($response, true); if ($httpCode >= 400 || !$data['success']) { $errorInfo = $data['error']['info'] ?? 'Unknown error'; $errorCode = $data['error']['code'] ?? $httpCode; // Don't retry client errors (4xx) if ($httpCode >= 400 && $httpCode < 500) { throw new Exception("API Error $errorCode: $errorInfo"); } throw new Exception("Server Error $errorCode: $errorInfo"); } return $data; } catch (Exception $e) { error_log("Attempt $attempt failed: " . $e->getMessage()); // Don't retry authentication or client errors if (strpos($e->getMessage(), 'API Error 4') === 0) { throw $e; } // Retry on network errors and server errors if ($attempt < $this->maxRetries) { sleep($this->retryDelay * $attempt); return $this->makeRequestWithRetry($endpoint, $attempt + 1); } throw $e; } } public function getLatestRatesSafe(): array { try { return $this->makeRequestWithRetry('/latest'); } catch (Exception $e) { error_log('Failed to fetch latest rates after retries: ' . $e->getMessage()); // Try to return cached data $cached = $this->getCachedRates(); if ($cached) { error_log('Using cached rates due to API failure'); return array_merge($cached, ['fromCache' => true]); } throw new Exception('Unable to fetch exchange rates. Please try again later.'); } } private function getCachedRates(): ?array { $cacheFile = sys_get_temp_dir() . '/exchange_rates_cache.json'; if (file_exists($cacheFile)) { $cached = json_decode(file_get_contents($cacheFile), true); // Check if cache is less than 1 hour old if (time() - $cached['cachedAt'] < 3600) { return $cached['rates']; } } return null; } private function setCachedRates(array $rates): void { $cacheFile = sys_get_temp_dir() . '/exchange_rates_cache.json'; $cacheData = [ 'rates' => $rates, 'cachedAt' => time() ]; file_put_contents($cacheFile, json_encode($cacheData)); } } ``` ## Next Steps Python examples and best practices Command-line examples and shell scripts JavaScript/Node.js examples and React hooks Complete API documentation # Python SDK Source: https://docs.exchangeratesapi.com.au/sdks/python Official Python SDK for the Exchange Rates API with examples for Django, Flask and vanilla Python ## Installation ```bash pip theme={null} pip install exchangeratesapi ``` ```bash pipenv theme={null} pipenv install exchangeratesapi ``` ```bash poetry theme={null} poetry add exchangeratesapi ``` The official Python SDK is coming soon! For now, use the native requests examples below or create your own wrapper class. ## Quick Start ### Basic Python Class ```python theme={null} import requests import os from typing import Dict, List, Optional from datetime import datetime class ExchangeRatesAPI: def __init__(self, api_key: str): self.api_key = api_key self.base_url = 'https://api.exchangeratesapi.com.au' self.session = requests.Session() self.session.headers.update({ 'Authorization': f'Bearer {api_key}', 'Content-Type': 'application/json', 'User-Agent': 'Python-ExchangeRatesAPI/1.0' }) def _make_request(self, endpoint: str) -> Dict: response = self.session.get(f'{self.base_url}{endpoint}') if response.status_code != 200: response.raise_for_status() data = response.json() if not data.get('success', False): error_info = data.get('error', {}).get('info', 'Unknown error') raise Exception(f"API Error: {error_info}") return data def get_latest_rates(self) -> Dict: """Get the latest exchange rates for all currencies""" return self._make_request('/latest') def get_latest_rate(self, currency: str) -> Dict: """Get the latest exchange rate for a specific currency""" return self._make_request(f'/latest/{currency}') def convert(self, from_currency: str, to_currency: str, amount: float, date: Optional[str] = None) -> Dict: """Convert an amount from one currency to another""" params = { 'from': from_currency, 'to': to_currency, 'amount': amount } if date: params['date'] = date query_string = '&'.join([f'{k}={v}' for k, v in params.items()]) return self._make_request(f'/convert?{query_string}') def get_historical_rates(self, date: str) -> Dict: """Get historical exchange rates for a specific date""" return self._make_request(f'/{date}') def get_historical_rate(self, date: str, currency: str) -> Dict: """Get historical exchange rate for a specific date and currency""" return self._make_request(f'/{date}/{currency}') def get_time_series(self, start_date: str, end_date: str, symbols: Optional[List[str]] = None) -> Dict: """Get time series data for a date range""" params = { 'start_date': start_date, 'end_date': end_date } if symbols: params['symbols'] = ','.join(symbols) query_string = '&'.join([f'{k}={v}' for k, v in params.items()]) return self._make_request(f'/timeseries?{query_string}') def get_status(self) -> Dict: """Get API status (no authentication required)""" response = requests.get(f'{self.base_url}/status') return response.json() # Usage api = ExchangeRatesAPI(os.getenv('EXCHANGE_RATES_API_KEY')) try: rates = api.get_latest_rates() print(f"USD Rate: {rates['rates']['USD']}") conversion = api.convert('AUD', 'USD', 100) print(f"100 AUD = {conversion['result']} USD") except Exception as e: print(f"Error: {e}") ``` ### Using with Type Hints ```python theme={null} from typing import Dict, List, Optional, Union from dataclasses import dataclass from datetime import datetime import requests @dataclass class ExchangeRate: currency: str rate: float date: str @dataclass class ConversionResult: from_currency: str to_currency: str amount: float result: float rate: float date: str class ExchangeRatesClient: def __init__(self, api_key: str, timeout: int = 30): self.api_key = api_key self.base_url = 'https://api.exchangeratesapi.com.au' self.timeout = timeout self.session = requests.Session() self.session.headers.update({ 'Authorization': f'Bearer {api_key}', 'Content-Type': 'application/json' }) def _make_request(self, endpoint: str) -> Dict: try: response = self.session.get( f'{self.base_url}{endpoint}', timeout=self.timeout ) response.raise_for_status() data = response.json() if not data.get('success'): raise ValueError(f"API Error: {data.get('error', {}).get('info', 'Unknown')}") return data except requests.RequestException as e: raise ConnectionError(f"Network error: {e}") def get_latest_rates(self) -> Dict[str, ExchangeRate]: """Get latest rates as ExchangeRate objects""" data = self._make_request('/latest') return { currency: ExchangeRate( currency=currency, rate=rate, date=data['date'] ) for currency, rate in data['rates'].items() } def convert_currency(self, from_currency: str, to_currency: str, amount: float, date: Optional[str] = None) -> ConversionResult: """Convert currency and return structured result""" params = { 'from': from_currency, 'to': to_currency, 'amount': amount } if date: params['date'] = date query = '&'.join([f'{k}={v}' for k, v in params.items()]) data = self._make_request(f'/convert?{query}') return ConversionResult( from_currency=data['query']['from'], to_currency=data['query']['to'], amount=data['query']['amount'], result=data['result'], rate=data['info']['rate'], date=data['date'] ) def get_rate(self, currency: str, date: Optional[str] = None) -> Optional[float]: """Get a single rate, optionally for a specific date""" try: if date: data = self._make_request(f'/{date}/{currency}') else: data = self._make_request(f'/latest/{currency}') return data['rates'][currency] except Exception: return None # Usage with type hints client = ExchangeRatesClient(os.getenv('EXCHANGE_RATES_API_KEY')) rates: Dict[str, ExchangeRate] = client.get_latest_rates() usd_rate: ExchangeRate = rates['USD'] print(f"USD: {usd_rate.rate} on {usd_rate.date}") conversion: ConversionResult = client.convert_currency('AUD', 'USD', 100) print(f"{conversion.amount} {conversion.from_currency} = {conversion.result} {conversion.to_currency}") ``` ## Django Integration ### Settings Configuration ```python theme={null} # settings.py EXCHANGE_RATES_API = { 'API_KEY': os.getenv('EXCHANGE_RATES_API_KEY'), 'BASE_URL': 'https://api.exchangeratesapi.com.au', 'TIMEOUT': 30, 'CACHE_TIMEOUT': 30 * 60, # 30 minutes } ``` ### Service Class ```python theme={null} # services/exchange_rates.py from django.conf import settings from django.core.cache import cache from django.utils import timezone import requests import logging logger = logging.getLogger(__name__) class ExchangeRatesService: def __init__(self): self.api_key = settings.EXCHANGE_RATES_API['API_KEY'] self.base_url = settings.EXCHANGE_RATES_API['BASE_URL'] self.timeout = settings.EXCHANGE_RATES_API['TIMEOUT'] self.cache_timeout = settings.EXCHANGE_RATES_API['CACHE_TIMEOUT'] def _make_request(self, endpoint: str) -> dict: headers = { 'Authorization': f'Bearer {self.api_key}', 'Content-Type': 'application/json' } try: response = requests.get( f'{self.base_url}{endpoint}', headers=headers, timeout=self.timeout ) response.raise_for_status() data = response.json() if not data.get('success'): raise ValueError(f"API Error: {data.get('error', {}).get('info')}") return data except requests.RequestException as e: logger.error(f"Exchange rates API request failed: {e}") raise def get_latest_rates(self, use_cache: bool = True) -> dict: cache_key = 'exchange_rates_latest' if use_cache: cached_data = cache.get(cache_key) if cached_data: return cached_data try: data = self._make_request('/latest') if use_cache: cache.set(cache_key, data, self.cache_timeout) return data except Exception as e: logger.error(f"Failed to get latest rates: {e}") # Try to return cached data as fallback cached_data = cache.get(cache_key) if cached_data: logger.info("Returning cached rates due to API failure") return cached_data raise def convert_currency(self, from_currency: str, to_currency: str, amount: float) -> dict: params = { 'from': from_currency, 'to': to_currency, 'amount': amount } query = '&'.join([f'{k}={v}' for k, v in params.items()]) return self._make_request(f'/convert?{query}') def get_rate(self, currency: str, use_cache: bool = True) -> float: try: rates_data = self.get_latest_rates(use_cache) return rates_data['rates'].get(currency) except Exception as e: logger.error(f"Failed to get rate for {currency}: {e}") return None ``` ### Views ```python theme={null} # views.py from django.http import JsonResponse from django.views import View from django.utils.decorators import method_decorator from django.views.decorators.csrf import csrf_exempt import json from .services.exchange_rates import ExchangeRatesService class ExchangeRatesView(View): def __init__(self): super().__init__() self.exchange_service = ExchangeRatesService() def get(self, request): try: rates = self.exchange_service.get_latest_rates() return JsonResponse({ 'success': True, 'data': rates }) except Exception as e: return JsonResponse({ 'success': False, 'error': str(e) }, status=500) @method_decorator(csrf_exempt, name='dispatch') class ConvertCurrencyView(View): def __init__(self): super().__init__() self.exchange_service = ExchangeRatesService() def post(self, request): try: data = json.loads(request.body) from_currency = data.get('from') to_currency = data.get('to') amount = data.get('amount') if not all([from_currency, to_currency, amount]): return JsonResponse({ 'success': False, 'error': 'Missing required parameters: from, to, amount' }, status=400) result = self.exchange_service.convert_currency( from_currency, to_currency, float(amount) ) return JsonResponse({ 'success': True, 'data': result }) except ValueError as e: return JsonResponse({ 'success': False, 'error': str(e) }, status=400) except Exception as e: return JsonResponse({ 'success': False, 'error': 'Internal server error' }, status=500) ``` ### Models ```python theme={null} # models.py from django.db import models from django.utils import timezone class ExchangeRateLog(models.Model): currency = models.CharField(max_length=3) rate = models.DecimalField(max_digits=12, decimal_places=6) date = models.DateField() created_at = models.DateTimeField(default=timezone.now) class Meta: unique_together = ['currency', 'date'] ordering = ['-date', 'currency'] def __str__(self): return f"{self.currency}: {self.rate} ({self.date})" class CurrencyConversion(models.Model): from_currency = models.CharField(max_length=3) to_currency = models.CharField(max_length=3) amount = models.DecimalField(max_digits=15, decimal_places=2) result = models.DecimalField(max_digits=15, decimal_places=2) rate = models.DecimalField(max_digits=12, decimal_places=6) conversion_date = models.DateField() created_at = models.DateTimeField(default=timezone.now) class Meta: ordering = ['-created_at'] def __str__(self): return f"{self.amount} {self.from_currency} � {self.result} {self.to_currency}" ``` ### Management Command ```python theme={null} # management/commands/update_exchange_rates.py from django.core.management.base import BaseCommand from django.utils import timezone from myapp.services.exchange_rates import ExchangeRatesService from myapp.models import ExchangeRateLog class Command(BaseCommand): help = 'Update exchange rates from the API' def add_arguments(self, parser): parser.add_argument( '--currency', type=str, help='Update specific currency only' ) def handle(self, *args, **options): service = ExchangeRatesService() try: self.stdout.write('Fetching latest exchange rates...') rates_data = service.get_latest_rates(use_cache=False) today = timezone.now().date() updated_count = 0 currencies_to_update = [options['currency']] if options['currency'] else rates_data['rates'].keys() for currency in currencies_to_update: if currency in rates_data['rates']: rate = rates_data['rates'][currency] log, created = ExchangeRateLog.objects.update_or_create( currency=currency, date=today, defaults={'rate': rate} ) if created: updated_count += 1 self.stdout.write(f"Added {currency}: {rate}") else: self.stdout.write(f"Updated {currency}: {rate}") self.stdout.write( self.style.SUCCESS( f'Successfully updated {updated_count} exchange rates for {rates_data["date"]}' ) ) except Exception as e: self.stdout.write( self.style.ERROR(f'Failed to update exchange rates: {e}') ) ``` ## Flask Integration ### Application Setup ```python theme={null} # app.py from flask import Flask, jsonify, request import os import requests from functools import wraps import logging app = Flask(__name__) # Configuration app.config['EXCHANGE_RATES_API_KEY'] = os.getenv('EXCHANGE_RATES_API_KEY') app.config['EXCHANGE_RATES_BASE_URL'] = 'https://api.exchangeratesapi.com.au' # Setup logging logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__) class ExchangeRatesAPI: def __init__(self, api_key: str, base_url: str): self.api_key = api_key self.base_url = base_url self.session = requests.Session() self.session.headers.update({ 'Authorization': f'Bearer {api_key}', 'Content-Type': 'application/json' }) def get_latest_rates(self): response = self.session.get(f'{self.base_url}/latest') response.raise_for_status() return response.json() def convert_currency(self, from_currency: str, to_currency: str, amount: float): params = { 'from': from_currency, 'to': to_currency, 'amount': amount } response = self.session.get(f'{self.base_url}/convert', params=params) response.raise_for_status() return response.json() # Initialize API client exchange_api = ExchangeRatesAPI( app.config['EXCHANGE_RATES_API_KEY'], app.config['EXCHANGE_RATES_BASE_URL'] ) def handle_api_errors(f): @wraps(f) def decorated_function(*args, **kwargs): try: return f(*args, **kwargs) except requests.RequestException as e: logger.error(f"API request failed: {e}") return jsonify({ 'success': False, 'error': 'Failed to fetch exchange rate data' }), 500 except Exception as e: logger.error(f"Unexpected error: {e}") return jsonify({ 'success': False, 'error': 'Internal server error' }), 500 return decorated_function @app.route('/api/rates') @handle_api_errors def get_rates(): rates = exchange_api.get_latest_rates() return jsonify({ 'success': True, 'data': rates }) @app.route('/api/convert', methods=['POST']) @handle_api_errors def convert_currency(): data = request.get_json() required_fields = ['from', 'to', 'amount'] if not all(field in data for field in required_fields): return jsonify({ 'success': False, 'error': 'Missing required fields: from, to, amount' }), 400 try: amount = float(data['amount']) except (ValueError, TypeError): return jsonify({ 'success': False, 'error': 'Amount must be a valid number' }), 400 result = exchange_api.convert_currency( data['from'], data['to'], amount ) return jsonify({ 'success': True, 'data': result }) @app.route('/health') def health_check(): return jsonify({ 'status': 'healthy', 'timestamp': int(timezone.now().timestamp()) }) if __name__ == '__main__': app.run(debug=True) ``` ## Error Handling & Retry Logic ```python theme={null} import time import logging from typing import Dict, Optional import requests from requests.adapters import HTTPAdapter from requests.packages.urllib3.util.retry import Retry class ExchangeRatesAPIWithRetry: def __init__(self, api_key: str, max_retries: int = 3, backoff_factor: float = 1.0): self.api_key = api_key self.base_url = 'https://api.exchangeratesapi.com.au' self.max_retries = max_retries # Setup session with retry strategy self.session = requests.Session() retry_strategy = Retry( total=max_retries, status_forcelist=[429, 500, 502, 503, 504], method_whitelist=["HEAD", "GET", "OPTIONS"], backoff_factor=backoff_factor ) adapter = HTTPAdapter(max_retries=retry_strategy) self.session.mount("http://", adapter) self.session.mount("https://", adapter) self.session.headers.update({ 'Authorization': f'Bearer {api_key}', 'Content-Type': 'application/json' }) def _make_request_with_fallback(self, endpoint: str) -> Dict: try: response = self.session.get(f'{self.base_url}{endpoint}', timeout=30) response.raise_for_status() data = response.json() if not data.get('success'): raise ValueError(f"API Error: {data.get('error', {}).get('info')}") # Cache successful responses self._cache_response(endpoint, data) return data except Exception as e: logging.error(f"Request to {endpoint} failed: {e}") # Try to return cached data cached = self._get_cached_response(endpoint) if cached: logging.info(f"Returning cached data for {endpoint}") cached['fromCache'] = True return cached raise def _cache_response(self, endpoint: str, data: Dict) -> None: """Simple file-based caching""" import json import os from pathlib import Path cache_dir = Path.home() / '.exchange_rates_cache' cache_dir.mkdir(exist_ok=True) cache_file = cache_dir / f"{endpoint.replace('/', '_')}.json" cache_data = { 'data': data, 'timestamp': time.time() } try: with open(cache_file, 'w') as f: json.dump(cache_data, f) except Exception as e: logging.warning(f"Failed to cache response: {e}") def _get_cached_response(self, endpoint: str) -> Optional[Dict]: """Retrieve cached response if still valid""" import json from pathlib import Path cache_dir = Path.home() / '.exchange_rates_cache' cache_file = cache_dir / f"{endpoint.replace('/', '_')}.json" if not cache_file.exists(): return None try: with open(cache_file, 'r') as f: cache_data = json.load(f) # Check if cache is less than 1 hour old if time.time() - cache_data['timestamp'] < 3600: return cache_data['data'] except Exception as e: logging.warning(f"Failed to read cache: {e}") return None def get_latest_rates_safe(self) -> Dict: """Get latest rates with fallback to cached data""" return self._make_request_with_fallback('/latest') def convert_safe(self, from_currency: str, to_currency: str, amount: float) -> Dict: """Convert currency with fallback support""" params = { 'from': from_currency, 'to': to_currency, 'amount': amount } query = '&'.join([f'{k}={v}' for k, v in params.items()]) endpoint = f'/convert?{query}' return self._make_request_with_fallback(endpoint) # Usage api = ExchangeRatesAPIWithRetry( api_key=os.getenv('EXCHANGE_RATES_API_KEY'), max_retries=3, backoff_factor=2.0 ) try: rates = api.get_latest_rates_safe() print("Latest rates fetched successfully") if rates.get('fromCache'): print("Note: Data served from cache due to API issues") except Exception as e: print(f"Failed to get rates even with retries: {e}") ``` ## Next Steps PHP examples and Laravel integration Command-line examples and shell scripts JavaScript/Node.js examples and React hooks Complete API documentation # Support Source: https://docs.exchangeratesapi.com.au/support Get help with the Exchange Rates API - support channels, response times, and troubleshooting resources ## Support Channels ### Email Support **[https://www.exchangeratesapi.com.au/contact](https://www.exchangeratesapi.com.au/contact)** **Response Times:** * **Free**: Best effort (2-5 business days) * **Starter**: 48 hours * **Professional**: 24 hours * **Business**: 4 hours * **Enterprise**: 4 hours + phone support ### Self-Service Real-time API status Usage and billing ### When Contacting Support Include: * Your email address and plan * Exact error messages and HTTP codes * Request ID from X-Request-Id header * Sample cURL command (with key redacted) *Check [status page](https://exchangeratesapi-com-au.openstatus.dev/) first, then email us* ## Business Hours **Australian Eastern Time (AEST/AEDT)** * Monday-Friday: 9 AM - 5 PM