currentsapi 0.1.0__py3-none-any.whl
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.
- currentsapi/__init__.py +21 -0
- currentsapi/authentication.py +21 -0
- currentsapi/client.py +127 -0
- currentsapi/constants.py +9 -0
- currentsapi-0.1.0.dist-info/METADATA +138 -0
- currentsapi-0.1.0.dist-info/RECORD +9 -0
- currentsapi-0.1.0.dist-info/WHEEL +5 -0
- currentsapi-0.1.0.dist-info/licenses/LICENSE.txt +21 -0
- currentsapi-0.1.0.dist-info/top_level.txt +1 -0
currentsapi/__init__.py
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
|
|
3
|
+
__project__ = "currentsapi"
|
|
4
|
+
__version__ = "0.1.0"
|
|
5
|
+
__repo__ = "https://github.com/currentslab/currentsapi-python"
|
|
6
|
+
|
|
7
|
+
from .client import CurrentsAPI
|
|
8
|
+
|
|
9
|
+
__all__ = ["CurrentsAPI"]
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def print_version():
|
|
13
|
+
sv = sys.version_info
|
|
14
|
+
py_version = "{}.{}.{}".format(sv.major, sv.minor, sv.micro)
|
|
15
|
+
version_parts = __version__.split(".")
|
|
16
|
+
s = "{} version: [{}], Python {}".format(__project__, __version__, py_version)
|
|
17
|
+
s += "\nMajor version: {} (breaking changes)".format(version_parts[0])
|
|
18
|
+
s += "\nMinor version: {} (extra feature)".format(version_parts[1])
|
|
19
|
+
s += "\nMicro version: {} (commit count)".format(version_parts[2])
|
|
20
|
+
s += "\nFind out the most recent version at {}".format(__repo__)
|
|
21
|
+
return s
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
from requests.auth import AuthBase
|
|
2
|
+
|
|
3
|
+
def set_header_payload(auth_key):
|
|
4
|
+
|
|
5
|
+
return {
|
|
6
|
+
'Authorization': auth_key,
|
|
7
|
+
'Content-Type': 'Application/JSON',
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class ApiAuth(AuthBase):
|
|
12
|
+
def __init__(self, api_key):
|
|
13
|
+
if not isinstance(api_key, str):
|
|
14
|
+
raise ValueError('api_key must be string')
|
|
15
|
+
self.api_key = api_key
|
|
16
|
+
|
|
17
|
+
def __call__(self, request):
|
|
18
|
+
request.headers.update(set_header_payload(self.api_key))
|
|
19
|
+
return request
|
|
20
|
+
|
|
21
|
+
|
currentsapi/client.py
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import datetime
|
|
2
|
+
import requests
|
|
3
|
+
from dateutil import parser
|
|
4
|
+
|
|
5
|
+
from currentsapi import constants
|
|
6
|
+
from currentsapi.authentication import ApiAuth
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class CurrentsAPIError(Exception):
|
|
10
|
+
"""Raised when the Currents API returns an error response."""
|
|
11
|
+
|
|
12
|
+
def __init__(self, response):
|
|
13
|
+
self.response = response
|
|
14
|
+
super().__init__(str(response))
|
|
15
|
+
|
|
16
|
+
@property
|
|
17
|
+
def status(self):
|
|
18
|
+
return self.response.get("status")
|
|
19
|
+
|
|
20
|
+
@property
|
|
21
|
+
def code(self):
|
|
22
|
+
return self.response.get("code")
|
|
23
|
+
|
|
24
|
+
@property
|
|
25
|
+
def message(self):
|
|
26
|
+
return self.response.get("message")
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class CurrentsAPI:
|
|
30
|
+
def __init__(
|
|
31
|
+
self,
|
|
32
|
+
api_key,
|
|
33
|
+
domain=constants.DOMAIN,
|
|
34
|
+
version=constants.VERSION,
|
|
35
|
+
timeout=30,
|
|
36
|
+
):
|
|
37
|
+
if not isinstance(api_key, str):
|
|
38
|
+
raise ValueError("api_key must be a string")
|
|
39
|
+
self.api_key = ApiAuth(api_key)
|
|
40
|
+
self.latest_endpoint = constants.LATEST_NEWS_URL % (domain, version)
|
|
41
|
+
self.search_endpoint = constants.SEARCH_URL % (domain, version)
|
|
42
|
+
self.available_languages_endpoint = constants.AVAILABLE_LANGUAGES_URL % (domain, version)
|
|
43
|
+
self.available_regions_endpoint = constants.AVAILABLE_REGIONS_URL % (domain, version)
|
|
44
|
+
self.available_category_endpoint = constants.AVAILABLE_CATEGORIES_URL % (domain, version)
|
|
45
|
+
self.timeout = timeout
|
|
46
|
+
|
|
47
|
+
def _get(self, endpoint, params=None):
|
|
48
|
+
r = requests.get(
|
|
49
|
+
endpoint,
|
|
50
|
+
auth=self.api_key,
|
|
51
|
+
timeout=self.timeout,
|
|
52
|
+
params=params or {},
|
|
53
|
+
)
|
|
54
|
+
if r.status_code != requests.codes.ok:
|
|
55
|
+
raise CurrentsAPIError(r.json())
|
|
56
|
+
return r.json()
|
|
57
|
+
|
|
58
|
+
def latest_news(self, language=None):
|
|
59
|
+
params = {}
|
|
60
|
+
if language:
|
|
61
|
+
if not isinstance(language, str):
|
|
62
|
+
raise ValueError("language must be a string")
|
|
63
|
+
params["language"] = language
|
|
64
|
+
return self._get(self.latest_endpoint, params)
|
|
65
|
+
|
|
66
|
+
def search(
|
|
67
|
+
self,
|
|
68
|
+
language=None,
|
|
69
|
+
keywords=None,
|
|
70
|
+
country=None,
|
|
71
|
+
category=None,
|
|
72
|
+
start_date=None,
|
|
73
|
+
end_date=None,
|
|
74
|
+
):
|
|
75
|
+
params = {}
|
|
76
|
+
|
|
77
|
+
if keywords:
|
|
78
|
+
if not isinstance(keywords, str):
|
|
79
|
+
raise ValueError("keywords must be a string")
|
|
80
|
+
params["keywords"] = keywords
|
|
81
|
+
|
|
82
|
+
if country:
|
|
83
|
+
if not isinstance(country, str):
|
|
84
|
+
raise ValueError("country must be a string")
|
|
85
|
+
params["country"] = country
|
|
86
|
+
|
|
87
|
+
if language:
|
|
88
|
+
if not isinstance(language, str):
|
|
89
|
+
raise ValueError("language must be a string")
|
|
90
|
+
params["language"] = language
|
|
91
|
+
|
|
92
|
+
if category:
|
|
93
|
+
if not isinstance(category, str):
|
|
94
|
+
raise ValueError("category must be a string")
|
|
95
|
+
params["category"] = category
|
|
96
|
+
|
|
97
|
+
if start_date:
|
|
98
|
+
date = self._parse_date(start_date, "start_date")
|
|
99
|
+
params["start_date"] = date.strftime("%Y-%m-%dT%H:%M:%SZ")
|
|
100
|
+
|
|
101
|
+
if end_date:
|
|
102
|
+
date = self._parse_date(end_date, "end_date")
|
|
103
|
+
params["end_date"] = date.strftime("%Y-%m-%dT%H:%M:%SZ")
|
|
104
|
+
|
|
105
|
+
return self._get(self.search_endpoint, params)
|
|
106
|
+
|
|
107
|
+
def available_languages(self):
|
|
108
|
+
return self._get(self.available_languages_endpoint)
|
|
109
|
+
|
|
110
|
+
def available_regions(self):
|
|
111
|
+
return self._get(self.available_regions_endpoint)
|
|
112
|
+
|
|
113
|
+
def available_category(self):
|
|
114
|
+
return self._get(self.available_category_endpoint)
|
|
115
|
+
|
|
116
|
+
@staticmethod
|
|
117
|
+
def _parse_date(date_value, param_name):
|
|
118
|
+
if isinstance(date_value, str):
|
|
119
|
+
return parser.parse(date_value)
|
|
120
|
+
elif isinstance(date_value, datetime.date):
|
|
121
|
+
return date_value
|
|
122
|
+
else:
|
|
123
|
+
raise ValueError(
|
|
124
|
+
"{} must be a string parsable by dateutil or a datetime/date object".format(
|
|
125
|
+
param_name
|
|
126
|
+
)
|
|
127
|
+
)
|
currentsapi/constants.py
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
VERSION = "v1"
|
|
2
|
+
|
|
3
|
+
DOMAIN = "api.currentsapi.services"
|
|
4
|
+
|
|
5
|
+
LATEST_NEWS_URL = "https://%s/%s/latest-news"
|
|
6
|
+
SEARCH_URL = "https://%s/%s/search"
|
|
7
|
+
AVAILABLE_LANGUAGES_URL = "https://%s/%s/available/languages"
|
|
8
|
+
AVAILABLE_REGIONS_URL = "https://%s/%s/available/regions"
|
|
9
|
+
AVAILABLE_CATEGORIES_URL = "https://%s/%s/available/categories"
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: currentsapi
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Official Python client for the Currents API
|
|
5
|
+
Home-page: https://github.com/currentslab/currentsapi-python
|
|
6
|
+
Author: Currents Dev
|
|
7
|
+
Author-email: ray@currentsapi.services
|
|
8
|
+
License: MIT
|
|
9
|
+
Keywords: currentsapi,news,wrapper,currents,api
|
|
10
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: Intended Audience :: Financial and Insurance Industry
|
|
13
|
+
Classifier: Intended Audience :: Information Technology
|
|
14
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
22
|
+
Requires-Python: >=3.8
|
|
23
|
+
Description-Content-Type: text/markdown
|
|
24
|
+
License-File: LICENSE.txt
|
|
25
|
+
Requires-Dist: requests>=2.25.0
|
|
26
|
+
Requires-Dist: python-dateutil>=2.8.0
|
|
27
|
+
Provides-Extra: dev
|
|
28
|
+
Requires-Dist: pytest; extra == "dev"
|
|
29
|
+
Dynamic: author
|
|
30
|
+
Dynamic: author-email
|
|
31
|
+
Dynamic: classifier
|
|
32
|
+
Dynamic: description
|
|
33
|
+
Dynamic: description-content-type
|
|
34
|
+
Dynamic: home-page
|
|
35
|
+
Dynamic: keywords
|
|
36
|
+
Dynamic: license
|
|
37
|
+
Dynamic: license-file
|
|
38
|
+
Dynamic: provides-extra
|
|
39
|
+
Dynamic: requires-dist
|
|
40
|
+
Dynamic: requires-python
|
|
41
|
+
Dynamic: summary
|
|
42
|
+
|
|
43
|
+
# currentsapi-python
|
|
44
|
+
|
|
45
|
+
The official Python SDK for the [Currents API](https://currentsapi.services/en/docs/).
|
|
46
|
+
|
|
47
|
+
## Installation
|
|
48
|
+
|
|
49
|
+
Install the package from PyPI. The distribution name is `currentsapi` (this
|
|
50
|
+
repository is `currentsapi-python`, and the Python import name is also
|
|
51
|
+
`currentsapi`):
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
pip install currentsapi
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Python 3.8+ is supported.
|
|
58
|
+
|
|
59
|
+
## Usage
|
|
60
|
+
|
|
61
|
+
Import the client and initialize it with your API key:
|
|
62
|
+
|
|
63
|
+
```python
|
|
64
|
+
from currentsapi import CurrentsAPI
|
|
65
|
+
|
|
66
|
+
api = CurrentsAPI(api_key="YOUR_API_KEY")
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Endpoints
|
|
70
|
+
|
|
71
|
+
### Latest News
|
|
72
|
+
|
|
73
|
+
Retrieve the latest news headlines. Optionally filter by language:
|
|
74
|
+
|
|
75
|
+
```python
|
|
76
|
+
api.latest_news()
|
|
77
|
+
api.latest_news(language="en")
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### Search
|
|
81
|
+
|
|
82
|
+
Search news articles with optional filters:
|
|
83
|
+
|
|
84
|
+
```python
|
|
85
|
+
api.search(keywords="OpenAI", language="en")
|
|
86
|
+
api.search(country="US", category="technology", start_date="2024-01-01", end_date="2024-12-31")
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Supported parameters:
|
|
90
|
+
|
|
91
|
+
- `keywords` – search keywords
|
|
92
|
+
- `language` – article language code
|
|
93
|
+
- `country` – country code
|
|
94
|
+
- `category` – news category
|
|
95
|
+
- `start_date` – start date (`YYYY-MM-DD` or `datetime` object)
|
|
96
|
+
- `end_date` – end date (`YYYY-MM-DD` or `datetime` object)
|
|
97
|
+
|
|
98
|
+
### Available Resources
|
|
99
|
+
|
|
100
|
+
```python
|
|
101
|
+
api.available_languages()
|
|
102
|
+
api.available_regions()
|
|
103
|
+
api.available_category()
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
## Examples
|
|
107
|
+
|
|
108
|
+
- [Generate a source-linked news briefing](examples/source_linked_briefing/README.md) from a live Search API response or a deterministic offline fixture.
|
|
109
|
+
- [Build a company news monitor](examples/company_news_monitor/README.md) with
|
|
110
|
+
a JSON watchlist, bounded search window, local state, and deterministic fixture.
|
|
111
|
+
|
|
112
|
+
## Authentication
|
|
113
|
+
|
|
114
|
+
All requests are authenticated using an `Authorization` header. Pass your API key when instantiating the client:
|
|
115
|
+
|
|
116
|
+
```python
|
|
117
|
+
api = CurrentsAPI(api_key="YOUR_API_KEY")
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
Get your API key at [https://currentsapi.services/en/register](https://currentsapi.services/en/register).
|
|
121
|
+
|
|
122
|
+
## License
|
|
123
|
+
|
|
124
|
+
MIT License
|
|
125
|
+
|
|
126
|
+
## Error handling
|
|
127
|
+
|
|
128
|
+
Any non-200 API response raises `CurrentsAPIError`, which exposes the parsed
|
|
129
|
+
response body:
|
|
130
|
+
|
|
131
|
+
```python
|
|
132
|
+
from currentsapi.client import CurrentsAPIError
|
|
133
|
+
|
|
134
|
+
try:
|
|
135
|
+
api.latest_news()
|
|
136
|
+
except CurrentsAPIError as exc:
|
|
137
|
+
print(exc.status, exc.code, exc.message)
|
|
138
|
+
```
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
currentsapi/__init__.py,sha256=ertxbDc6JFUAIQmufsz6up2vyQwgdlOrgSIf4J8G6Qc,727
|
|
2
|
+
currentsapi/authentication.py,sha256=XMaRNF5_44QoJfC7A7K2xZAkfuV2OdWHYSmwxAj9kZA,484
|
|
3
|
+
currentsapi/client.py,sha256=KZKsIgakMDFCwma10vnaScTNGJl397VrCLPlwR8L6lI,3923
|
|
4
|
+
currentsapi/constants.py,sha256=vrzOu2LZ3hzlsylwQSF1U1UJTaeH8fdELHco8j8rEO8,319
|
|
5
|
+
currentsapi-0.1.0.dist-info/licenses/LICENSE.txt,sha256=SvtJMl4TBu84IwF3M-5g1SGh6NnMnZ3q6Jbx5so0cU0,1069
|
|
6
|
+
currentsapi-0.1.0.dist-info/METADATA,sha256=eLhkqh3wHEs6L6-JiuBG_ezZU6tSgyuUctQCcZdqKig,3654
|
|
7
|
+
currentsapi-0.1.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
|
|
8
|
+
currentsapi-0.1.0.dist-info/top_level.txt,sha256=-fNSY7QlDmXm6IUgjyx95khZ7dlYDry5tVhHzVqVJ9E,12
|
|
9
|
+
currentsapi-0.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2018 Matt Lisivick
|
|
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 @@
|
|
|
1
|
+
currentsapi
|