Which exchange does EURUSD trade on?
None. That is the honest answer, and it creates a problem for anyone building an API.
Foreign exchange is over-the-counter: a network of banks and electronic venues trading bilaterally, with no central order book and no consolidated tape. The Foreign Exchange Foundations course takes that structure apart. But an API needs somewhere to put a currency pair, because every symbol in the system is addressed as SYMBOL.EXCHANGE. So the vendor invents one.
A virtual exchange is an exchange code that names a data namespace rather than a venue. EODHD's FX namespace is FOREX, and the euro against the dollar is EURUSD.FOREX.
What is actually in the list
Called on 2026-07-28, /exchange-symbol-list/FOREX returned 998 rows, and /exchange-details/FOREX reported ActiveTickers: 998 the same day. Both figures drift, and they do not always agree with each other — three weeks later they read 994 and 997. Quote the count with the date you took it.
Every row carries the same seven fields as an equity row — Code, Name, Country, Exchange, Currency, Type, Isin — and three of them are hollow:
Typeis"Currency"on all 998.Countryis"Unknown"on all 998.Isinisnullon all 998. Not one exception.
That is not a data gap. A currency pair has no issuer, no country of incorporation and no security identifier, because it is not a security. The columns exist because the schema is shared with stocks; on FX rows they are structurally empty.
Currency does carry information: it is the quote currency of the pair. The EURUSD row reads USD; the USDJPY row reads JPY. Across the list there are 46 distinct values, and two of them are the literal strings "NA" and "Unknown" — used for crosses where the vendor has no clean answer. Treat that column as advisory, not authoritative.
The convention trap that will cost you a day
Of the 998 codes, 945 are six characters (EURUSD, GBPUSD, USDJPY), 52 are three characters (AED, EUR, CHF), and one is seven.
Those short codes are not abbreviations of the long ones. They are USD-based quotes, and the six-character codes follow market convention instead. Two independent pieces of evidence:
- The
AEDrow'sNameis "US Dollar/United Arab Emirates dirham FX Spot Rate", withCurrency: AED. SoAED.FOREXis USD/AED, not AED/USD. - Read on 2026-07-28, the closes for 2026-07-27 were 0.8796 for
EUR.FOREXand 1.1369 forEURUSD.FOREX. And 1 ÷ 0.8796 = 1.1369. They are reciprocals of each other, measured on the same day from the same endpoint. (Re-read on 28 September 2026, the EURUSD bar for that day had been restated to 1.1395 and the EUR bar had not, so those two stored closes no longer multiply to 1. Two quotes taken at the same instant still do, as step 3 below shows.)
So EUR.FOREX and EURUSD.FOREX are the same information quoted upside down, and both are in the same list. Load all 998 codes naively and you have double-counted 52 pairs with the sign of every return inverted on the duplicates. Reading the base/quote convention correctly, which Foreign Exchange Foundations covers, is what stops this.
Asset classes leak across namespaces
XAUUSD — gold against the dollar — sits in the FOREX list with Type: "Currency". Whether gold is a currency is a question for a different course; what matters here is that the namespace is a filing decision by the data vendor, not a statement about the nature of the instrument. The same metal is also reachable elsewhere in the API under a completely different code, which the commodities unit gets to.
One more practical note: /exchange-symbol-list/{exchangeCode} takes a type parameter, but its permitted values are common_stock, preferred_stock, stock, etf and fund. None of those matches what an FX row actually carries, so the filter is unusable on this exchange. You take all 998 and filter client-side.
And nothing in a row describes the currency's regime. USDCNY.FOREX and USDCNH.FOREX are both in the list, both Type: "Currency", and they are two markets: the onshore renminbi and the offshore one, quoted independently, with different closes and, in one batched call on 29 September 2026, timestamps 81 minutes apart. No field says which is deliverable or controlled, and a pegged pair such as USDHKD.FOREX carries exactly the same fields as a floating one. Pick a code because it matched the string CNY and you have chosen a market without knowing it.
Try it now
- Here is
/exchange-details/FOREX, with itsActiveTickerscount. The list itself,/exchange-symbol-list/FOREX, held 998 rows on 2026-07-28 and 997 on 28 September 2026, whenActiveTickersread 999. Quote the count you use with its date and its source, because it changes and the two sources disagree.
- Grouped by string length on 28 September 2026, the 997 codes were 944 of six characters, one of seven (
USDARSB) and these 52 of three:
| Three-character codes |
|---|
| AED ARS AUD BDT BRL CAD CHF CLP CNY CZK DKK EGP EUR GBP HKD HUF IDR ILS |
| INR ISK JPY KRW KYD LKR MXN MXV MYR NAD NGN NOK NZD PEN PHP PKR PLN RON |
| RUB SAR SEK SGD SHP SLE SRD THB TOP TRY TWD USD UYU VND ZAC ZAR |
That group is your duplicate set. Decide now whether you keep them, invert them or drop them, and say what you do with USD, whose row is named "US Dollar FX Spot Rate": the dollar quoted against itself.
3. Here are EUR.FOREX and EURUSD.FOREX from one /real-time call. Check that the two timestamps match, then multiply the two closes together. If the convention above holds, you should land very close to 1.
- Here are
USDCNY.FOREXandUSDCNH.FOREXfrom one batched call. Compute the gap between the two closes in per cent and the gap between the two timestamps in minutes. Then say which of the two numbers you would have to rule out before calling the first one a market difference.