Reconcile Cross-Border Payments With FX Rate Data
Pull dated exchange rates with named providers to reconcile what cross-border payments should have converted at versus what actually settled.

TL;DR — The Currency Rates Scraper gives you a dated, provider-named market rate for any day. Join it to payment settlements and the spread between expected and actual conversion becomes a measurable line item instead of a shrug.
Why cross-border reconciliation needs a benchmark rate
Every international payment has two rates: the one you assumed and the one that settled. The difference is spread plus timing, and it adds up — BIS counts $7.5 trillion in daily FX volume, and none of it moves at the rate your invoice guessed.
Without an independent benchmark, "the bank gave us a bad rate" is a feeling. With one — dated, provider-named — it is a number you can trend, dispute, or budget for.
How does this compare to the alternatives?
| Trust the statement | Paid market-data feed | Thirdwatch actor | |
|---|---|---|---|
| Cost | Free but blind | Expensive | Pay per result |
| Reliability | No benchmark | Excellent | Named free sources |
| Setup time | None | Weeks | Minutes |
| Maintenance | None | Vendor-managed | Scheduled pulls |
A paid feed is overkill for reconciliation — you need a daily fix, not tick data. The actor gives exactly that, with provenance.
How to reconcile payments in 4 steps
How do I pull rates for a settlement period?
One range pull covers the whole month:
import os
from apify_client import ApifyClient
client = ApifyClient(os.environ["APIFY_TOKEN"])
run = client.actor("thirdwatch/currency-rates-scraper").call(
run_input={
"baseCurrencies": ["USD"],
"targetCurrencies": ["EUR", "GBP", "INR"],
"startDate": "2026-08-01",
"endDate": "2026-08-31",
"maxResults": 250,
}
)
rates = list(client.dataset(run["defaultDatasetId"]).iterate_items())How do I join payments to the right rate?
Match each settlement to the rate on its date:
import pandas as pd
rates_df = pd.DataFrame(rates)
rates_df["date"] = pd.to_datetime(rates_df["date"]).dt.date
payments = pd.read_csv("settlements.csv") # id, pair, date, expected, settled
merged = payments.merge(
rates_df[["pair", "date", "rate", "provider_name"]],
on=["pair", "date"], how="left")Missing joins mark days where the provider had no fix — forward-fill or flag them deliberately.
How do I compute the variance?
merged["benchmark_amount"] = merged["amount"] * merged["rate"]
merged["variance_pct"] = (merged["settled"] / merged["benchmark_amount"] - 1) * 100
print(merged.groupby("pair")["variance_pct"].mean())A consistent 2-4% gap is your processor's real spread. A sudden spike on one date is worth a support ticket — and the provider-named rate is your evidence.
How do I make it repeatable?
Schedule the pull monthly, append rates to a JSONL store, and the reconciliation becomes a join you rerun, not a project:
import json
with open("fx_benchmark.jsonl", "a") as f:
for r in rates:
f.write(json.dumps(r) + "\n")Sample output
{"base_currency": "USD", "quote_currency": "GBP", "pair": "USD/GBP",
"date": "2026-08-14", "rate": 0.7861, "inverse_rate": 1.2721,
"amount": 1, "converted_amount": 0.7861,
"provider": "erapi", "provider_name": "ExchangeRate-API open access"}date keys the join; provider_name is the citation when you dispute a settlement.
Common pitfalls
Settlement date and quote date differ — decide which one your policy benchmarks and be consistent. Weekend settlements map to the nearest published fix; document the convention. A benchmark shows market rate, not your processor's rate — the gap is the finding, not a bug. The actor names the provider per row so variance reports survive a source switch.
Related use cases
Frequently asked questions
What is FX reconciliation in one sentence?
Comparing the exchange rate a payment was expected to convert at — a dated, sourced market rate — against the rate that actually settled, and explaining the difference.
Why does the settled amount differ from the quoted amount?
Processors add spread and fees on top of mid-market rates, and settlement can happen days after the quote. A dated benchmark rate separates real cost from noise.
Can I pull the rate for a past settlement date?
Yes. Set the date field to the settlement or quote day and the actor returns that day's published fix with the provider named.
How do I batch a month of payments?
Use startDate/endDate to pull the whole period's daily rates once, then join payments to the rate for each settlement date locally.
Related
100 free credits, no credit card.
About 30 real searches. Add the MCP to Claude or Cursor in two minutes.