Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,20 @@

All notable changes to this project will be documented in this file.

## [2.0.0] - 2026-02-19

### Added
- OHLC endpoint (`currency.ohlc()`) for Open, High, Low, Close price data (Tier 3+)
- Parameters: `currency` (required), `date` (required), `base` (optional), `interval` (optional)
- Supported intervals: `5m`, `15m`, `30m`, `1h`, `4h`, `12h`, `1d`

### Changed
- **Breaking:** Updated to API v2 (`https://currencyapi.net/api/v2/`)
- Updated README with OHLC endpoint documentation and usage examples

### Removed
- **Breaking:** API v1 support dropped; all requests now use v2

## [1.1.0] - 2026-02-13

### Added
Expand Down
135 changes: 130 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
# CurrencyApi Python wrapper
# CurrencyApi Python wrapper


[![PyPI version](https://badge.fury.io/py/currencyapinet.svg)](https://pypi.org/project/currencyapinet/) [![Coverage Status](https://coveralls.io/repos/github/houseofapis/currencyapi-python/badge.svg?branch=main)](https://coveralls.io/github/houseofapis/currencyapi-python?branch=main)
[![PyPI version](https://badge.fury.io/py/currencyapinet.svg)](https://pypi.org/project/currencyapinet/) [![Coverage Status](https://coveralls.io/repos/github/houseofapis/currencyapi-python/badge.svg?branch=main)](https://coveralls.io/github/houseofapis/currencyapi-python?branch=main)


<a href="https://currencyapi.net" title="CurrencyApi">CurrencyApi.net</a> provides live currency rates via a REST API. A live currency feed for over 152 currencies, including physical (USD, GBP, EUR + more) and cryptos (Bitcoin, Litecoin, Ethereum + more). A JSON and XML currency api updated every 60 seconds.
<a href="https://currencyapi.net" title="CurrencyApi">CurrencyApi.net</a> provides live currency rates via a REST API. A live currency feed for over 152 currencies, including physical (USD, GBP, EUR + more) and cryptos (Bitcoin, Litecoin, Ethereum + more). A JSON and XML currency api updated every 60 seconds.

Features:

Expand All @@ -13,13 +13,14 @@ Features:
- Popular cryptocurrencies included; Bitcoin, Litecoin etc.
- Convert currencies on the fly with the convert endpoint.
- Historical currency rates back to year 2000.
- OHLC (Open, High, Low, Close) data for technical analysis (Tier 3+).
- Easy to follow <a href="https://currencyapi.net/documentation" title="currency-api-documentation">documentation</a>

Signup for a free or paid account <a href="https://currencyapi.net/#pricing-sec" title="currency-api-pricing">here</a>.

## This package is a:

Python wrapper for <a href="https://currencyapi.net" title="CurrencyApi">CurrencyApi.net</a> endpoints.
Python wrapper for <a href="https://currencyapi.net" title="CurrencyApi">CurrencyApi.net</a> endpoints (API v2).

## Developer Guide

Expand All @@ -39,4 +40,128 @@ Alternatively keep reading below.

## Installation

View our <a href="https://currencyapi.net/sdk/python">Python SDK</a> guide
View our <a href="https://currencyapi.net/sdk/python">Python SDK</a> guide

```
pip install currencyapinet
```

## Usage

```python
from currencyapinet import Currency

currency = Currency('YOUR_API_KEY')
```

---

### Rates

Returns live currency rates for all supported currencies. Base currency defaults to USD.

```python
result = currency.rates().get()

# With optional base currency
result = currency.rates().base('EUR').get()

# XML output
result = currency.rates().output('XML').get()
```

---

### Convert

Converts an amount from one currency to another.

```python
result = currency.convert().from_currency('USD').to_currency('EUR').amount(100).get()
```

---

### History

Returns historical currency rates for a specific date.

```python
result = currency.history().date('2023-12-25').get()

# With optional base currency
result = currency.history().base('GBP').date('2023-12-25').get()
```

---

### Timeframe

Returns historical currency rates for a date range (max 365 days).

```python
result = currency.timeframe().start_date('2023-12-01').end_date('2023-12-31').get()

# With optional base currency
result = currency.timeframe().base('GBP').start_date('2023-12-01').end_date('2023-12-31').get()
```

---

### Currencies

Returns a list of all supported currencies.

```python
result = currency.currencies().get()
```

---

### OHLC

Returns OHLC (Open, High, Low, Close) data for a currency pair on a specific date. Requires a Tier 3 subscription.

**Parameters:**

| Parameter | Required | Description |
|------------|----------|-------------|
| `currency` | Yes | Target currency code (e.g. `EUR`, `GBP`, `BTC`) |
| `date` | Yes | Date in `YYYY-MM-DD` format (must be in the past) |
| `base` | No | Base currency code (defaults to `USD`) |
| `interval` | No | Time interval: `5m`, `15m`, `30m`, `1h`, `4h`, `12h`, `1d` (defaults to `1d`) |

```python
# Basic request (1-day interval)
result = currency.ohlc().currency('EUR').date('2023-12-25').get()

# With custom interval
result = currency.ohlc().currency('GBP').date('2023-12-25').interval('1h').get()

# With custom base currency and interval
result = currency.ohlc().currency('JPY').date('2023-12-25').base('EUR').interval('4h').get()

# XML output
result = currency.ohlc().currency('EUR').date('2023-12-25').output('XML').get()
```

**Example response:**

```json
{
"valid": true,
"base": "USD",
"quote": "EUR",
"date": "2023-12-25",
"interval": "1d",
"ohlc": [
{
"start": "2023-12-25T00:00:00Z",
"open": 0.92000000000000,
"high": 0.92500000000000,
"low": 0.91800000000000,
"close": 0.92200000000000
}
]
}
```
12 changes: 8 additions & 4 deletions currencyapinet/currency.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,26 @@
from currencyapinet.endpoints.history import History
from currencyapinet.endpoints.timeframe import Timeframe
from currencyapinet.endpoints.currencies import Currencies
from currencyapinet.endpoints.ohlc import Ohlc

class Currency:
def __init__(self, api_key: str):
self._api_key = api_key

def rates(self) -> Rates:
return Rates(self._api_key)

def convert(self) -> Convert:
return Convert(self._api_key)

def history(self) -> History:
return History(self._api_key)

def timeframe(self) -> Timeframe:
return Timeframe(self._api_key)

def currencies(self) -> Currencies:
return Currencies(self._api_key)

def ohlc(self) -> Ohlc:
return Ohlc(self._api_key)
3 changes: 2 additions & 1 deletion currencyapinet/endpoints/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@
from currencyapinet.endpoints.convert import Convert
from currencyapinet.endpoints.history import History
from currencyapinet.endpoints.timeframe import Timeframe
from currencyapinet.endpoints.currencies import Currencies
from currencyapinet.endpoints.currencies import Currencies
from currencyapinet.endpoints.ohlc import Ohlc
2 changes: 1 addition & 1 deletion currencyapinet/endpoints/endpoint.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import requests

API_VERSION = 'v1'
API_VERSION = 'v2'
BASE_URL = 'https://currencyapi.net/api/' + API_VERSION + '/'
DEFAULT_BASE = 'USD'
DEFAULT_OUTPUT = 'JSON'
Expand Down
23 changes: 23 additions & 0 deletions currencyapinet/endpoints/ohlc.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
from currencyapinet.endpoints.endpoint import Endpoint

OHLC_ENDPOINT = 'ohlc'

class Ohlc(Endpoint):
def __init__(self, api_key: str):
super().__init__(api_key, OHLC_ENDPOINT)

def currency(self, currency: str):
self.add_param('currency', currency.upper())
return self

def date(self, date: str):
self.add_param('date', date)
return self

def base(self, currency: str):
self._base(currency)
return self

def interval(self, interval: str):
self.add_param('interval', interval)
return self
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

setup(
name="currencyapinet",
version="1.1.0",
version="2.0.0",
packages=find_packages(exclude="tests"),
description="Python wrapper for CurrencyApi.net",
long_description=README_MD,
Expand Down
70 changes: 70 additions & 0 deletions tests/endpoints/test_ohlc.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
from unittest import TestCase
from unittest.mock import Mock, patch
from currencyapinet.endpoints.ohlc import Ohlc
import requests

class Test(TestCase):

def setUp(self):
self.class_under_test = Ohlc('fakekey')

def test_ohlc_currency(self):
self.class_under_test.currency('eUr')
self.assertEqual('EUR', self.class_under_test.param.get('currency'))
self.assertIsInstance(self.class_under_test.currency('EUR'), Ohlc)

def test_ohlc_date(self):
self.class_under_test.date('2023-12-25')
self.assertEqual('2023-12-25', self.class_under_test.param.get('date'))
self.assertIsInstance(self.class_under_test.date('2023-12-25'), Ohlc)

def test_ohlc_base(self):
self.class_under_test.base('gBp')
self.assertEqual('GBP', self.class_under_test.param.get('base'))
self.assertIsInstance(self.class_under_test.base('USD'), Ohlc)

def test_ohlc_interval(self):
self.class_under_test.interval('1h')
self.assertEqual('1h', self.class_under_test.param.get('interval'))
self.assertIsInstance(self.class_under_test.interval('1d'), Ohlc)

def test_ohlc_endpoint_set(self):
self.assertEqual('ohlc', self.class_under_test.endpoint)

def test_ohlc__build_url_params(self):
self.assertDictEqual(
{'key': 'fakekey', 'output': 'JSON'},
self.class_under_test._build_url_params()
)
self.class_under_test.currency('EUR')
self.class_under_test.date('2023-12-25')
self.assertDictEqual(
{'key': 'fakekey', 'output': 'JSON', 'currency': 'EUR', 'date': '2023-12-25'},
self.class_under_test._build_url_params()
)
self.class_under_test.base('gbP')
self.class_under_test.interval('1h')
self.assertDictEqual(
{'key': 'fakekey', 'output': 'JSON', 'currency': 'EUR', 'date': '2023-12-25', 'base': 'GBP', 'interval': '1h'},
self.class_under_test._build_url_params()
)

@patch.object(requests, 'get')
def test_ohlc_get(self, mock_requests_get):
mock_data = [{"id": 1}, {"id": 2}]
response_mock = Mock(return_value=mock_data)
mock_requests_get.return_value.json = response_mock
self.assertEqual(
mock_data,
self.class_under_test.get()
)

@patch.object(requests, 'get')
def test_ohlc_get_xml(self, mock_requests_get):
self.class_under_test.output('xMl')
mock_data = [{"id": 1}, {"id": 2}]
mock_requests_get.return_value.text = mock_data
self.assertEqual(
mock_data,
self.class_under_test.get()
)
7 changes: 6 additions & 1 deletion tests/test_currency.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from currencyapinet.endpoints.timeframe import Timeframe
from currencyapinet.endpoints.convert import Convert
from currencyapinet.endpoints.currencies import Currencies
from currencyapinet.endpoints.ohlc import Ohlc
from unittest import TestCase

class Test(TestCase):
Expand All @@ -25,4 +26,8 @@ def test_convert_method(self):

def test_currencies_method(self):
class_under_test = Currency('fakeKey')
self.assertIsInstance(class_under_test.currencies(), Currencies)
self.assertIsInstance(class_under_test.currencies(), Currencies)

def test_ohlc_method(self):
class_under_test = Currency('fakeKey')
self.assertIsInstance(class_under_test.ohlc(), Ohlc)