What relocation and expat tools need from a tax API
Relocation tools have specific requirements that differ from simple tax calculators:
- Common currency baseline — the user has one salary, expressed in one currency. The tool needs to evaluate what that income means in 10–20 different countries with different currencies and tax systems.
- Zero-tax jurisdiction support — for expat and digital nomad tools, the UAE, Qatar, Bahrain, and Cayman Islands are among the most searched destinations. The API must return valid zero-tax results (not errors).
- Current country comparison — the user's starting point. The value isn't just "what's my net in Singapore" — it's "how much more (or less) would I keep compared to what I keep now."
- Honest confidence labelling — some countries have complex tax systems (Germany's non-linear formula, US state-by-state variation) where simplified estimates are the best available model. Users should know when a result is approximate.
- Appropriate disclaimers — relocation decisions involve visa eligibility, residency rules, social security agreements, and personal circumstances. Tax is one factor. The tool must not overstate the precision of its output.
Currency conversion for meaningful comparisons
When a user enters their salary in one currency (say, £80,000 GBP) and wants to compare destinations in EUR, SGD, AED, and CHF — all calculations need to use the same real income value. The currency field handles this automatically.
// User earns £80,000. Compare UK vs Germany vs Singapore vs UAE.
// Use "GBP" as the baseline currency — all countries receive GBP 80,000 equivalent.
const countries = ["GB", "DE", "SG", "AE"];
const results = await Promise.all(
countries.map(country =>
fetch("https://api.countrytaxcalc.com/api/v1/calculate", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-API-Key": process.env.COUNTRYTAXCALC_API_KEY
},
body: JSON.stringify({
income: 80000,
currency: "GBP", // same baseline for all
country
})
}).then(r => r.json())
)
);
// Each result now has:
// - net_income in local currency (GBP, EUR, SGD, AED)
// - currency_conversion.exchange_rate used
// - currency_conversion.rate_date and source
// - effective_tax_rate: directly comparable across all four (same real income)
The currency_conversion object in each response gives you full provenance:
// Example: currency_conversion for UK when passing GBP (no conversion needed)
// For Germany when passing GBP:
"currency_conversion": {
"input_income": 80000,
"input_currency": "GBP",
"local_currency": "EUR",
"exchange_rate": 1.1742,
"converted_income": 93936,
"rate_date": "2026-07-07",
"rate_source": "api.frankfurter.dev (European Central Bank)",
"fx_source": "frankfurter",
"fx_rate_age_hours": 18
}
Surface fx_rate_age_hours to users if it's over 48 hours — you can show "exchange rates may be slightly outdated" for transparency. In practice ECB rates update every business day, so fx_rate_age_hours is almost always under 24.
Zero-tax jurisdictions
Zero-tax jurisdictions are among the most popular destination queries in relocation tools. The API handles them correctly — no special case required:
// UAE has zero income tax
// Response:
{
"country": "United Arab Emirates",
"country_code": "AE",
"gross_income": 93933, // £80,000 converted to AED equivalent
"breakdown": {
"deductions": [],
"taxable_income": 93933,
"national_income_tax": {
"amount": 0,
"effective_rate": 0,
"marginal_rate": 0,
"brackets_applied": []
},
"social_contributions": []
},
"totals": {
"total_tax": 0,
"net_income": 93933,
"effective_tax_rate": 0,
"take_home_percentage": 100,
"monthly_net_income": 7827.75
},
"metadata": {
"accuracy_confidence": 0.95,
"data_source": "UAE Ministry of Finance"
}
}
Zero-tax countries supported: UAE, Saudi Arabia, Bahrain, Qatar, Cayman Islands, and others. The full list is in GET /api/v1/countries. In your UI, these will always rank first in a net income sort — which is the correct result.
Current vs. destination comparison
The most useful output for a relocation tool is the delta — how much more or less the user would take home in the destination versus their current situation. Here's the complete pattern:
async function relocationComparison(currentCountry, destinationCountries, grossIncome, currency) {
// Fetch all at once: current country + all destinations
const allCountries = [currentCountry, ...destinationCountries];
const results = await Promise.all(
allCountries.map(country =>
fetch("https://api.countrytaxcalc.com/api/v1/calculate", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-API-Key": process.env.COUNTRYTAXCALC_API_KEY
},
body: JSON.stringify({ income: grossIncome, currency, country })
}).then(r => r.json())
)
);
const current = results[0];
const destinations = results.slice(1);
// For each destination, calculate the delta vs. current
const comparison = destinations.map(dest => {
// Note: delta is in respective local currencies — for cross-currency delta,
// you'd need to convert both to a common currency using the FX data
const netDelta = dest.totals.net_income - current.totals.net_income;
const rateDelta = dest.totals.effective_tax_rate - current.totals.effective_tax_rate;
return {
country: dest.country,
country_code: dest.country_code,
currency: dest.currency,
currency_symbol: dest.currency_symbol,
net_income: dest.totals.net_income,
monthly_net: dest.totals.monthly_net_income,
effective_rate: dest.totals.effective_tax_rate,
rate_delta: rateDelta.toFixed(2), // e.g. -8.5 means 8.5pp lower tax
confidence: dest.metadata.accuracy_confidence,
data_source: dest.metadata.data_source,
currency_conversion: dest.currency_conversion || null
};
});
// Sort: lowest effective tax rate first
comparison.sort((a, b) => a.effective_rate - b.effective_rate);
return {
current: {
country: current.country,
net_income: current.totals.net_income,
monthly_net: current.totals.monthly_net_income,
effective_rate: current.totals.effective_tax_rate,
currency: current.currency,
currency_symbol: current.currency_symbol
},
destinations: comparison
};
}
// Usage
const result = await relocationComparison(
"GB",
["DE", "NL", "IE", "SG", "AE", "CH", "PT"],
80000,
"GBP"
); Worked example: UK to Singapore comparison
A user earning £80,000 in the UK wants to understand their take-home in Singapore. Using GBP as the baseline currency:
| Country | Local net income | Effective rate | Monthly net | Source |
|---|---|---|---|---|
| UK (current) | £56,456 | 29.43% | £4,705/mo | HMRC |
| Singapore | SGD 123,420 | 14.1% | SGD 10,285/mo | IRAS |
| UAE | AED 375,720 | 0% | AED 31,310/mo | UAE Ministry of Finance |
The net income figures are in each country's local currency. To display the delta meaningfully, either use the exchange rate from currency_conversion to convert destination net to GBP, or compare effective rates directly — rates are always expressed as a percentage and are directly comparable regardless of currency.
Display guidance for relocation tools
Recommendations for how to present tax comparison data in a relocation context:
- Lead with effective rate — it's currency-agnostic and immediately comparable. "Singapore taxes 14.1% of this income" is clear.
- Show local net income with currency symbol — SGD 123,420 feels real; a USD conversion adds context but isn't the primary fact.
- Include monthly equivalent — monthly figures are more relatable for day-to-day planning than annual.
- Confidence indicator — for results with
accuracy_confidence < 0.9, prefix with "~" or add "(estimated)" — never present a 0.7-confidence result as a precise figure. - Data source attribution — show the
metadata.data_sourcefor each country ("Tax rates sourced from HMRC / IRAS / Bundeszentralamt für Steuern"). This builds trust with users who want to verify the numbers. - Exchange rate note — if displaying currency-converted figures, show the exchange rate date and source ("Exchange rates: ECB, [date]").
Honest limitations for relocation tools
The API is highly accurate for what it models — national income tax and standard employee social contributions for a resident taxpayer. There are several things it does not model that are directly relevant to relocation decisions:
- Residency qualification — many zero-tax or low-tax jurisdictions require minimum residency periods or specific visa types. The API tells you the tax rate if you were resident, not whether you qualify to be resident.
- Split-year treatment — the year of arriving in or departing a country often involves pro-rated tax treatment or dual residency periods. The API models a full year of standard residency.
- Social security implications — many countries have bilateral social security agreements that affect which country's contributions apply. National Insurance, pension contributions, and healthcare coverage are material relocation considerations beyond income tax.
- Municipal and local taxes — some countries have significant local-level taxes (e.g. Swiss cantonal taxes, US local income taxes) that may not be fully captured in the standard model.
- Cost of living — tax is one component of financial outcome. Housing, healthcare, childcare, and general costs of living are not reflected in the API data.
Frequently asked questions
Q: How do I compare the user's current country against a destination?
Call POST /calculate twice — once for the user's current country with their current salary, and once for the destination country with the equivalent salary (using the currency field to convert to a common baseline). The difference in totals.net_income is the annual gain or loss from the move — in local currency terms.
Q: How does currency conversion work for relocation comparisons?
Pass a currency field in each request — use the same currency for both requests (e.g. "currency": "USD"). The API converts the income to each country's local currency using ECB exchange rates, calculates tax in local terms, and returns the net income in local currency with full currency_conversion metadata. This means effective rates are calculated on the same real income value — directly comparable.
Q: Do zero-tax countries (UAE, Saudi Arabia) return valid responses?
Yes. The API returns a full valid response for zero-tax jurisdictions with totals.total_tax: 0, totals.effective_tax_rate: 0, and totals.net_income equal to gross income (after currency conversion). These are correct and expected. For relocation tools, zero-tax results are often the most compelling outcome — 'moving from Germany to the UAE on €100,000 income saves you ~€42,000/year in income tax' is a meaningful headline.
Q: What about cost of living — should I combine it with the tax data?
The API provides tax data only — it does not include cost of living, housing costs, or purchasing power data. For a complete relocation picture, combine the tax API with a cost of living API or static cost-of-living data. A common approach: show after-tax income from the tax API, then show an estimated purchasing power index alongside it. Keep the two data sources clearly labelled so users understand what's from each source.
Q: Does the API model non-resident tax treatment?
The API models standard resident tax treatment. If a user is planning to live in a country as a tax resident, this is the right model. It does not model: non-resident withholding tax, tax treaty benefits, split-year treatment for year of arrival/departure, or short-term visa tax positions. Add a clear note in your product that results assume full tax residency.
Q: What is the accuracy_confidence field and when should I show the 'estimated' label?
Each response includes metadata.accuracy_confidence. A value of 0.95 means the calculation uses verified official government data modelled with high precision. A value of 0.7 means the calculation is a simplified estimate — the country may have complex systems (municipality taxes, complex contribution structures) not fully captured by the model. Show an 'estimated' or '~' prefix on net income figures where accuracy_confidence < 0.9.