rate-api-python 1.0.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- rate_api_python-1.0.0/LICENSE +21 -0
- rate_api_python-1.0.0/PKG-INFO +71 -0
- rate_api_python-1.0.0/README.md +57 -0
- rate_api_python-1.0.0/pyproject.toml +20 -0
- rate_api_python-1.0.0/rate_api/__init__.py +183 -0
- rate_api_python-1.0.0/rate_api_python.egg-info/PKG-INFO +71 -0
- rate_api_python-1.0.0/rate_api_python.egg-info/SOURCES.txt +8 -0
- rate_api_python-1.0.0/rate_api_python.egg-info/dependency_links.txt +1 -0
- rate_api_python-1.0.0/rate_api_python.egg-info/top_level.txt +1 -0
- rate_api_python-1.0.0/setup.cfg +4 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Rate-API.com
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: rate-api-python
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Official Python client for the Rate-API.com exchange-rate & crypto API
|
|
5
|
+
License: MIT
|
|
6
|
+
Project-URL: Homepage, https://rate-api.com
|
|
7
|
+
Project-URL: Documentation, https://rate-api.com/en/docs
|
|
8
|
+
Project-URL: Repository, https://github.com/Vilgar/rate-api.com
|
|
9
|
+
Keywords: exchange-rate,currency,forex,crypto,api,rate-api
|
|
10
|
+
Requires-Python: >=3.8
|
|
11
|
+
Description-Content-Type: text/markdown
|
|
12
|
+
License-File: LICENSE
|
|
13
|
+
Dynamic: license-file
|
|
14
|
+
|
|
15
|
+
# rate-api-python
|
|
16
|
+
|
|
17
|
+
Official Python client for [Rate-API.com](https://rate-api.com). Standard library only — no dependencies. Python 3.8+.
|
|
18
|
+
|
|
19
|
+
## Install
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
pip install rate-api-python
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Usage
|
|
26
|
+
|
|
27
|
+
```python
|
|
28
|
+
from rate_api import RateApiClient, RateApiError
|
|
29
|
+
|
|
30
|
+
client = RateApiClient("YOUR_API_KEY")
|
|
31
|
+
|
|
32
|
+
rates = client.latest("USD", ["EUR", "GBP"])
|
|
33
|
+
print(rates["rates"]["EUR"])
|
|
34
|
+
|
|
35
|
+
client.convert("USD", "EUR", 100) # Pro+
|
|
36
|
+
client.historical("2026-01-15", "USD", ["EUR"]) # Pro+
|
|
37
|
+
client.timeseries("2026-01-01", "2026-01-31") # Business+
|
|
38
|
+
client.crypto(["BTC", "ETH"]) # Pro+
|
|
39
|
+
client.health() # public
|
|
40
|
+
|
|
41
|
+
try:
|
|
42
|
+
client.timeseries("2020-01-01", "2026-12-31")
|
|
43
|
+
except RateApiError as e:
|
|
44
|
+
print(e, e.status) # "Date range too large. Maximum is 366 days." 400
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## v2 features
|
|
48
|
+
|
|
49
|
+
The client exposes the v2 endpoints directly (they resolve to `/api/v2` regardless of base URL):
|
|
50
|
+
|
|
51
|
+
```python
|
|
52
|
+
# Latest with 24h change, metadata and precision
|
|
53
|
+
r = client.latest_v2("USD", ["EUR", "GBP"], include_change=True, include_metadata=True, precision=4)
|
|
54
|
+
print(r["changes_pct"]["EUR"])
|
|
55
|
+
|
|
56
|
+
# Historical comparison between two dates (Pro+)
|
|
57
|
+
cmp = client.historical_compare("2026-01-15", "2026-01-01", "USD", ["EUR"])
|
|
58
|
+
|
|
59
|
+
# Batch conversion — up to 100 pairs in one call (Pro+)
|
|
60
|
+
batch = client.batch_convert([
|
|
61
|
+
{"from": "USD", "to": "EUR", "amount": 100},
|
|
62
|
+
{"from": "GBP", "to": "JPY", "amount": 50},
|
|
63
|
+
])
|
|
64
|
+
|
|
65
|
+
# Your configured rate alerts (Business+)
|
|
66
|
+
alerts = client.alerts()
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## License
|
|
70
|
+
|
|
71
|
+
MIT
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# rate-api-python
|
|
2
|
+
|
|
3
|
+
Official Python client for [Rate-API.com](https://rate-api.com). Standard library only — no dependencies. Python 3.8+.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pip install rate-api-python
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
```python
|
|
14
|
+
from rate_api import RateApiClient, RateApiError
|
|
15
|
+
|
|
16
|
+
client = RateApiClient("YOUR_API_KEY")
|
|
17
|
+
|
|
18
|
+
rates = client.latest("USD", ["EUR", "GBP"])
|
|
19
|
+
print(rates["rates"]["EUR"])
|
|
20
|
+
|
|
21
|
+
client.convert("USD", "EUR", 100) # Pro+
|
|
22
|
+
client.historical("2026-01-15", "USD", ["EUR"]) # Pro+
|
|
23
|
+
client.timeseries("2026-01-01", "2026-01-31") # Business+
|
|
24
|
+
client.crypto(["BTC", "ETH"]) # Pro+
|
|
25
|
+
client.health() # public
|
|
26
|
+
|
|
27
|
+
try:
|
|
28
|
+
client.timeseries("2020-01-01", "2026-12-31")
|
|
29
|
+
except RateApiError as e:
|
|
30
|
+
print(e, e.status) # "Date range too large. Maximum is 366 days." 400
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## v2 features
|
|
34
|
+
|
|
35
|
+
The client exposes the v2 endpoints directly (they resolve to `/api/v2` regardless of base URL):
|
|
36
|
+
|
|
37
|
+
```python
|
|
38
|
+
# Latest with 24h change, metadata and precision
|
|
39
|
+
r = client.latest_v2("USD", ["EUR", "GBP"], include_change=True, include_metadata=True, precision=4)
|
|
40
|
+
print(r["changes_pct"]["EUR"])
|
|
41
|
+
|
|
42
|
+
# Historical comparison between two dates (Pro+)
|
|
43
|
+
cmp = client.historical_compare("2026-01-15", "2026-01-01", "USD", ["EUR"])
|
|
44
|
+
|
|
45
|
+
# Batch conversion — up to 100 pairs in one call (Pro+)
|
|
46
|
+
batch = client.batch_convert([
|
|
47
|
+
{"from": "USD", "to": "EUR", "amount": 100},
|
|
48
|
+
{"from": "GBP", "to": "JPY", "amount": 50},
|
|
49
|
+
])
|
|
50
|
+
|
|
51
|
+
# Your configured rate alerts (Business+)
|
|
52
|
+
alerts = client.alerts()
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## License
|
|
56
|
+
|
|
57
|
+
MIT
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "rate-api-python"
|
|
7
|
+
version = "1.0.0"
|
|
8
|
+
description = "Official Python client for the Rate-API.com exchange-rate & crypto API"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = { text = "MIT" }
|
|
11
|
+
requires-python = ">=3.8"
|
|
12
|
+
keywords = ["exchange-rate", "currency", "forex", "crypto", "api", "rate-api"]
|
|
13
|
+
|
|
14
|
+
[project.urls]
|
|
15
|
+
Homepage = "https://rate-api.com"
|
|
16
|
+
Documentation = "https://rate-api.com/en/docs"
|
|
17
|
+
Repository = "https://github.com/Vilgar/rate-api.com"
|
|
18
|
+
|
|
19
|
+
[tool.setuptools]
|
|
20
|
+
packages = ["rate_api"]
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
"""Official Python client for the Rate-API.com exchange-rate & crypto API.
|
|
2
|
+
|
|
3
|
+
Uses only the standard library (urllib) — no dependencies.
|
|
4
|
+
|
|
5
|
+
from rate_api import RateApiClient
|
|
6
|
+
rates = RateApiClient("YOUR_API_KEY").latest("USD", ["EUR", "GBP"])
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import json
|
|
10
|
+
import time
|
|
11
|
+
import urllib.error
|
|
12
|
+
import urllib.parse
|
|
13
|
+
import urllib.request
|
|
14
|
+
|
|
15
|
+
__version__ = "1.0.0"
|
|
16
|
+
__all__ = ["RateApiClient", "RateApiError", "RateLimitError", "RateApiTimeoutError"]
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class RateApiError(Exception):
|
|
20
|
+
def __init__(self, message, status=None, type=None, request_id=None):
|
|
21
|
+
super().__init__(message)
|
|
22
|
+
self.status = status # HTTP status (None for network/timeout)
|
|
23
|
+
self.type = type # stable error.type slug from the API
|
|
24
|
+
self.request_id = request_id # X-Request-Id for support correlation
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class RateLimitError(RateApiError):
|
|
28
|
+
"""Raised on HTTP 429; carries retry_after (seconds)."""
|
|
29
|
+
def __init__(self, message, status=None, type=None, request_id=None, retry_after=60):
|
|
30
|
+
super().__init__(message, status, type, request_id)
|
|
31
|
+
self.retry_after = retry_after
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class RateApiTimeoutError(RateApiError):
|
|
35
|
+
"""Raised when a request exceeds the timeout."""
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class RateApiClient:
|
|
39
|
+
def __init__(self, api_key, base_url="https://rate-api.com/api/v1", timeout=15, max_retries=2):
|
|
40
|
+
if not api_key:
|
|
41
|
+
raise RateApiError("An API key is required.")
|
|
42
|
+
self.api_key = api_key
|
|
43
|
+
self.base_url = base_url.rstrip("/")
|
|
44
|
+
self.timeout = timeout
|
|
45
|
+
self.max_retries = max_retries
|
|
46
|
+
|
|
47
|
+
def latest(self, base="USD", symbols=None):
|
|
48
|
+
return self._get("latest", self._with_symbols({"base": base}, symbols))
|
|
49
|
+
|
|
50
|
+
def convert(self, from_currency, to_currency, amount):
|
|
51
|
+
return self._get("convert", {"from": from_currency, "to": to_currency, "amount": amount})
|
|
52
|
+
|
|
53
|
+
def historical(self, date, base="USD", symbols=None):
|
|
54
|
+
return self._get("historical", self._with_symbols({"date": date, "base": base}, symbols))
|
|
55
|
+
|
|
56
|
+
def pair(self, from_currency, to_currency):
|
|
57
|
+
return self._get(f"pair/{urllib.parse.quote(from_currency)}/{urllib.parse.quote(to_currency)}")
|
|
58
|
+
|
|
59
|
+
def timeseries(self, start_date, end_date, base="USD", symbols=None):
|
|
60
|
+
return self._get("timeseries", self._with_symbols(
|
|
61
|
+
{"start_date": start_date, "end_date": end_date, "base": base}, symbols))
|
|
62
|
+
|
|
63
|
+
def fluctuation(self, start_date, end_date, base="USD", symbols=None):
|
|
64
|
+
return self._get("fluctuation", self._with_symbols(
|
|
65
|
+
{"start_date": start_date, "end_date": end_date, "base": base}, symbols))
|
|
66
|
+
|
|
67
|
+
def crypto(self, symbols=None):
|
|
68
|
+
query = {"symbols": ",".join(symbols)} if symbols else {}
|
|
69
|
+
return self._get("crypto", query)
|
|
70
|
+
|
|
71
|
+
def currencies(self):
|
|
72
|
+
return self._get("currencies")
|
|
73
|
+
|
|
74
|
+
def health(self):
|
|
75
|
+
return self._request(f"{self.base_url}/health", {})
|
|
76
|
+
|
|
77
|
+
# ---- v2 endpoints (resolve to /api/v2 regardless of the configured base) ----
|
|
78
|
+
|
|
79
|
+
def _v2_base(self):
|
|
80
|
+
if self.base_url.endswith("/v1"):
|
|
81
|
+
return self.base_url[:-3] + "/v2"
|
|
82
|
+
return self.base_url.replace("/v1/", "/v2/")
|
|
83
|
+
|
|
84
|
+
def latest_v2(self, base="USD", symbols=None, include_metadata=False, include_change=False, precision=None):
|
|
85
|
+
"""v2 latest with metadata / 24h change / precision options."""
|
|
86
|
+
query = self._with_symbols({"base": base}, symbols)
|
|
87
|
+
if include_metadata:
|
|
88
|
+
query["include_metadata"] = "true"
|
|
89
|
+
if include_change:
|
|
90
|
+
query["include_change"] = "true"
|
|
91
|
+
if precision is not None:
|
|
92
|
+
query["precision"] = precision
|
|
93
|
+
return self._request(f"{self._v2_base()}/{self.api_key}/latest", query)
|
|
94
|
+
|
|
95
|
+
def historical_compare(self, date, compare_date=None, base="USD", symbols=None):
|
|
96
|
+
"""v2 historical with an optional compare_date for per-currency deltas (Pro+)."""
|
|
97
|
+
query = self._with_symbols({"date": date, "base": base}, symbols)
|
|
98
|
+
if compare_date:
|
|
99
|
+
query["compare_date"] = compare_date
|
|
100
|
+
return self._request(f"{self._v2_base()}/{self.api_key}/historical", query)
|
|
101
|
+
|
|
102
|
+
def batch_convert(self, conversions):
|
|
103
|
+
"""v2 batch conversion: list of {"from","to","amount"} dicts (Pro+). Max 100."""
|
|
104
|
+
return self._request(f"{self._v2_base()}/{self.api_key}/batch-convert", {}, method="POST", body=conversions)
|
|
105
|
+
|
|
106
|
+
def alerts(self):
|
|
107
|
+
"""List your configured rate alerts (Business+). Manage them in the dashboard."""
|
|
108
|
+
return self._request(f"{self._v2_base()}/{self.api_key}/alerts", {})
|
|
109
|
+
|
|
110
|
+
@staticmethod
|
|
111
|
+
def _with_symbols(query, symbols):
|
|
112
|
+
if symbols:
|
|
113
|
+
query["symbols"] = ",".join(symbols)
|
|
114
|
+
return query
|
|
115
|
+
|
|
116
|
+
def _get(self, endpoint, query=None):
|
|
117
|
+
return self._request(f"{self.base_url}/{self.api_key}/{endpoint}", query or {})
|
|
118
|
+
|
|
119
|
+
@staticmethod
|
|
120
|
+
def _retry_after(headers, default=60):
|
|
121
|
+
try:
|
|
122
|
+
return int(headers.get("Retry-After", default))
|
|
123
|
+
except (TypeError, ValueError):
|
|
124
|
+
return default
|
|
125
|
+
|
|
126
|
+
def _backoff(self, attempt, headers):
|
|
127
|
+
ra = self._retry_after(headers, default=0)
|
|
128
|
+
return min(ra, 30) if ra else min(2 ** (attempt - 1), 8)
|
|
129
|
+
|
|
130
|
+
def _request(self, url, query, method="GET", body=None):
|
|
131
|
+
if query:
|
|
132
|
+
url += "?" + urllib.parse.urlencode(query)
|
|
133
|
+
headers = {
|
|
134
|
+
"Accept": "application/json",
|
|
135
|
+
"X-API-Key": self.api_key,
|
|
136
|
+
"User-Agent": "rate-api-python/1.0",
|
|
137
|
+
}
|
|
138
|
+
data_bytes = None
|
|
139
|
+
if body is not None:
|
|
140
|
+
data_bytes = json.dumps(body).encode("utf-8")
|
|
141
|
+
headers["Content-Type"] = "application/json"
|
|
142
|
+
|
|
143
|
+
attempt = 0
|
|
144
|
+
while True:
|
|
145
|
+
req = urllib.request.Request(url, data=data_bytes, method=method, headers=headers)
|
|
146
|
+
status = None
|
|
147
|
+
resp_headers = {}
|
|
148
|
+
try:
|
|
149
|
+
with urllib.request.urlopen(req, timeout=self.timeout) as resp:
|
|
150
|
+
status = resp.status
|
|
151
|
+
resp_headers = dict(resp.headers)
|
|
152
|
+
data = json.loads(resp.read().decode("utf-8"))
|
|
153
|
+
except urllib.error.HTTPError as e:
|
|
154
|
+
status = e.code
|
|
155
|
+
resp_headers = dict(e.headers or {})
|
|
156
|
+
# Retry transient statuses, honoring Retry-After.
|
|
157
|
+
if status in (429, 503) and attempt < self.max_retries:
|
|
158
|
+
attempt += 1
|
|
159
|
+
time.sleep(self._backoff(attempt, resp_headers))
|
|
160
|
+
continue
|
|
161
|
+
try:
|
|
162
|
+
data = json.loads(e.read().decode("utf-8")) # API returns JSON error bodies
|
|
163
|
+
except Exception:
|
|
164
|
+
raise RateApiError(f"HTTP {status}", status)
|
|
165
|
+
except OSError as e: # URLError, socket.timeout, TimeoutError all subclass OSError
|
|
166
|
+
if attempt < self.max_retries:
|
|
167
|
+
attempt += 1
|
|
168
|
+
time.sleep(self._backoff(attempt, {}))
|
|
169
|
+
continue
|
|
170
|
+
reason = str(getattr(e, "reason", e))
|
|
171
|
+
if isinstance(e, TimeoutError) or "timed out" in reason.lower():
|
|
172
|
+
raise RateApiTimeoutError(f"Request timed out after {self.timeout}s")
|
|
173
|
+
raise RateApiError(f"Request failed: {reason}")
|
|
174
|
+
|
|
175
|
+
if isinstance(data, dict) and data.get("success") is False:
|
|
176
|
+
err = data.get("error") or {}
|
|
177
|
+
msg = err.get("message") or data.get("message") or "Unknown API error"
|
|
178
|
+
etype = err.get("type")
|
|
179
|
+
rid = data.get("request_id") or resp_headers.get("X-Request-Id")
|
|
180
|
+
if status == 429:
|
|
181
|
+
raise RateLimitError(msg, status, etype, rid, self._retry_after(resp_headers))
|
|
182
|
+
raise RateApiError(msg, status, etype, rid)
|
|
183
|
+
return data
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: rate-api-python
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Official Python client for the Rate-API.com exchange-rate & crypto API
|
|
5
|
+
License: MIT
|
|
6
|
+
Project-URL: Homepage, https://rate-api.com
|
|
7
|
+
Project-URL: Documentation, https://rate-api.com/en/docs
|
|
8
|
+
Project-URL: Repository, https://github.com/Vilgar/rate-api.com
|
|
9
|
+
Keywords: exchange-rate,currency,forex,crypto,api,rate-api
|
|
10
|
+
Requires-Python: >=3.8
|
|
11
|
+
Description-Content-Type: text/markdown
|
|
12
|
+
License-File: LICENSE
|
|
13
|
+
Dynamic: license-file
|
|
14
|
+
|
|
15
|
+
# rate-api-python
|
|
16
|
+
|
|
17
|
+
Official Python client for [Rate-API.com](https://rate-api.com). Standard library only — no dependencies. Python 3.8+.
|
|
18
|
+
|
|
19
|
+
## Install
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
pip install rate-api-python
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Usage
|
|
26
|
+
|
|
27
|
+
```python
|
|
28
|
+
from rate_api import RateApiClient, RateApiError
|
|
29
|
+
|
|
30
|
+
client = RateApiClient("YOUR_API_KEY")
|
|
31
|
+
|
|
32
|
+
rates = client.latest("USD", ["EUR", "GBP"])
|
|
33
|
+
print(rates["rates"]["EUR"])
|
|
34
|
+
|
|
35
|
+
client.convert("USD", "EUR", 100) # Pro+
|
|
36
|
+
client.historical("2026-01-15", "USD", ["EUR"]) # Pro+
|
|
37
|
+
client.timeseries("2026-01-01", "2026-01-31") # Business+
|
|
38
|
+
client.crypto(["BTC", "ETH"]) # Pro+
|
|
39
|
+
client.health() # public
|
|
40
|
+
|
|
41
|
+
try:
|
|
42
|
+
client.timeseries("2020-01-01", "2026-12-31")
|
|
43
|
+
except RateApiError as e:
|
|
44
|
+
print(e, e.status) # "Date range too large. Maximum is 366 days." 400
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## v2 features
|
|
48
|
+
|
|
49
|
+
The client exposes the v2 endpoints directly (they resolve to `/api/v2` regardless of base URL):
|
|
50
|
+
|
|
51
|
+
```python
|
|
52
|
+
# Latest with 24h change, metadata and precision
|
|
53
|
+
r = client.latest_v2("USD", ["EUR", "GBP"], include_change=True, include_metadata=True, precision=4)
|
|
54
|
+
print(r["changes_pct"]["EUR"])
|
|
55
|
+
|
|
56
|
+
# Historical comparison between two dates (Pro+)
|
|
57
|
+
cmp = client.historical_compare("2026-01-15", "2026-01-01", "USD", ["EUR"])
|
|
58
|
+
|
|
59
|
+
# Batch conversion — up to 100 pairs in one call (Pro+)
|
|
60
|
+
batch = client.batch_convert([
|
|
61
|
+
{"from": "USD", "to": "EUR", "amount": 100},
|
|
62
|
+
{"from": "GBP", "to": "JPY", "amount": 50},
|
|
63
|
+
])
|
|
64
|
+
|
|
65
|
+
# Your configured rate alerts (Business+)
|
|
66
|
+
alerts = client.alerts()
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## License
|
|
70
|
+
|
|
71
|
+
MIT
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
rate_api
|