Skip to main content
Thirdwatchthirdwatch
Business & local data

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.

Sep 21, 2026 · 3 min read · 563 words
See the scraper →

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

Try it yourself

100 free credits, no credit card.

About 30 real searches. Add the MCP to Claude or Cursor in two minutes.