The two programmatic routes to Bank of Israel representative rates, what a response contains, the unit trap on yen, and how to store a rate you can explain.
Key takeaways
- The simple route returns current rates only, with a single update timestamp shared by the whole collection.
- The EDGE series database returns one series per code in the pattern `RER_<currency>_ILS`, by date range or by a count of latest observations.
- Not every currency is quoted per single unit. In our test the Japanese yen came back with a `unit` of 100 and the Lebanese pound with 10.
- No new rate is published on non-trading days, so a system expecting a daily value stalls on weekends and holidays.
- A price stored without the rate used to compute it cannot be explained, and that is the problem that surfaces two months after launch.
The Bank of Israel publishes representative rates on two programmatic routes: a simple endpoint returning the current rate for every currency, and the EDGE series database returning a historical series by code. Import pricing needs both - one for the daily refresh, the other to explain a price after the fact.
The two routes, and when to use each
| Property | Simple endpoint | EDGE series database |
|---|---|---|
| What it returns | Every currency, current rate only | One series by code, with history |
| Item selection | None - the whole collection | A series code such as RER_USD_ILS |
| Date range | None | startPeriod and endPeriod, or lastNObservations |
| Formats | JSON, plus XML via the asXML parameter | sdmx-json and csv |
| Typical use | Daily refresh of a rates table | History loads, after-the-fact checks |
In the test we ran, the simple endpoint returned 14 currencies: US dollar, euro, pound sterling, yen, Swiss franc, Canadian and Australian dollars, Danish, Norwegian and Swedish krone, South African rand, Jordanian dinar, Lebanese pound and Egyptian pound. If the currency you import in is not on that list, that is a decision to make up front rather than discover in production.
What does a response contain, and why does the unit field break calculations?
Each item in the simple route's response carries the currency code, the rate, a unit field and a percentage change, plus one lastUpdate timestamp shared across the whole collection. The unit field is the trap: it is not always 1.
In our test the Japanese yen came back at 1.9227 with a unit of 100 - meaning the value refers to 100 yen, not one. The Lebanese pound came back with a unit of 10. Code that multiplies an amount by the rate without dividing by the unit is wrong by a factor of a hundred, in precisely the currency where that is hardest to notice by eye.
Two practical consequences:
- Store the rate and the unit as two separate fields, never their quotient.
- Route every conversion through one function taking amount, currency and date, rather than scattered manual multiplication.
Which rate do you actually need - today's, or the one in force then?
It depends on what the line does. A quote to a customer needs a current rate, because it is forward-looking. A supplier invoice received two months ago needs the rate in force on that day, or the recorded amount cannot be explained.
So the correct separation is between a live rate and a locked rate:
- The live rate is fetched automatically and used only for display and for computing a new quote.
- The locked rate is written onto the document at creation, together with the date it came from and the unit.
- Regenerating a document uses its locked rate, and does not fetch a fresh one because somebody pressed print.
- A rate change does not update existing documents. It creates a new version, if a new version is wanted.
- A historical report reads the stored fields rather than re-fetching from the source.
The same principle of freezing a value onto the document appears in keeping one source of truth for price lists and quotes.
The separation sounds obvious until you look at what happens without it. A system recomputing every amount from the current rate shows a quote sent in January at a different total in March, and the salesperson opening it in front of the customer cannot tell which number is right. Once the rate is locked onto the document, "why did it come out like that" is answered by three stored fields rather than by reconstruction. In a business that also runs recurring contracts the same question returns at every renewal - see contract renewal reminder control.
What a daily fetch that breaks nobody looks like
A good daily fetch is a few lines of code and three decisions. The code requests the current collection, writes one row per currency carrying the rate, the unit and the publication date, and does not overwrite an existing row for the same date. The decisions are when it runs, what happens when the response is identical to yesterday, and who is told when no response arrived at all.
- Frequency. Once a day suits most businesses, preferably after the publication hour rather than at midnight.
- No change. A response identical to yesterday is not an error. Store it as the same publication, not as a new day.
- Silent failure. A fetch that failed and wrote nothing looks exactly like a day with no publication. Separate "no publication" from "we could not fetch" into two distinct statuses.
- Historical load. For the initial load, use the series database with a date range and run it once. There is no reason to re-fetch 2019 every morning.
What actually breaks in import pricing
- A day with no rate. Weekends, holidays and non-publication days leave a hole in the series. A fetch expecting a daily value needs an explicit rule: take the last publication preceding the date, and record which date was actually used.
- Time zone. The update timestamp arrives in UTC. A system tagging it as local time shifts a morning publication into the previous day.
- Rounding. Rounding at every intermediate step instead of at the last one produces agora-level differences that accumulate across many order lines.
- Double conversion. An amount converted to shekels, stored, then converted back for display almost never returns to the original number.
- Representative rate versus actual rate. The published rate is not the rate at which your commercial bank actually converted your money. That difference shows up in bank reconciliation, not in pricing.
That last gap ties directly into cost allocation on imports, covered in landed cost allocation control for an importer.
What to have ready before connecting
- The list of currencies the business genuinely trades in, checked against the list the source publishes.
- A decision on what "today's rate" means in your business, including what happens on a non-publication day.
- Fields on the document for rate, unit and rate date - three fields, not one.
- Somewhere to keep the raw fetch response, at least briefly, for investigation.
- A rule on who may override a rate manually, and where that is recorded.
Sources
Frequently asked questions
Why has the rate not changed since yesterday?
Because publication does not happen on every calendar day. In the test we ran on a Friday, the returned update timestamp was the previous day, which is ordinary behaviour over weekends and holidays. A system assuming a daily value needs a rule returning the last publication preceding the requested date, and recording which date was used.
How do I fetch a historical rate for a specific date?
Through the series database, using that currency's series code in the pattern `RER_<currency>_ILS`, with date-range parameters or a count of latest observations. The simple endpoint returns only the current state and is unsuitable for historical retrieval, including for a date as recent as yesterday.
What does the unit field in the response mean?
It states how many units of the currency the rate refers to. In our test the Japanese yen came back with a unit of 100 and the Lebanese pound with 10, while most currencies came back with 1. A calculation ignoring that field is wrong by an order of magnitude, in exactly the currencies where the error is hardest to spot.
Can I price purely from the exchange rate?
The rate is only one component. An import price is also shaped by conversion fees, freight, insurance and other costs allocated to the item. The rate fixes the conversion base, but a final price built from it alone will diverge from actual cost, which is why the conversion layer is normally kept separate from the cost allocation layer.
Keep reading
Related service
Business Automation
I build custom automations that remove repetitive work end to end.
About the author
Yehonatan Saadia
Freelance automation, web & MVP developer
I'm Yehonatan Saadia, a senior developer who builds business automation, custom websites, and MVPs for small and mid-sized companies across the US, Europe, and Israel. These guides come from real client work, not theory.
Work with meHave a project like this?
Tell me what you're trying to automate or build. I reply within 24 business hours with a few targeted questions, then we walk through it on a free 30-minute call, with no commitment. You come away with a scope, a timeline and a fixed price - or a straight answer that it isn't worth building.
