Tutorial/PHP and Laravel

Exchange rates in PHP and Laravel

Fetch and cache live exchange rates with Laravel’s HTTP client, bearer authentication, bounded timeouts, and freshness metadata.

MMexchangerate.dev·Jul 17, 2026·6 min read

A small Laravel service can call exchangerate.dev, reject failed HTTP responses, and cache a complete FX observation for 60 seconds. Keep the API key in configuration and return source, market_session, and data_updated_at with the rates.

Key points
Use Laravel’s HTTP client with bearer authentication, an explicit timeout, and throw() for non-success responses.
Cache the complete successful response for 60 seconds to stay well inside the Free tier’s 12 requests per minute.
Keep the key in config/services.php through EXCHANGERATE_API_KEY, not in application code.
Use a decimal library for calculations that require exact money arithmetic; PHP floats are binary floating point.

Build one cached FX service

The service below creates a stable cache key from the base and symbol list. The callback stores only a successful JSON response because throw() raises before Laravel writes a failed request into the cache.

php · app/Services/ExchangeRateService.phpcopy
<?php

namespace App\Services;

use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Http;

final class ExchangeRateService
{
    public function latest(string $base, array $symbols): array
    {
        sort($symbols);
        $symbolList = implode(',', $symbols);
        $cacheKey = "fx:latest:{$base}:{$symbolList}";

        return Cache::remember($cacheKey, now()->addSeconds(60), function () use (
            $base,
            $symbolList,
        ): array {
            return Http::acceptJson()
                ->withToken(config('services.exchangerate.key'))
                ->timeout(5)
                ->get(
                    "https://api.exchangerate.dev/v1/latest/{$base}",
                    ['symbols' => $symbolList],
                )
                ->throw()
                ->json();
        });
    }
}

Configure the key outside source control

php · config/services.php and .envcopy
// config/services.php
'exchangerate' => [
    'key' => env('EXCHANGERATE_API_KEY'),
],

// .env (never commit the real value)
EXCHANGERATE_API_KEY=exr_live_...

Read the rate and its observation fields

Call $service->latest("USD", ["EUR", "GBP"]), then read $result["rates"]["EUR"]. Keep $result["source"], $result["market_session"], and $result["data_updated_at"] in the view model or stored record so the value never loses its context.

Do not cache errors as rates
A 401 means the key or header is wrong. A 429 means the application has exceeded a request limit. Let both fail, back off where appropriate, and keep any prior successful observation only with its original update time.
  • Sort symbols before building the cache key.
  • Set a bounded HTTP timeout.
  • Call throw before reading and caching JSON.
  • Round the final amount for display, not the API rate.
MM
exchangerate.dev
Integration guides for developers building with FX data.

Keep reading

GuideHistorical FX rates and time series in one callRead ReferenceReading source and market_sessionRead GuideIndicative vs executable FX ratesRead