Best Exchange Rate API for PHP and Laravel (2026)
PHP powers over 75% of the web, and Laravel is the most popular PHP framework by a wide margin. If your Laravel application handles multi-currency pricing, international payments, invoicing, or financial reporting, you need a currency API that integrates cleanly with the PHP and Laravel ecosystem.
The problem: most currency APIs treat PHP as an afterthought. They offer no Composer package, no Laravel service provider, and force you to write boilerplate Guzzle code for every call. Some have community-maintained wrappers that break when the API changes or the maintainer moves on. Others cap their free tier so aggressively that you burn through your quota during local development.
This article compares the 5 most popular exchange rate APIs for PHP and Laravel in 2026 and shows you exactly how to integrate each one — from Composer install to production-ready caching. Spoiler: AllRatesToday is the clear winner for PHP developers, with an official Packagist package, a few-line Laravel container binding, real-time mid-market rates, and a free tier that does not require a credit card.
Side-by-Side Comparison
Here is an honest look at how the top 5 exchange rate APIs stack up for PHP and Laravel developers:
| API | PHP SDK | Composer Package | Free Tier | Laravel Integration | Update Speed |
|---|---|---|---|---|---|
| AllRatesToday | Official | allratestoday/sdk | Free, no CC | Container binding | 60 seconds |
| Open Exchange Rates | Community only | Community packages | 1,000 req/mo | Manual setup | Hourly |
| Fixer.io | No | None | 100 req/mo | Manual setup | Daily |
| CurrencyAPI | Official | currencyapi/currencyapi-php | 300 req/mo | Manual setup | Daily |
| ExchangeRate-API | No | None | 1,500 req/mo | Manual setup | Daily |
Key takeaway: AllRatesToday is the only API on this list that offers an official Composer package that drops into Laravel's service container, real-time rates updated every 60 seconds, historical data from €4.99/month, and no credit card requirement to start.
1. AllRatesToday — Best Overall for PHP and Laravel
AllRatesToday was built API-first with official SDKs for PHP, Python, and JavaScript. The PHP package is published on Packagist, maintained by the AllRatesToday team. It is a plain, dependency-free client that you bind into Laravel's service container in a few lines.
Install via Composer
composer require allratestoday/sdk Basic PHP usage (no framework)
<?php
require 'vendor/autoload.php';
use AllRatesToday\AllRatesToday;
$client = new AllRatesToday('YOUR_API_KEY');
// Fetch real-time USD to EUR rate (the API returns a list of rows)
$rows = $client->getRates('USD', 'EUR');
echo "1 USD = {$rows[0]['rate']} EUR\n"; Convert an amount
<?php
$result = $client->convert('USD', 'EUR', 1000);
echo "\$1,000 = €{$result['result']}\n";
echo "Rate used: {$result['rate']}\n"; Fetch historical rates (paid plans)
<?php
$history = $client->getHistoricalRates('USD', 'EUR', '30d');
// ['source' => 'USD', 'target' => 'EUR', 'period' => '30d', 'data' => [['date', 'rate', 'timestamp'], ...]]
foreach ($history['data'] as $point) {
echo "{$point['date']}: {$point['rate']}\n";
} Using Guzzle directly (no SDK)
If you prefer raw HTTP calls, here is how to use Guzzle:
<?php
require 'vendor/autoload.php';
use GuzzleHttp\Client;
$http = new Client([
'base_uri' => 'https://allratestoday.com/api/v1/',
'headers' => [
'Authorization' => 'Bearer YOUR_API_KEY',
'Accept' => 'application/json',
],
]);
$response = $http->get('rates', [
'query' => [
'source' => 'USD',
'target' => 'EUR',
],
]);
// Always a list of rows: [['rate' => ..., 'source' => 'USD', 'target' => 'EUR', 'time' => ...]]
$data = json_decode($response->getBody(), true);
echo "1 USD = {$data[0]['rate']} EUR\n";
echo "Updated: {$data[0]['time']}\n"; Laravel service provider setup
The SDK is framework-agnostic, so you register it in Laravel's container yourself. Add your API key to .env:
ALLRATESTODAY_API_KEY=your_api_key_here Expose it in config/services.php:
'allratestoday' => [
'key' => env('ALLRATESTODAY_API_KEY'),
], Then bind the client as a singleton in app/Providers/AppServiceProvider.php:
use AllRatesToday\AllRatesToday;
public function register(): void
{
$this->app->singleton(AllRatesToday::class, function () {
return new AllRatesToday(config('services.allratestoday.key'));
});
} Using dependency injection in Laravel
<?php
namespace App\Http\Controllers;
use AllRatesToday\AllRatesToday;
use Illuminate\Http\JsonResponse;
class CurrencyController extends Controller
{
public function __construct(
private AllRatesToday $rates
) {}
public function convert(string $from, string $to, float $amount): JsonResponse
{
$result = $this->rates->convert($from, $to, $amount);
return response()->json([
'from' => $from,
'to' => $to,
'amount' => $amount,
'converted' => $result['result'],
'rate' => $result['rate'],
]);
}
public function latest(string $source = 'USD'): JsonResponse
{
$rate = $this->rates->getRates($source, request('target', 'EUR'))[0]['rate'];
return response()->json([
'source' => $source,
'target' => request('target', 'EUR'),
'rate' => $rate,
]);
}
} Caching with Laravel's Cache facade
Caching exchange rates reduces API calls and improves response times. Here is a production-ready service class that wraps AllRatesToday with Laravel's Cache facade:
<?php
namespace App\Services;
use AllRatesToday\AllRatesToday;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Http;
class ExchangeRateService
{
public function __construct(
private AllRatesToday $client
) {}
/**
* Get exchange rate with caching.
* Default TTL: 5 minutes (300 seconds).
*/
public function getRate(string $source, string $target, int $ttl = 300): float
{
$cacheKey = "fx_rate_{$source}_{$target}";
return Cache::remember($cacheKey, $ttl, function () use ($source, $target) {
return $this->client->getRates($source, $target)[0]['rate'];
});
}
/**
* Convert amount with cached rate.
*/
public function convert(string $source, string $target, float $amount): array
{
$rate = $this->getRate($source, $target);
return [
'source' => $source,
'target' => $target,
'amount' => $amount,
'result' => round($amount * $rate, 2),
'rate' => $rate,
];
}
/**
* Get historical rate (cached for 24 hours since historical data doesn't change).
* Dated queries (time=YYYY-MM-DD) need a paid plan.
*/
public function getHistoricalRate(string $source, string $target, string $date): float
{
$cacheKey = "fx_historical_{$source}_{$target}_{$date}";
return Cache::remember($cacheKey, 86400, function () use ($source, $target, $date) {
$rows = Http::withToken(config('services.allratestoday.key'))
->get('https://allratestoday.com/api/v1/rates', [
'source' => $source,
'target' => $target,
'time' => $date,
])
->throw()
->json();
return $rows[0]['rate'];
});
}
/**
* Flush all cached exchange rates.
*/
public function clearCache(): void
{
Cache::flush(); // Or use tagged cache for more precision
}
} Register it in your AppServiceProvider:
<?php
// app/Providers/AppServiceProvider.php
use App\Services\ExchangeRateService;
use AllRatesToday\AllRatesToday;
public function register(): void
{
$this->app->singleton(ExchangeRateService::class, function ($app) {
return new ExchangeRateService($app->make(AllRatesToday::class));
});
} Laravel Artisan command for rate updates
<?php
namespace App\Console\Commands;
use App\Services\ExchangeRateService;
use Illuminate\Console\Command;
class UpdateExchangeRates extends Command
{
protected $signature = 'rates:update {--source=USD} {--targets=EUR,GBP,JPY,CAD,AUD}';
protected $description = 'Fetch and cache the latest exchange rates';
public function handle(ExchangeRateService $service): int
{
$source = $this->option('source');
$targets = explode(',', $this->option('targets'));
$this->info("Updating rates for {$source}...");
foreach ($targets as $target) {
$rate = $service->getRate($source, trim($target), 600);
$this->line(" {$source}/{$target}: {$rate}");
}
$this->info('Rates updated and cached.');
return Command::SUCCESS;
}
} Schedule it in routes/console.php or app/Console/Kernel.php:
// routes/console.php (Laravel 11+)
Schedule::command('rates:update')->everyFiveMinutes(); Packagist: allratestoday/sdk — View on GitHub
- Data source: institutional foreign exchange market data
- Update frequency: Every 60 seconds (real-time)
- Currencies: 160+ including majors, minors, and exotics
- Rate type: Mid-market (no bank markup)
- Free tier: no credit card required
- Authentication: Bearer token
2. Open Exchange Rates
Open Exchange Rates has been around since 2012 but has no official PHP SDK. You need to use Guzzle or file_get_contents directly. There are community-maintained Composer packages, but they are not officially supported and may break without warning.
<?php
use GuzzleHttp\Client;
$http = new Client();
$appId = 'YOUR_APP_ID';
$response = $http->get("https://openexchangerates.org/api/latest.json", [
'query' => [
'app_id' => $appId,
'base' => 'USD', // Free tier: USD only
],
]);
$data = json_decode($response->getBody(), true);
$eurRate = $data['rates']['EUR'];
echo "1 USD = {$eurRate} EUR\n"; - Free tier: 1,000 requests/month, USD base only
- Update frequency: Hourly on free, more frequent on paid
- Limitation: Free plan locked to USD as base currency
- PHP SDK: Community only, not officially maintained
- Laravel integration: Manual setup required
3. Fixer.io
Fixer was popular years ago but has stagnated since its acquisition by APILayer. There is no PHP SDK, no Composer package, and the free tier is nearly unusable at 100 requests per month with EUR-only base currency and HTTP-only access (no HTTPS).
<?php
$apiKey = 'YOUR_API_KEY';
// Note: free tier is HTTP only, not HTTPS
$url = "http://data.fixer.io/api/latest?"
. http_build_query([
'access_key' => $apiKey,
'base' => 'EUR', // Free tier: EUR base only
'symbols' => 'USD,GBP,JPY',
]);
$response = file_get_contents($url);
$data = json_decode($response, true);
foreach ($data['rates'] as $currency => $rate) {
echo "EUR/{$currency}: {$rate}\n";
} - Free tier: 100 requests/month, EUR base only
- Update frequency: Daily
- Limitation: Free plan uses HTTP (not HTTPS), locked to EUR base
- PHP SDK: None
- Laravel integration: Manual setup required
4. CurrencyAPI
CurrencyAPI (currencyapi.com) does offer an official PHP package on Packagist, which puts it ahead of most competitors on developer experience. However, the free tier is limited to 300 free requests with daily-only rates, and there is no dedicated Laravel service provider.
<?php
// composer require currencyapi/currencyapi-php
use CurrencyApi\CurrencyApi\CurrencyApiClient;
$client = new CurrencyApiClient('YOUR_API_KEY');
$result = $client->latest([
'base_currency' => 'USD',
'currencies' => 'EUR,GBP,JPY',
]);
foreach ($result['data'] as $code => $info) {
echo "USD/{$code}: {$info['value']}\n";
} - Free tier: 300 free requests
- Update frequency: Daily on free tier
- PHP SDK: Official (currencyapi/currencyapi-php on Packagist)
- Laravel integration: No service provider; manual setup required
- Limitation: Historical data requires paid plan
5. ExchangeRate-API
ExchangeRate-API offers a simple REST interface with no PHP SDK and no Composer package. The free tier gives 1,500 requests per month with daily rates only.
<?php
$apiKey = 'YOUR_API_KEY';
$url = "https://v6.exchangerate-api.com/v6/{$apiKey}/latest/USD";
$response = file_get_contents($url);
$data = json_decode($response, true);
$eurRate = $data['conversion_rates']['EUR'];
echo "1 USD = {$eurRate} EUR\n";
echo "Last updated: {$data['time_last_update_utc']}\n"; - Free tier: 1,500 requests/month
- Update frequency: Daily
- PHP SDK: None
- Laravel integration: Manual setup required
- Limitation: No real-time rates, historical data requires paid plan
Laravel E-Commerce: Multi-Currency Pricing
A common use case is displaying product prices in the visitor's local currency. Here is a complete implementation using AllRatesToday with Laravel:
Middleware for currency detection
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
class DetectCurrency
{
private array $currencyMap = [
'US' => 'USD', 'GB' => 'GBP', 'DE' => 'EUR', 'FR' => 'EUR',
'JP' => 'JPY', 'CA' => 'CAD', 'AU' => 'AUD', 'CH' => 'CHF',
'IN' => 'INR', 'BR' => 'BRL', 'MX' => 'MXN', 'KR' => 'KRW',
];
public function handle(Request $request, Closure $next)
{
$currency = $request->query('currency')
?? $request->cookie('currency')
?? $this->detectFromCountry($request);
$request->merge(['display_currency' => $currency]);
return $next($request);
}
private function detectFromCountry(Request $request): string
{
$country = $request->header('CF-IPCountry', 'US');
return $this->currencyMap[$country] ?? 'USD';
}
} Blade helper for price display
<?php
// app/Helpers/CurrencyHelper.php
namespace App\Helpers;
use App\Services\ExchangeRateService;
class CurrencyHelper
{
public static function formatPrice(
float $amountUsd,
string $targetCurrency
): string {
if ($targetCurrency === 'USD') {
return '$' . number_format($amountUsd, 2);
}
$service = app(ExchangeRateService::class);
$converted = $service->convert('USD', $targetCurrency, $amountUsd);
$symbols = [
'EUR' => '€', 'GBP' => '£', 'JPY' => '¥',
'CAD' => 'CA$', 'AUD' => 'A$', 'CHF' => 'CHF ',
'INR' => '₹', 'BRL' => 'R$', 'MXN' => 'MX$',
];
$symbol = $symbols[$targetCurrency] ?? $targetCurrency . ' ';
return $symbol . number_format($converted['result'], 2);
}
} Use it in Blade templates:
{{-- resources/views/products/show.blade.php --}}
<div class="price">
{{ CurrencyHelper::formatPrice($product->price_usd, request('display_currency', 'USD')) }}
</div> Laravel API Routes
Add currency conversion endpoints to your Laravel API:
<?php
// routes/api.php
use App\Http\Controllers\CurrencyController;
Route::prefix('currency')->group(function () {
Route::get('/rate/{from}/{to}', [CurrencyController::class, 'latest']);
Route::get('/convert/{from}/{to}/{amount}', [CurrencyController::class, 'convert']);
}); Test it:
curl http://localhost:8000/api/currency/rate/USD/EUR
curl http://localhost:8000/api/currency/convert/USD/EUR/500 Why AllRatesToday Wins for PHP and Laravel
After comparing all five APIs, here is why AllRatesToday is the best choice for PHP and Laravel developers:
- Official Composer package:
composer require allratestoday/sdk— maintained by the AllRatesToday team, not a community fork that may break or go unmaintained. - Laravel-friendly: A plain client that binds into the service container in a few lines and works with dependency injection.
- Cache-friendly design: Works seamlessly with Laravel's Cache facade (Redis, Memcached, file, database). Cache exchange rates for 5 minutes and dramatically reduce your API call count.
- Real-time rates: Updated every 60 seconds from institutional foreign exchange market data. Every other free API on this list provides daily rates at best.
- Mid-market rates: The true mid-market rate with no markup. This is the rate expected in financial applications and audited systems.
- 160+ currencies: Covers majors, minors, and exotic currency pairs.
- Affordable historical data: Included on every paid plan from €4.99/month. Essential for month-end reconciliation and reporting.
- No credit card required: Sign up and start building immediately. Open Exchange Rates and Fixer require payment information.
- Clean API design: Standard REST conventions, Bearer token auth, consistent JSON responses. Works perfectly with Guzzle, Laravel HTTP client, or the official SDK.
Quick Reference
- Install:
composer require allratestoday/sdk - Config:
config/services.phpplus a singleton binding inAppServiceProvider - Env:
ALLRATESTODAY_API_KEY=your_key - Get rates:
GET /api/v1/rates?source=USD&target=EUR - Historical (paid plans):
GET /api/v1/rates?source=USD&target=EUR&time=2026-01-15 - Auth:
Authorization: Bearer YOUR_API_KEY - Packagist: allratestoday/sdk
- GitHub: allratestoday/exchange-rates-api
Start Building with PHP, Laravel, and Real-Time Rates
Get your free API key in 30 seconds. Install the SDK with composer require allratestoday/sdk and fetch real-time mid-market rates for 160+ currencies. Compare all options on our AllRatesToday exchange rate API page.