# 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.