> ## Documentation Index
> Fetch the complete documentation index at: https://docs.d-edge.com/llms.txt
> Use this file to discover all available pages before exploring further.

> ## Agent Instructions
> This site documents the D-EDGE APIs for technology partners (web agencies, OTAs, PMS and RMS vendors, booking and CRM tools). It is written for developers integrating with hotels that use the D-EDGE platform.
> Seven APIs, grouped by domain. Connectivity: Channel Manager API (distribute a hotel's inventory to your channel and send bookings back). Pricing & Availability: Best Price API (best offers to highlight), Quotation API (all bookable offers for a stay), Daily Rates API (daily availability between two dates). Booking: Booking Engine API (a hotel's full direct booking funnel), Gift Shop API (gift vouchers from catalogue to payment). Guest Management: CRM API (guest profiles).
> Pick the API by the need: to display prices, use the Best Price API or the Daily Rates API; to quote a specific stay, use the Quotation API; to book, use the Booking Engine API; to sell gift vouchers, use the Gift Shop API; to manage guest profiles, use the CRM API; to receive hotel inventory and send bookings back as a distribution channel, use the Channel Manager API.
> Base URLs, hotel identifiers, and credentials are delivered by the D-EDGE partnership team during onboarding. Do not invent base URLs, endpoints, parameters, or example credentials that do not appear on these pages.
> Authentication differs per API: OAuth 1.0 for Best Price, Quotation, Daily Rates, and Booking Engine; an X-API-KEY header for Gift Shop; OAuth 2.0 client credentials for CRM; partner-defined credentials carried in the XML message for the Channel Manager API. Read each API's Authentication page before writing code.
> A mock environment returns realistic but fictitious data without credentials. The Postman and Insomnia collections on the API collections page target it by default. Use it for exploration and tests, never for production traffic.
> Use the official names exactly: D-EDGE, D-EDGE platform, Channel Manager API, partnership team. Do not use Back Office or Extranet.
> Each API has a Changelog page. Check it for the current behaviour before relying on an older answer. For questions these pages do not answer, direct the reader to the Contact us page.

# Multi-currency price display

> Show prices in the guest's currency with display-time conversion

A currency switcher lets guests browse prices in a currency they know, even when the property sells in another one. The Best Price API returns prices in the property's currency and does not convert them, so the conversion layer belongs to your interface. This page shows how to build it.

## Prerequisites

Before building the conversion layer, make sure you have:

* A working Best Price call. If not, follow the [Quick start](/best-price-api/get-started/quick-start.md) first.
* A hotel ID and a group ID for the property you display.
* A public source of daily exchange rates. The [European Central Bank reference rates](https://www.ecb.europa.eu/stats/policy_and_exchange_rates/euro_reference_exchange_rates/html/index.en.html) are free, published every working day, and cover the main currencies.

## How it works

The API returns every offer in the currency the property sells in. `filter.currency` selects, it does not convert: when a property sells in several currencies, the filter picks which one you receive. Requesting a currency the property does not sell in returns an empty response, not converted prices.

> [!NOTE]
>
> A guest browsing in EUR on a GBP-only property sees an empty calendar if you pass the guest's currency to the API. Keep the API currency and the display currency as two separate settings.

The pattern is one call in the property's currency, a daily exchange rate feed on the side, and a conversion at display time. The converted amount is indicative: the booking and the payment always happen in the property's currency.

## Build the conversion layer

> [!NOTE]
>
> The examples below assume the OAuth signing setup from the [Quick start](/best-price-api/get-started/quick-start.md).

### 1. Request prices in the property's currency

Set `filter.currency` to the currency the property sells in. If you do not know it yet, omit the filter on the first call, at a small performance cost: the service then uses the property's most used currency, and every response entry carries its `currency`.

```bash cURL theme={null}
curl --request POST \
  --url https://{apiBaseUrl}/Planning/2018A/BestPrice/Calendar \
  --header 'Authorization: OAuth ...' \
  --header 'Content-Type: application/json' \
  --data '{
    "beginDate": "2026-12-07",
    "endDate": "2027-01-05",
    "guests": [{"adults": 2}],
    "filter": {
      "ratePlan": {"hotelId": <hotelId>, "groupId": <groupId>},
      "currency": "GBP"
    }
  }'
```

```javascript Node.js theme={null}
const url = "https://{apiBaseUrl}/Planning/2018A/BestPrice/Calendar";

const response = await fetch(url, {
  method: "POST",
  headers: { ...authHeaders, "Content-Type": "application/json" },
  body: JSON.stringify({
    beginDate: "2026-12-07",
    endDate: "2027-01-05",
    guests: [{ adults: 2 }],
    filter: {
      ratePlan: { hotelId: <hotelId>, groupId: <groupId> },
      currency: "GBP",
    },
  }),
});
const data = await response.json();
```

```python Python theme={null}
response = session.post(
    "https://{apiBaseUrl}/Planning/2018A/BestPrice/Calendar",
    json={
        "beginDate": "2026-12-07",
        "endDate": "2027-01-05",
        "guests": [{"adults": 2}],
        "filter": {
            "ratePlan": {"hotelId": <hotelId>, "groupId": <groupId>},
            "currency": "GBP",
        },
    },
)
data = response.json()
```

```php PHP theme={null}
$response = $client->post('Planning/2018A/BestPrice/Calendar', [
    'json' => [
        'beginDate' => '2026-12-07',
        'endDate'   => '2027-01-05',
        'guests'    => [['adults' => 2]],
        'filter'    => [
            'ratePlan' => ['hotelId' => <hotelId>, 'groupId' => <groupId>],
            'currency' => 'GBP',
        ],
    ],
]);
$data = json_decode($response->getBody(), true);
```

```csharp C# theme={null}
var request = new RestRequest("/BestPrice/Calendar", Method.Post);
request.AddJsonBody(new
{
    beginDate = "2026-12-07",
    endDate = "2027-01-05",
    guests = new[] { new { adults = 2 } },
    filter = new
    {
        ratePlan = new { hotelId = <hotelId>, groupId = <groupId> },
        currency = "GBP"
    }
});

var response = await client.ExecuteAsync(request);
var data = JsonDocument.Parse(response.Content!).RootElement;
```

```java Java theme={null}
String body = mapper.writeValueAsString(Map.of(
    "beginDate", "2026-12-07",
    "endDate", "2027-01-05",
    "guests", List.of(Map.of("adults", 2)),
    "filter", Map.of(
        "ratePlan", Map.of("hotelId", <hotelId>, "groupId", <groupId>),
        "currency", "GBP"
    )
));

HttpPost request = new HttpPost("https://{apiBaseUrl}/Planning/2018A/BestPrice/Calendar");
request.setEntity(new StringEntity(body, "UTF-8"));
request.setHeader("Content-Type", "application/json");
consumer.sign(request);

JsonNode data;
try (CloseableHttpResponse response = client.execute(request)) {
    data = mapper.readTree(EntityUtils.toString(response.getEntity()));
}
```

### 2. Fetch the daily exchange rates

Load the reference rates once a day from your source and cache them. The examples below parse the ECB daily feed into a map of units per euro, with EUR as the pivot.

```javascript Node.js theme={null}
const ECB_URL = "https://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml";

async function fetchDailyRates() {
  const xml = await (await fetch(ECB_URL)).text();
  const rates = { EUR: 1 };
  for (const [, currency, rate] of xml.matchAll(/currency='([A-Z]{3})'\s+rate='([\d.]+)'/g)) {
    rates[currency] = Number(rate);
  }
  return rates; // units per euro, e.g. { EUR: 1, GBP: 0.855, USD: 1.08 }
}
```

```python Python theme={null}
import re
import requests

ECB_URL = "https://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml"

def fetch_daily_rates():
    xml = requests.get(ECB_URL).text
    rates = {"EUR": 1.0}
    for currency, rate in re.findall(r"currency='([A-Z]{3})'\s+rate='([\d.]+)'", xml):
        rates[currency] = float(rate)
    return rates  # units per euro, e.g. {"EUR": 1, "GBP": 0.855, "USD": 1.08}
```

```php PHP theme={null}
const ECB_URL = 'https://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml';

function fetchDailyRates(): array
{
    $rates = ['EUR' => 1.0];
    $xml = simplexml_load_file(ECB_URL);
    foreach ($xml->Cube->Cube->Cube as $cube) {
        $rates[(string) $cube['currency']] = (float) $cube['rate'];
    }
    return $rates; // units per euro, e.g. ['EUR' => 1, 'GBP' => 0.855, 'USD' => 1.08]
}
```

```csharp C# theme={null}
const string EcbUrl = "https://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml";

async Task<Dictionary<string, decimal>> FetchDailyRates()
{
    var xml = XDocument.Parse(await httpClient.GetStringAsync(EcbUrl));
    var rates = new Dictionary<string, decimal> { ["EUR"] = 1m };
    foreach (var cube in xml.Descendants().Where(e => e.Attribute("currency") != null))
    {
        rates[cube.Attribute("currency")!.Value] =
            decimal.Parse(cube.Attribute("rate")!.Value, CultureInfo.InvariantCulture);
    }
    return rates; // units per euro, e.g. EUR = 1, GBP = 0.855, USD = 1.08
}
```

```java Java theme={null}
String ECB_URL = "https://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml";

Map<String, Double> fetchDailyRates() throws Exception {
    String xml = new String(new URL(ECB_URL).openStream().readAllBytes(), StandardCharsets.UTF_8);
    Map<String, Double> rates = new HashMap<>();
    rates.put("EUR", 1.0);
    Matcher m = Pattern.compile("currency='([A-Z]{3})'\\s+rate='([\\d.]+)'").matcher(xml);
    while (m.find()) {
        rates.put(m.group(1), Double.parseDouble(m.group(2)));
    }
    return rates; // units per euro, e.g. EUR = 1, GBP = 0.855, USD = 1.08
}
```

### 3. Convert at display time

Convert each price from the `currency` carried by the response entry to the currency the guest picked, then round for display. Always convert from the exact amount the API returned, and keep that amount untouched for the booking hand-off.

```javascript Node.js theme={null}
function convertAmount(amount, source, target, rates) {
  return amount * (rates[target] / rates[source]);
}

data.forEach(day => {
  if (day.status === "Available") {
    const displayPrice = convertAmount(day.totalPrice, day.currency, guestCurrency, rates);
    renderCell(day.arrivalDate, {
      price: `≈ ${displayPrice.toFixed(0)} ${guestCurrency}`,
    });
  }
});
```

```python Python theme={null}
def convert_amount(amount, source, target, rates):
    return amount * rates[target] / rates[source]

for day in data:
    if day["status"] == "Available":
        display_price = convert_amount(day["totalPrice"], day["currency"], guest_currency, rates)
        render_cell(day["arrivalDate"], {"price": f"≈ {display_price:.0f} {guest_currency}"})
```

```php PHP theme={null}
function convertAmount(float $amount, string $source, string $target, array $rates): float
{
    return $amount * $rates[$target] / $rates[$source];
}

foreach ($data as $day) {
    if ($day['status'] === 'Available') {
        $displayPrice = convertAmount($day['totalPrice'], $day['currency'], $guestCurrency, $rates);
        renderCell($day['arrivalDate'], [
            'price' => sprintf('≈ %d %s', round($displayPrice), $guestCurrency),
        ]);
    }
}
```

```csharp C# theme={null}
decimal ConvertAmount(decimal amount, string source, string target, Dictionary<string, decimal> rates)
    => amount * rates[target] / rates[source];

foreach (var day in data.EnumerateArray())
{
    if (day.GetProperty("status").GetString() == "Available")
    {
        var displayPrice = ConvertAmount(
            day.GetProperty("totalPrice").GetDecimal(),
            day.GetProperty("currency").GetString()!,
            guestCurrency,
            rates);
        RenderCell(day.GetProperty("arrivalDate").GetString()!, new
        {
            price = $"≈ {Math.Round(displayPrice)} {guestCurrency}"
        });
    }
}
```

```java Java theme={null}
double convertAmount(double amount, String source, String target, Map<String, Double> rates) {
    return amount * rates.get(target) / rates.get(source);
}

for (JsonNode day : data) {
    if ("Available".equals(day.get("status").asText())) {
        double displayPrice = convertAmount(
            day.get("totalPrice").asDouble(), day.get("currency").asText(), guestCurrency, rates);
        renderCell(
            day.get("arrivalDate").asText(),
            Map.of("price", "≈ %.0f %s".formatted(displayPrice, guestCurrency))
        );
    }
}
```

### 4. Let the guest switch currency

A currency switch is a re-render, not a new API call. The response you already hold is in the property's currency, so apply the new target currency to the same data. Reserve new calls for changes that affect the offer itself: dates, party, or filters.

At the point where the guest commits, show the price in the property's currency as well, since that is the amount they will pay. A short mention such as "You will be charged in GBP" avoids surprises in the booking funnel.

## Tips

* **Keep two currency settings**. The API currency belongs to the property, the display currency belongs to the guest. Sending the guest's currency in `filter.currency` returns an empty response whenever the property does not sell in it, and the guest sees an empty calendar.
* **Refresh the rates once a day**. Align the refresh with your source's publication time (the ECB publishes around 16:00 CET on working days; weekends keep Friday's rates). Cache the map so a source outage never blanks your prices.
* **Say the amount is approximate**. The `≈` sign or a footnote is enough. Daily reference rates differ slightly from the rate the guest's bank applies, and the payment is taken in the property's currency.
* **Mind multi-currency properties**. When a property sells in more than one currency, `filter.currency` picks which one you receive. Pick one and keep it stable, so your cache and your conversions stay consistent.

## Related

> **[Dynamic price calendar](/best-price-api/resources/use-cases/dynamic-price-calendar.md)**
>
> The browse experience this conversion layer usually plugs into.

> **[Best practices](/best-price-api/build/best-practices.md)**
>
> Caching, performance, and resilience patterns.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.