How Do Currency Converter Apps Keep Exchange Rates Up to Date?
A currency converter stays current by fetching a complete rate observation, caching successful responses, refreshing on a controlled schedule, and preserving the market timestamp when a refresh fails or the market is closed.
A reliable converter does more than request a number on every page view. It stores the rate together with source, market_session, and data_updated_at, reuses that observation for a short period, and replaces it only after a successful refresh. The response time tells you when the API answered; the observation time tells you when the rate itself changed.
rates, sources, market_session, and data_updated_at.data_updated_at for data age. timestamp records when the API built the HTTP response.Treat a rate response as one observation
A rate without its metadata is ambiguous. The same numeric value may be an intraday observation, a daily reference fix, or a derived cross. A converter should therefore store the response as one unit rather than extracting rates.EUR and discarding the rest. The API reference defines the response fields, and the methodology explains how source and session labels are assigned.
source summarizes the least-fresh data class in a multi-currency response. Read the per-currency sources map when individual symbols can differ. derived_symbols identifies rates calculated through other currency legs rather than observed as a direct pair.
timestamp is the time the API assembled the response. data_updated_at is the time the underlying observation was last written. Display and freshness decisions should use data_updated_at.Choose a refresh policy before writing the timer
There is no universal refresh interval. A checkout preview, a dashboard, and an overnight reporting job have different requirements. Start with the maximum data age your feature accepts, then cache for that period. Requesting on every render wastes calls and can still return the same observation.
Actively traded currencies can update intraday through the trading week. Other currencies use daily reference series and move on their publication schedule. market_session is open, weekend, or interbank_closed; it lets the interface explain why an otherwise valid observation has not moved.
Build a small server-side cache in TypeScript
The cache below sorts the symbol list to create a stable key, stores only successful responses, and deduplicates concurrent refreshes. Keep authenticated calls on the server so the API key never enters a browser bundle.
This in-memory map is suitable for one long-running process. In serverless or multi-instance deployments, move the same cache entry to a shared store or use the hosting framework’s request cache. The data contract stays the same: value plus observation metadata, replaced only on success.
Handle a failed refresh without changing history
A timeout, 429 response, or upstream error is not a new market observation. Do not write zero, null, or the current clock time over the previous rate. If the feature allows last-known-good data, return the cached observation with its original data_updated_at and an application-level warning. If no valid observation exists, return an error.
For 429 responses, respect Retry-After when present and stop all instances from retrying at once. Exponential backoff with jitter is appropriate for transient failures. Authentication errors need a configuration fix, not repeated retries.
Market closures are a valid state
The interbank market is closed over the weekend. A response labeled market_session: weekend can correctly carry the final observation from the trading week. Keep that label and the original data_updated_at; do not present a newly fetched HTTP response as a new weekend quote.
A converter may lengthen its application cache while the market is closed, but it should still refresh after the trading week resumes. Base that decision on the returned session state and your product requirements, not on a hardcoded assumption that every currency follows one schedule.
Convert with the stored rate and round once
For a response based on USD, converting 25 USD to EUR is 25 × rates.EUR. Keep the rate at its returned precision and round the final display amount according to the target currency. Do not round the rate first, because the error grows with larger amounts.
Production checklist
- Request only the symbols the screen needs.
- Create a stable cache key from the base and sorted symbols.
- Cache complete successful observations, never HTTP errors.
- Deduplicate concurrent refreshes to prevent a cache stampede.
- Display
data_updated_atwhen the age of a rate matters. - Read the per-currency
sourcesmap for mixed responses. - Preserve
market_sessionandderived_symbols. - Keep authenticated calls and API keys on the server.
- Use last-known-good data only with its original timestamp and a clear state.
- Use the settlement provider for final charged amounts.