Exchange Rates

PerfLocale can automatically fetch live exchange rates and keep your per-language currency configuration up to date.

Quick Start

  1. Go to PerfLocale > Settings > WooCommerce
  2. Enable Per-Language Currency
  3. Configure a currency code for each language
  4. Enable Auto-Sync Rates
  5. Select a provider and sync interval
  6. Click Sync Now to verify

Providers

Frankfurter (Free, default)

  • Source: European Central Bank (ECB) reference rates
  • API Key: Not required
  • Limits: No rate limits
  • Base currencies: All major currencies
  • URL: api.frankfurter.app
  • Data sent: base + symbol currency codes only (e.g. ?base=USD&symbols=EUR,GBP). No user data, no WordPress identity, no personally identifiable information.
  • License / Terms: MIT License - open-source, no formal privacy policy because no personal data is collected.
  • Docs: frankfurter.dev
  • Notes: Rates update once per business day (ECB schedule). Best for daily or weekly sync.

ExchangeRate-API (Free)

  • Source: Multiple central banks
  • API Key: Not required for the free tier
  • Limits: ~1,500 requests/month
  • Base currencies: All supported currencies
  • URL: open.er-api.com
  • Notes: Good general-purpose provider. Rates update daily.

Open Exchange Rates (Freemium)

  • Source: Aggregated market data
  • API Key: Required (sign up)
  • Free tier: 1,000 requests/month, USD base only
  • Paid tiers: All base currencies, hourly updates
  • URL: openexchangerates.org
  • Notes: Free tier only supports USD as base currency. PerfLocale automatically cross-calculates rates for other base currencies, but accuracy may vary slightly.

CurrencyFreaks (Freemium)

  • Source: Aggregated market data
  • API Key: Required (sign up)
  • Free tier: 1,000 requests/month
  • Base currencies: Configurable
  • URL: api.currencyfreaks.com

Fixer.io (Paid, HTTPS)

  • Source: European Central Bank + 15 other sources
  • API Key: Required (sign up)
  • Plan: Paid plan required - PerfLocale uses HTTPS exclusively, which is only available on paid Fixer.io plans
  • Paid tiers: All base currencies, HTTPS, hourly updates
  • URL: data.fixer.io
  • Notes: Free tier only supports EUR base and HTTP (no HTTPS). PerfLocale requires HTTPS for all API calls, so a paid plan is necessary. PerfLocale cross-calculates rates for non-EUR base currencies.

Sync Intervals

IntervalRequests/dayRequests/month
Every Hour24~720
Every 2 Hours12~360
Every 4 Hours6~180
Every 6 Hours4~120
Every 8 Hours3~90
Every 12 Hours2~60
Once Daily (default)1~30
Once Weekly~0.14~4
Once Monthly~0.031

Choose an interval that fits within your API plan limits. For most stores, daily is sufficient since exchange rates don't change drastically within a day.

API Key Management

Database (Settings UI)

Enter the key in the Settings page. The key is stored in the perflocale_settings option.

Important: Keys stored in the database are excluded from export/import to prevent accidental leakage. If you export settings and import them on another site, you must re-enter API keys.

Define the key as a PHP constant in wp-config.php for security. When a constant is defined, it takes priority and the field in the UI shows the constant name instead of an input.

// Open Exchange Rates
define( 'PERFLOCALE_OXR_API_KEY', 'your-api-key-here' );

// CurrencyFreaks
define( 'PERFLOCALE_CURRENCYFREAKS_KEY', 'your-api-key-here' );

// Fixer.io
define( 'PERFLOCALE_FIXER_KEY', 'your-api-key-here' );

Benefits of using constants:

  • Not stored in the database (not exposed in exports, backups, or SQL dumps)
  • Not visible in the WordPress admin UI
  • Can differ per environment (staging vs production)
  • Cannot be changed by other plugins or admin users

Custom Provider via Filter

Register your own exchange rate provider using the perflocale/woocommerce/exchange_rate_providers filter:

add_filter( 'perflocale/woocommerce/exchange_rate_providers', function( array $providers ): array {
	$providers['my_custom_api'] = [
		'name'           => 'My Custom API',
		'needs_key'      => true,
		'key_setting'    => 'wc_my_custom_key',
		'fetch_callback' => function( string $base_currency, array $target_currencies, string $api_key ): array {
			$response = wp_remote_get( 'https://my-api.example.com/rates?base=' . $base_currency . '&key=' . $api_key );

			if ( is_wp_error( $response ) ) {
				return [];
			}

			$body = json_decode( wp_remote_retrieve_body( $response ), true );

			$rates = [];
			foreach ( $target_currencies as $code ) {
				if ( isset( $body['rates'][ $code ] ) ) {
					$rates[ $code ] = (float) $body['rates'][ $code ];
				}
			}

			return $rates;
		},
	];

	return $providers;
} );

To also support a wp-config.php constant for the custom API key, add it to the settings constant map:

add_filter( 'perflocale/settings/constant_map', function( array $map ): array {
	$map['wc_my_custom_key'] = 'MY_CUSTOM_API_KEY';
	return $map;
} );

Then define in wp-config.php:

define( 'MY_CUSTOM_API_KEY', 'your-key-here' );

Hooks Reference

Filters

perflocale/woocommerce/exchange_rate_providers

Modify or add exchange rate providers.

add_filter( 'perflocale/woocommerce/exchange_rate_providers', function( $providers ) {
	// Remove a built-in provider
	unset( $providers['fixer'] );

	// Add a custom provider (see example above)
	$providers['my_api'] = [ ... ];

	return $providers;
} );

perflocale/woocommerce/exchange_rates_fetched

Filter rates after they are fetched but before they are saved.

add_filter( 'perflocale/woocommerce/exchange_rates_fetched', function( array $rates, string $base, string $provider ): array {
	// Add a margin to all rates
	foreach ( $rates as $code => $rate ) {
		$rates[ $code ] = $rate * 1.02; // 2% markup
	}
	return $rates;
}, 10, 3 );

Actions

perflocale/woocommerce/exchange_rates_synced

Fires after rates are successfully saved.

add_action( 'perflocale/woocommerce/exchange_rates_synced', function( array $rates, string $base, string $provider ): void {
	// Log the sync
	error_log( sprintf( 'Exchange rates synced from %s: %s', $provider, wp_json_encode( $rates ) ) );

	// Clear page cache
	if ( function_exists( 'wp_cache_flush' ) ) {
		wp_cache_flush();
	}
}, 10, 3 );

Troubleshooting

Rates not updating

  1. Check that WP-Cron is running. Some hosts disable WP-Cron; you may need a server-side cron job:
    */5 * * * * curl -s https://example.com/wp-cron.php > /dev/null 2>&1
  2. Click Sync Now in the settings to test manually.
  3. Enable WP_DEBUG and WP_DEBUG_LOG in wp-config.php to see sync errors:
    define( 'WP_DEBUG', true );
    define( 'WP_DEBUG_LOG', true );
    Check wp-content/debug.log for lines starting with PerfLocale Exchange Rate Sync:.

"API key missing" error

Ensure the key is either:

  • Entered in the Settings UI, or
  • Defined as a constant in wp-config.php

The constant takes priority. If defined as a constant, the UI field is disabled.

Rates differ from expected

  • Free API tiers may return rates from the previous day.
  • Cross-calculation (when the API base differs from your store base) introduces minor rounding.
  • Some providers have different data sources; rates may vary slightly between providers.

"No rates returned" error

  • The API may be temporarily unavailable. Try again or switch providers.
  • Check that your target currency codes are valid ISO 4217 codes (e.g., EUR, GBP, JPY).
  • Free tiers may restrict certain currencies.