mansaapi 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.
mansaapi/__init__.py ADDED
@@ -0,0 +1,4 @@
1
+ from ._client import MansaAPI, __version__
2
+ from .errors import MansaAPIError
3
+
4
+ __all__ = ["MansaAPI", "MansaAPIError", "__version__"]
mansaapi/_client.py ADDED
@@ -0,0 +1,217 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Any, Dict, Optional
4
+
5
+ import requests
6
+
7
+ from .errors import MansaAPIError
8
+
9
+ __version__ = "0.1.0"
10
+ DEFAULT_BASE_URL = "https://mansaapi.com"
11
+
12
+ Params = Optional[Dict[str, Any]]
13
+
14
+
15
+ class _HttpClient:
16
+ def __init__(self, api_key: str, base_url: str, timeout: float) -> None:
17
+ self._api_key = api_key
18
+ self._base_url = base_url.rstrip("/")
19
+ self._timeout = timeout
20
+ self._session = requests.Session()
21
+ self._session.headers.update(
22
+ {
23
+ "Authorization": f"Bearer {api_key}",
24
+ "Content-Type": "application/json",
25
+ "User-Agent": f"mansaapi-python/{__version__}",
26
+ }
27
+ )
28
+
29
+ def get(self, path: str, params: Params = None) -> Dict[str, Any]:
30
+ clean = {k: v for k, v in (params or {}).items() if v is not None}
31
+ resp = self._session.get(
32
+ f"{self._base_url}{path}", params=clean, timeout=self._timeout
33
+ )
34
+ return self._handle(resp)
35
+
36
+ def post(self, path: str, payload: Any) -> Dict[str, Any]:
37
+ resp = self._session.post(
38
+ f"{self._base_url}{path}", json=payload, timeout=self._timeout
39
+ )
40
+ return self._handle(resp)
41
+
42
+ @staticmethod
43
+ def _handle(resp: requests.Response) -> Dict[str, Any]:
44
+ try:
45
+ body = resp.json()
46
+ except ValueError:
47
+ raise MansaAPIError(
48
+ "INVALID_RESPONSE",
49
+ f"Non-JSON response (status {resp.status_code})",
50
+ resp.status_code,
51
+ resp.text,
52
+ )
53
+
54
+ if not resp.ok or body.get("success") is False:
55
+ err = body.get("error") or {}
56
+ raise MansaAPIError(
57
+ err.get("code", "UNKNOWN_ERROR"),
58
+ err.get("message", f"Request failed with status {resp.status_code}"),
59
+ resp.status_code,
60
+ body,
61
+ )
62
+ return body
63
+
64
+
65
+ class _Markets:
66
+ def __init__(self, http: _HttpClient) -> None:
67
+ self._http = http
68
+
69
+ def get_exchanges(self) -> Dict[str, Any]:
70
+ return self._http.get("/api/v1/markets/exchanges")
71
+
72
+ def get_exchange(self, exchange_code: str) -> Dict[str, Any]:
73
+ return self._http.get(f"/api/v1/markets/exchanges/{exchange_code}")
74
+
75
+ def get_stocks(self, exchange_code: str, **params: Any) -> Dict[str, Any]:
76
+ return self._http.get(f"/api/v1/markets/exchanges/{exchange_code}/stocks", params)
77
+
78
+ def get_stock(self, exchange_code: str, ticker: str) -> Dict[str, Any]:
79
+ return self._http.get(f"/api/v1/markets/exchanges/{exchange_code}/stocks/{ticker}")
80
+
81
+ def get_movers(self, exchange_code: str, **params: Any) -> Dict[str, Any]:
82
+ return self._http.get(f"/api/v1/markets/exchanges/{exchange_code}/movers", params)
83
+
84
+ def get_pan_african_movers(self, **params: Any) -> Dict[str, Any]:
85
+ return self._http.get("/api/v1/markets/movers/pan-african", params)
86
+
87
+ def get_indices(self, exchange_code: str) -> Dict[str, Any]:
88
+ return self._http.get(f"/api/v1/markets/exchanges/{exchange_code}/indices")
89
+
90
+ def get_index(self, exchange_code: str, code: str) -> Dict[str, Any]:
91
+ return self._http.get(f"/api/v1/markets/exchanges/{exchange_code}/indices/{code}")
92
+
93
+ def get_etfs(self, exchange_code: str) -> Dict[str, Any]:
94
+ return self._http.get(f"/api/v1/markets/exchanges/{exchange_code}/etfs")
95
+
96
+ def get_forex(self) -> Dict[str, Any]:
97
+ return self._http.get("/api/v1/markets/forex")
98
+
99
+ def get_commodities(self) -> Dict[str, Any]:
100
+ return self._http.get("/api/v1/markets/commodities")
101
+
102
+ def search(self, query: str, **params: Any) -> Dict[str, Any]:
103
+ return self._http.get("/api/v1/markets/search", {"q": query, **params})
104
+
105
+ # Premium (Starter+)
106
+ def get_dividends(self, exchange_code: str, ticker: str, **params: Any) -> Dict[str, Any]:
107
+ return self._http.get(
108
+ f"/api/v1/markets/exchanges/{exchange_code}/dividends/{ticker}", params
109
+ )
110
+
111
+ def get_disclosures(self, exchange_code: str, **params: Any) -> Dict[str, Any]:
112
+ return self._http.get(
113
+ f"/api/v1/markets/exchanges/{exchange_code}/disclosures", params
114
+ )
115
+
116
+ def get_recommendations(self, exchange_code: str, **params: Any) -> Dict[str, Any]:
117
+ return self._http.get(
118
+ f"/api/v1/markets/exchanges/{exchange_code}/recommendations", params
119
+ )
120
+
121
+ # Premium (Pro+)
122
+ def get_insider_trades(self, exchange_code: str, **params: Any) -> Dict[str, Any]:
123
+ return self._http.get(
124
+ f"/api/v1/markets/exchanges/{exchange_code}/insider-trades", params
125
+ )
126
+
127
+
128
+ class _Identity:
129
+ def __init__(self, http: _HttpClient) -> None:
130
+ self._http = http
131
+
132
+ def get_banks(self, country: Optional[str] = None) -> Dict[str, Any]:
133
+ return self._http.get("/api/v1/identity/banks", {"country": country})
134
+
135
+ def get_bank(self, code: str) -> Dict[str, Any]:
136
+ return self._http.get(f"/api/v1/identity/banks/{code}")
137
+
138
+ def get_mobile_networks(self, country: Optional[str] = None) -> Dict[str, Any]:
139
+ return self._http.get("/api/v1/identity/mobile-networks", {"country": country})
140
+
141
+ def lookup_mobile_network(self, phone: str) -> Dict[str, Any]:
142
+ return self._http.get("/api/v1/identity/mobile-networks/lookup", {"phone": phone})
143
+
144
+ def get_currencies(self) -> Dict[str, Any]:
145
+ return self._http.get("/api/v1/identity/currencies")
146
+
147
+ def validate_nuban(self, account: str, bank_code: str) -> Dict[str, Any]:
148
+ return self._http.get(
149
+ "/api/v1/identity/nuban/validate",
150
+ {"account": account, "bank_code": bank_code},
151
+ )
152
+
153
+
154
+ class _Location:
155
+ def __init__(self, http: _HttpClient) -> None:
156
+ self._http = http
157
+
158
+ def get_countries(self) -> Dict[str, Any]:
159
+ return self._http.get("/api/v1/location/countries")
160
+
161
+ def get_country(self, code: str) -> Dict[str, Any]:
162
+ return self._http.get(f"/api/v1/location/countries/{code}")
163
+
164
+ def get_states(self, country_code: str) -> Dict[str, Any]:
165
+ return self._http.get(f"/api/v1/location/countries/{country_code}/states")
166
+
167
+ def get_lgas(self, country_code: str, state_code: str) -> Dict[str, Any]:
168
+ return self._http.get(
169
+ f"/api/v1/location/countries/{country_code}/states/{state_code}/lgas"
170
+ )
171
+
172
+ def get_holidays(self, country_code: str, year: int) -> Dict[str, Any]:
173
+ return self._http.get(
174
+ f"/api/v1/location/countries/{country_code}/holidays/{year}"
175
+ )
176
+
177
+
178
+ class _Keys:
179
+ def __init__(self, http: _HttpClient) -> None:
180
+ self._http = http
181
+
182
+ def create(self, name: str, email: str, project_description: Optional[str] = None) -> Dict[str, Any]:
183
+ return self._http.post(
184
+ "/api/v1/keys",
185
+ {
186
+ "name": name,
187
+ "email": email,
188
+ "tier": "standard",
189
+ "project_description": project_description,
190
+ },
191
+ )
192
+
193
+
194
+ class MansaAPI:
195
+ """Official Python client for the Mansa API.
196
+
197
+ Example:
198
+ from mansaapi import MansaAPI
199
+
200
+ mansa = MansaAPI(api_key="mansa_live_sk_...")
201
+ bank = mansa.identity.get_bank("058")
202
+ print(bank["data"]["name"]) # "Guaranty Trust Bank (GTBank)"
203
+ """
204
+
205
+ def __init__(
206
+ self,
207
+ api_key: str,
208
+ base_url: str = DEFAULT_BASE_URL,
209
+ timeout: float = 30.0,
210
+ ) -> None:
211
+ if not api_key:
212
+ raise ValueError("api_key is required. Get one free at https://mansaapi.com")
213
+ http = _HttpClient(api_key, base_url, timeout)
214
+ self.markets = _Markets(http)
215
+ self.identity = _Identity(http)
216
+ self.location = _Location(http)
217
+ self.keys = _Keys(http)
mansaapi/errors.py ADDED
@@ -0,0 +1,17 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Any, Optional
4
+
5
+
6
+ class MansaAPIError(Exception):
7
+ """Raised when the Mansa API returns an error response."""
8
+
9
+ def __init__(self, code: str, message: str, status: int, raw: Optional[Any] = None) -> None:
10
+ super().__init__(message)
11
+ self.code = code
12
+ self.message = message
13
+ self.status = status
14
+ self.raw = raw
15
+
16
+ def __repr__(self) -> str: # pragma: no cover
17
+ return f"MansaAPIError(code={self.code!r}, status={self.status}, message={self.message!r})"
@@ -0,0 +1,146 @@
1
+ Metadata-Version: 2.4
2
+ Name: mansaapi
3
+ Version: 0.1.0
4
+ Summary: Official Python SDK for Mansa API — African market data, bank codes, location, and identity.
5
+ Project-URL: Homepage, https://mansaapi.com
6
+ Project-URL: Documentation, https://mansaapi.com/docs
7
+ Project-URL: Repository, https://github.com/HeyZod/mansaapi-python
8
+ Author: Mansa Labs
9
+ License: MIT
10
+ License-File: LICENSE
11
+ Keywords: africa,african,api,bank codes,fintech,ngx,nigeria,sdk,stock market
12
+ Requires-Python: >=3.8
13
+ Requires-Dist: requests>=2.25
14
+ Description-Content-Type: text/markdown
15
+
16
+ # mansaapi (Python)
17
+
18
+ Official Python SDK for [Mansa API](https://mansaapi.com) — African market data, bank codes, location, and financial identity in one clean REST layer.
19
+
20
+ [![PyPI version](https://img.shields.io/pypi/v/mansaapi.svg)](https://pypi.org/project/mansaapi/)
21
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
22
+
23
+ ## Install
24
+
25
+ ```bash
26
+ pip install mansaapi
27
+ ```
28
+
29
+ ## Quick start
30
+
31
+ ```python
32
+ from mansaapi import MansaAPI
33
+
34
+ mansa = MansaAPI(api_key="mansa_live_sk_...")
35
+
36
+ # GTBank by code
37
+ bank = mansa.identity.get_bank("058")
38
+ print(bank["data"]["name"]) # "Guaranty Trust Bank (GTBank)"
39
+ print(bank["data"]["swift_code"]) # "GTBINGLA"
40
+
41
+ # Pan-African market movers
42
+ movers = mansa.markets.get_pan_african_movers(limit=10)
43
+
44
+ # Nigerian public holidays 2026
45
+ holidays = mansa.location.get_holidays("NG", 2026)
46
+ ```
47
+
48
+ Get a free API key at [mansaapi.com](https://mansaapi.com) — 100 requests/day, no credit card.
49
+
50
+ ---
51
+
52
+ ## Markets
53
+
54
+ ```python
55
+ mansa.markets.get_exchanges()
56
+ mansa.markets.get_stocks("NGX", sector="Banking", limit=20)
57
+ mansa.markets.get_stock("NGX", "GTCO")
58
+ mansa.markets.get_movers("NGX", type="gainers")
59
+ mansa.markets.get_pan_african_movers()
60
+ mansa.markets.get_indices("NGX")
61
+ mansa.markets.get_etfs("NGX")
62
+ mansa.markets.get_forex()
63
+ mansa.markets.get_commodities()
64
+ mansa.markets.search("Zenith", exchange="NGX")
65
+ ```
66
+
67
+ ### Premium endpoints
68
+
69
+ Starter tier or higher for dividends, disclosures, recommendations.
70
+ Pro tier for insider trades. See [mansaapi.com/pricing](https://mansaapi.com/pricing).
71
+
72
+ ```python
73
+ mansa.markets.get_dividends("NGX", "GTCO") # Starter+
74
+ mansa.markets.get_disclosures("NGX", symbol="GTCO") # Starter+
75
+ mansa.markets.get_recommendations("NGX") # Starter+
76
+ mansa.markets.get_insider_trades("NGX", days=30) # Pro+
77
+ ```
78
+
79
+ ---
80
+
81
+ ## Identity
82
+
83
+ ```python
84
+ mansa.identity.get_banks() # all African banks
85
+ mansa.identity.get_banks(country="NG") # filter by country
86
+ mansa.identity.get_bank("057") # single bank by code
87
+ mansa.identity.get_mobile_networks(country="GH")
88
+ mansa.identity.lookup_mobile_network("08031234567")
89
+ mansa.identity.get_currencies()
90
+ mansa.identity.validate_nuban("0123456789", "058")
91
+ ```
92
+
93
+ ---
94
+
95
+ ## Location
96
+
97
+ ```python
98
+ mansa.location.get_countries()
99
+ mansa.location.get_country("NG")
100
+ mansa.location.get_states("NG")
101
+ mansa.location.get_lgas("NG", "LA") # Lagos LGAs
102
+ mansa.location.get_holidays("NG", 2026)
103
+ ```
104
+
105
+ ---
106
+
107
+ ## Error handling
108
+
109
+ ```python
110
+ from mansaapi import MansaAPI, MansaAPIError
111
+
112
+ mansa = MansaAPI(api_key="mansa_live_sk_...")
113
+
114
+ try:
115
+ data = mansa.markets.get_insider_trades("NGX")
116
+ except MansaAPIError as err:
117
+ print(err.code) # "TIER_REQUIRED"
118
+ print(err.status) # 403
119
+ print(err.message) # "This endpoint requires a professional tier key..."
120
+ ```
121
+
122
+ ### Error codes
123
+
124
+ | Code | Status | Meaning |
125
+ |---|---|---|
126
+ | `INVALID_API_KEY` | 401 | Key not found or revoked |
127
+ | `RATE_LIMIT_EXCEEDED` | 429 | Daily request limit reached |
128
+ | `TIER_REQUIRED` | 403 | Endpoint requires a higher tier |
129
+ | `NOT_FOUND` | 404 | Resource not found |
130
+ | `UPSTREAM_ERROR` | 502 | Upstream data source unreachable |
131
+
132
+ ---
133
+
134
+ ## Supported exchanges
135
+
136
+ NGX (Nigeria), GSE (Ghana), NSE (Kenya), JSE (South Africa), BRVM (West Africa), DSE (Tanzania), LUSE (Zambia).
137
+
138
+ ---
139
+
140
+ ## Links
141
+
142
+ - [Documentation](https://mansaapi.com/docs)
143
+ - [Python quickstart](https://mansaapi.com/docs/quickstart/python)
144
+ - [Pricing](https://mansaapi.com/pricing)
145
+ - [OpenAPI spec](https://mansaapi.com/openapi.json)
146
+ - [api@mansamarkets.com](mailto:api@mansamarkets.com)
@@ -0,0 +1,7 @@
1
+ mansaapi/__init__.py,sha256=2VzaTZ9dr7o4y-J8c7Nr7aawKcw9UcdFJZ7pikyB04Y,133
2
+ mansaapi/_client.py,sha256=_fKCKL-GoA0iIQ2SJa-oAkJTfGHSbcF6MlcpuE4y0Oc,7791
3
+ mansaapi/errors.py,sha256=r_nYFR30sPbQepTTCHkrsL2qBn1qhefp745gcLNKNYo,558
4
+ mansaapi-0.1.0.dist-info/METADATA,sha256=r5WgzypUJeZGQrCzcHV3OKJANvuo4yUUZhsBxk0pARY,4125
5
+ mansaapi-0.1.0.dist-info/WHEEL,sha256=mffPy8wBnZQn2VnJUU5jE99KsxaSfiyMHV9Yt0aLVxs,87
6
+ mansaapi-0.1.0.dist-info/licenses/LICENSE,sha256=q7hhjewpKS8xnbfZVR2DRRl2jFLn312baxygvcJ9Iw4,1067
7
+ mansaapi-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.30.1
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Mansa Labs
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.