Спросите у пяти разных сервисов курс EUR/USD прямо сейчас — и вы получите пять слегка различающихся чисел. Не радикально разных, но отличающихся в четвёртом знаке после запятой, иногда в третьем. Если ваша финансовая команда когда-нибудь заводила тикет из-за того, что чекаут показывал 1,0847, а в банковской выписке значилось 1,0821, вы уже знаете: это не академический вопрос.
Так откуда API валютных курсов берут данные? Честный ответ: ни один API не «знает» обменный курс, потому что единого обменного курса просто не существует. Валютный рынок — внебиржевой рынок без центральной биржи и без закрывающего звонка: тысячи организаций по всему миру котируют цены друг другу. Любой API, который вы можете вызвать, — это конвейер, который сэмплирует этот рынок, очищает данные и отдаёт вам одно число. Это руководство разбирает конвейер слой за слоем, объясняет, почему именно два провайдера расходятся, и показывает, как проверить источник до того, как строить на нём биллинговую логику.
Короткий ответ: три слоя между рынком и вашим JSON
Любой API валютных курсов — бесплатный или платный, включая наш — построен на одних и тех же трёх слоях:
- Сбор. Сырые цены забираются из вышестоящих источников: институциональных FX-фидов, публикаций центральных банков, котировок брокеров и розницы.
- Нормализация. Эти цены валидируются, выбросы отбрасываются, несколько источников смешиваются, и выводится единый референсный курс по каждой паре.
- Доставка. Полученный курс фиксируется снимком по расписанию, кешируется и отдаётся по HTTP с временной меткой.
Различия в любом из этих трёх слоёв дают другое число в теле вашего ответа. Большинство разработчиков считает, что расхождение возникает на слое 1. На практике слои 2 и 3 дают его ничуть не меньше.
Слой 1 — Откуда на самом деле берутся сырые цены
Межбанковские и институциональные фиды
Ближе всего к «настоящему» обменному курсу — межбанковский рынок: цены, по которым крупные банки и поставщики ликвидности торгуют друг с другом. Эти цены приходят непрерывными потоками котировок bid и ask с торговых площадок, от прайм-брокеров и поставщиков рыночных данных.
Такие фиды — источник наивысшей достоверности. Они же и самые дорогие, и именно поэтому бесплатные API редко используют их как основной источник. Когда провайдер обещает курсы «в реальном времени» или «с обновлением чаще минуты», почти всегда это означает, что в начале его конвейера стоят институциональные фиды.
Обратите внимание: межбанковский фид даёт вам две цены, а не одну — bid и ask. Единственный курс, который вы видите в ответе API, обычно является серединой между ними. Если это различие для вас новое, наше руководство о спреде bid-ask при обмене валют разбирает его подробно.
Референсные курсы центральных банков
Второй крупный источник — официальные публикации центральных банков. Самый известный пример: Европейский центральный банк публикует референсные валютные курсы евро в каждый рабочий день TARGET примерно в 16:00 CET на основе процедуры согласования между европейскими центробанками. Десятки других центральных банков публикуют аналогичные ежедневные курсы для своих валют.
У курсов центробанков два огромных преимущества: они бесплатны и авторитетны. Многие налоговые органы и стандарты бухгалтерского учёта прямо принимают их для отчётности. Именно поэтому значительная часть экосистемы бесплатных API построена на них. Frankfurter, широко используемый в этой области open-source проект, отслеживает ежедневные курсы 84 центральных банков по 201 валюте с историей вплоть до 1948 года — всё это публичные данные в перераспределённом виде.
У них есть и два серьёзных ограничения:
- Это ежедневные снимки, а не живые цены. Референсный курс на 16:00 CET ничего не говорит о том, что происходило в 09:00 или в 22:00.
- Они прекращаются в выходные и праздники. Если ваш API не возвращает данные за субботу или повторяет пятничное число — обычно объяснение в источнике, производном от ЕЦБ.
Розничные и брокерские котировки
Третий источник — цены для конечного клиента: то, что банк, карточная сеть, платёжный процессор или сервис переводов реально даст клиенту. Такие курсы уже включают наценку — маржу, зашитую в цену поверх рыночного курса.
Именно поэтому курс на потребительском сайте-сравнителе не совпадает с курсом в вашей банковской выписке. Ошибки нет ни там, ни там: они измеряют разные вещи. Потребительские сайты обычно показывают среднерыночный курс, а банк котирует вам среднерыночный курс плюс свой спред. В большинстве программных сценариев вам нужно среднерыночное число, а собственную наценку стоит применять явно — там, где вы её видите и можете проверить.
Слой 2 — Как провайдеры превращают фиды в один курс
Как только сырые цены поступили, провайдер должен решить, какое число публиковать. Здесь принимаются четыре решения, и каждое из них — точка расхождения между провайдерами.
Смешивание (blending). Большинство коммерческих API не опираются на один вышестоящий источник. Например, Open Exchange Rates описывает свои данные как собранные у нескольких провайдеров и смешанные алгоритмически. Смешивание сглаживает единичный плохой тик, но веса смешивания проприетарны — и именно поэтому два смешанных фида никогда не совпадают в точности.
Отбрасывание выбросов. Ошибочная котировка с одной площадки может отличаться на порядок. Провайдеры применяют фильтры, отсекающие цены за пределами полосы допуска вокруг консенсуса. Агрессивная фильтрация даёт стабильные курсы, но замедляет реакцию на настоящие движения. Мягкая фильтрация даёт быструю реакцию, но временами шум.
Вывод середины. Если вышестоящий фид даёт bid/ask, провайдер публикует середину. Простая середина (bid + ask) / 2 — стандарт, но взвешенные по объёму подходы дают слегка иной результат.
Триангуляция кросс-курсов. Ни один провайдер не получает напрямую все 30 000+ возможных валютных пар. Вместо этого большинство пар вычисляется через опорную валюту — обычно USD или EUR:
GBP/JPY = (USD/JPY) / (USD/GBP)Это значит, что курс экзотической пары наследует округление и тайминг двух других пар. Провайдеры, опирающиеся на USD, и провайдеры, опирающиеся на EUR, придут к разным числам для одного и того же кросса. Механику мы разбираем в статье кросс-курсы простыми словами.
Слой 3 — Как курс попадает в ваш код
Последний слой — тот, который разработчики контролируют больше всего и обдумывают меньше всего.
Частота обновления — главный отличительный фактор между провайдерами и между тарифами. Бесплатные планы обычно обновляются один-два раза в сутки. Платные уровни обновляются ежечасно, каждые десять минут или каждые 60 секунд. Два API на идентичных данных разойдутся просто потому, что один сделал снимок в 14:00, а другой в 14:47.
Кеширование усугубляет это. Большинство API стоит за CDN, а грамотные клиенты кешируют локально поверх. Добавьте 15-минутный edge-кеш к 10-минутному обновлению — и ваше приложение потенциально работает с курсом 25-минутной давности. Для отображения цен это нормально, для расчёта по сделке — нет; практический вопрос всегда звучит как насколько устаревшим курс становится неприемлемым именно для этой операции. Наше руководство по кешированию и обработке ошибок в валютных API объясняет, как подбирать эти окна.
Временные метки — ваша защита. Любой серьёзный API возвращает момент фиксации курса. Читайте его. Не считайте, что момент получения ответа — это момент, когда курс был верен:
const MAX_AGE_SECONDS = 900; // 15 minutes
async function getRate(base, symbol) {
const res = await fetch(
`https://api.finexly.com/v1/latest?base=${base}&symbols=${symbol}`,
{ headers: { Authorization: `Bearer ${process.env.FINEXLY_API_KEY}` } }
);
const data = await res.json();
const ageSeconds = Math.floor(Date.now() / 1000) - data.timestamp;
if (ageSeconds > MAX_AGE_SECONDS) {
throw new Error(`Rate is ${ageSeconds}s old — refusing to price on stale data`);
}
return { rate: data.rates[symbol], ageSeconds };
}Вот сам запрос и репрезентативная структура ответа:
curl "https://api.finexly.com/v1/latest?base=USD&symbols=EUR,GBP,JPY" \
-H "Authorization: Bearer YOUR_API_KEY"{
"success": true,
"base": "USD",
"timestamp": 1755244800,
"rates": {
"EUR": 0.9241,
"GBP": 0.7863,
"JPY": 147.2150
}
}Полное описание параметров и эндпоинтов — в документации API Finexly.
Почему два API возвращают разные числа по одной паре
Собрав три слоя вместе, получаем шесть причин расхождения, примерно в порядке наносимого ущерба:
- Разное время снимка. Самая частая причина с большим отрывом. Ни один из фидов не сломан — они просто посмотрели в разные моменты.
- Разный набор источников. Курс, производный от центробанка, и курс, производный от межбанка, по определению измеряют разные вещи.
- Среднерыночный против курса с наценкой. Один провайдер отдаёт середину рынка, другой — клиентскую цену со спредом уже внутри.
- Разные опорные валюты для кроссов. Триангуляция через USD и через EUR даёт разные результаты для одной и той же не-USD пары.
- Точность и округление. Шесть знаков, усечённые до четырёх, либо курсы, опубликованные как обратные пары и заново инвертированные, — оба варианта дают дрейф.
- Забытые слои кеша. Ваш CDN, HTTP-кеш фреймворка и собственный слой Redis — каждый добавляет возраст.
Полезное правило: для основных пар расхождение в несколько базисных пунктов (0,01% = 1 б.п.) между двумя авторитетными среднерыночными источниками нормально и ожидаемо. Расхождение в 50 б.п. и больше означает, что один из двух устарел, содержит наценку или сломан — и это стоит выяснить до релиза.
Как проверить API валютных курсов, прежде чем ему доверять
Не принимайте заявления провайдера о точности на веру. Прогоняйте эту проверку неделю против того источника, который ваша финансовая команда считает авторитетным:
import os
import requests
from datetime import datetime, timezone
FINEXLY_URL = "https://api.finexly.com/v1/latest"
HEADERS = {"Authorization": f"Bearer {os.environ['FINEXLY_API_KEY']}"}
def get_rate(base: str, symbol: str) -> dict:
r = requests.get(
FINEXLY_URL,
headers=HEADERS,
params={"base": base, "symbols": symbol},
timeout=5,
)
r.raise_for_status()
data = r.json()
return {
"rate": data["rates"][symbol],
"captured_at": datetime.fromtimestamp(data["timestamp"], tz=timezone.utc),
}
def basis_points(a: float, b: float) -> float:
"""Difference between two rates, in basis points."""
return abs(a - b) / ((a + b) / 2) * 10_000
primary = get_rate("EUR", "USD")
reference = 1.0839 # whatever your accounting source published
diff = basis_points(primary["rate"], reference)
print(f"Finexly: {primary['rate']} captured {primary['captured_at']:%H:%M UTC}")
print(f"Reference: {reference}")
print(f"Delta: {diff:.1f} bp -> {'OK' if diff < 25 else 'INVESTIGATE'}")Три вещи, на которые стоит смотреть в результатах:
- Расхождение стабильно или дрейфует? Постоянное смещение говорит о систематической наценке. Случайное — о тайминге.
- Расхождение всплескивает в определённые часы? Это указывает на время снимка, обычно рядом с окном публикации центробанка.
- Что происходит в выходные? Если источник замирает в пятницу днём и оживает в понедельник — он производный от центробанка; планируйте понедельничную сверку с учётом этого.
Триангуляцию тоже можно проверить: запросите кросс напрямую и вычислите его через USD — расхождение должно укладываться в один-два базисных пункта.
Как выбрать источник данных под ваш сценарий
Универсально «лучшего» источника нет — есть источник, подходящий именно тому, что вы строите.
| Сценарий | Что нужно | Допустимый возраст данных |
|---|---|---|
| Показ цен покупателям | Среднерыночный курс, ваша наценка сверху | Часы |
| Биллинг SaaS-подписок | Среднерыночный, один снимок на биллинг-прогон, сохранённый со счётом | Часы, но обязательно с записью |
| Бухгалтерия и налоговая отчётность | Референсный курс центробанка на конкретную дату | Ежедневно, по определению |
| Аналитика и дашборды | Согласованный исторический ряд из одного источника | Ежедневно |
| Выплаты и переводы | Свежий среднерыночный курс с явной полосой допуска | Минуты |
| Трейдинг и хеджирование | Настоящие bid/ask из институционального фида | Секунды |
Если вы всё ещё выбираете, наше сравнение бесплатных и платных валютных API объясняет, что меняется при переходе на старшие тарифы, а страница тарифных планов показывает, где находятся частота обновления и лимиты запросов. Для быстрой ручной проверки любой пары конвертер валют использует тот же базовый фид, что и API.
Часто задаваемые вопросы
Откуда бесплатные валютные API берут данные?
Почти всегда из публикаций центральных банков, чаще всего из ежедневных референсных курсов евро Европейского центрального банка, иногда в смеси с несколькими другими открытыми источниками. Поэтому бесплатные тарифы обычно обновляются раз в сутки, пропускают выходные и покрывают меньше экзотических валют, чем платные.
Почему курс в моём API отличается от курса в Google?
Google показывает среднерыночный референсный курс — это снимок, а не непрерывная живая цена, и он не обязательно взят в тот же момент, что и ваш вызов API. Небольшая разница нормальна. Большая обычно означает, что один из двух — розничный курс с наценкой, а не среднерыночный.
Какой курс использовать для бухгалтерии и налоговой отчётности?
Используйте официальный референсный курс профильного центрального банка на дату операции — именно этого ждёт большинство налоговых органов. Получайте его через исторический эндпоинт с явной датой, а не переиспользуйте живой курс, и сохраняйте вместе с записью о транзакции.
Действительно ли API курсов «в реальном времени» работает в реальном времени?
В буквальном смысле — редко. «Реальное время» обычно значит, что провайдер обновляется с коротким интервалом: 60 секунд типично для верхнего тарифа, — а не что он транслирует каждый тик. Смотрите на временную метку в ответе и на задокументированный интервал обновления, а не на маркетинговый текст.
Можно ли просто парсить курсы вместо использования API?
Можно, но вы наследуете все режимы отказа страницы, которую парсите: изменения вёрстки, ограничения по частоте, отсутствие временных меток, отсутствие исторической подгрузки и, нередко, нарушение условий использования. Полный разбор компромиссов — в статье валютный API против веб-скрейпинга.
Стройте на фиде, который можно проверить
Понимание того, откуда берутся ваши курсовые данные, — это разница между валютным багом, который вы объясняете одной фразой, и багом, съедающим неделю инженерного времени. Задайте любому провайдеру три вопроса перед интеграцией: каковы источники, как часто идёт обновление и несёт ли каждый ответ метку времени фиксации.
Готовы интегрировать курсы, которые действительно можно проверить? Получите бесплатный ключ API Finexly — без банковской карты. Начните с 1 000 бесплатных запросов в месяц по 170+ валютам, с ответами с временными метками и историческими данными с первого дня.
Explore More
Vlado Grigirov
Senior Currency Markets Analyst & Financial Strategist
Vlado Grigirov is a senior currency markets analyst and financial strategist with over 14 years of experience in foreign exchange markets, cross-border finance, and currency risk management. He has wo...
View full profile →