primeguardia 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.
- primeguardia-1.0.0/LICENSE +21 -0
- primeguardia-1.0.0/MANIFEST.in +3 -0
- primeguardia-1.0.0/PKG-INFO +640 -0
- primeguardia-1.0.0/README.md +605 -0
- primeguardia-1.0.0/pyproject.toml +63 -0
- primeguardia-1.0.0/setup.cfg +4 -0
- primeguardia-1.0.0/setup.py +4 -0
- primeguardia-1.0.0/src/primeguardia/__init__.py +55 -0
- primeguardia-1.0.0/src/primeguardia/client.py +520 -0
- primeguardia-1.0.0/src/primeguardia/exceptions.py +86 -0
- primeguardia-1.0.0/src/primeguardia/py.typed +0 -0
- primeguardia-1.0.0/src/primeguardia/types.py +532 -0
- primeguardia-1.0.0/src/primeguardia.egg-info/PKG-INFO +640 -0
- primeguardia-1.0.0/src/primeguardia.egg-info/SOURCES.txt +17 -0
- primeguardia-1.0.0/src/primeguardia.egg-info/dependency_links.txt +1 -0
- primeguardia-1.0.0/src/primeguardia.egg-info/requires.txt +13 -0
- primeguardia-1.0.0/src/primeguardia.egg-info/top_level.txt +1 -0
- primeguardia-1.0.0/tests/test_client.py +443 -0
- primeguardia-1.0.0/tests/test_live_prod_contract.py +158 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 PrimeGuardia
|
|
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,640 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: primeguardia
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Official PrimeGuardia Sanctions Screening SDK for Python
|
|
5
|
+
Author-email: PrimeGuardia <support@primeguardia.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://primeguardia.com
|
|
8
|
+
Project-URL: Documentation, https://docs.primeguardia.com
|
|
9
|
+
Project-URL: Bug Tracker, https://github.com/primeguardia/sanctions-sdk-python/issues
|
|
10
|
+
Keywords: sanctions,compliance,screening,aml,kyc,ofac,pep,primeguardia
|
|
11
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Typing :: Typed
|
|
21
|
+
Requires-Python: >=3.8
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
License-File: LICENSE
|
|
24
|
+
Requires-Dist: httpx>=0.24.0
|
|
25
|
+
Requires-Dist: typing-extensions>=4.0.0; python_version < "3.11"
|
|
26
|
+
Provides-Extra: dev
|
|
27
|
+
Requires-Dist: pytest>=7.0.0; extra == "dev"
|
|
28
|
+
Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"
|
|
29
|
+
Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
|
|
30
|
+
Requires-Dist: respx>=0.20.0; extra == "dev"
|
|
31
|
+
Requires-Dist: black>=23.0.0; extra == "dev"
|
|
32
|
+
Requires-Dist: mypy>=1.0.0; extra == "dev"
|
|
33
|
+
Requires-Dist: ruff>=0.0.260; extra == "dev"
|
|
34
|
+
Dynamic: license-file
|
|
35
|
+
|
|
36
|
+
# PrimeGuardia Python SDK
|
|
37
|
+
|
|
38
|
+
Official Python SDK for PrimeGuardia's Sanctions Screening API. Screen individuals and entities against global sanctions lists, PEPs, and watchlists with type-safe, Pythonic interfaces.
|
|
39
|
+
|
|
40
|
+
[](https://pypi.org/project/primeguardia/)
|
|
41
|
+
[](https://pypi.org/project/primeguardia/)
|
|
42
|
+
[](https://opensource.org/licenses/MIT)
|
|
43
|
+
|
|
44
|
+
## Features
|
|
45
|
+
|
|
46
|
+
- ✅ **Full type hints** for excellent IDE support
|
|
47
|
+
- ⚡ **Both sync and async** clients
|
|
48
|
+
- 🔄 **Automatic retry** with exponential backoff
|
|
49
|
+
- 🛡️ **Typed exceptions** for precise error handling
|
|
50
|
+
- 📊 **Dataclasses** for structured responses
|
|
51
|
+
- 🎯 **Context managers** for resource management
|
|
52
|
+
- 🔍 **Bulk screening** (up to 1,000 entities)
|
|
53
|
+
- 📡 **Real-time monitoring** support
|
|
54
|
+
- 🚀 **Production-ready** with connection pooling
|
|
55
|
+
- 🐍 **Pythonic** API design
|
|
56
|
+
|
|
57
|
+
## Installation
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
pip install primeguardia
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Or with Poetry:
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
poetry add primeguardia
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Quick Start (5 minutes)
|
|
70
|
+
|
|
71
|
+
### 1. Get your API key
|
|
72
|
+
|
|
73
|
+
Sign up at [primeguardia.com](https://primeguardia.com) and get your API key.
|
|
74
|
+
|
|
75
|
+
### 2. Initialize the client
|
|
76
|
+
|
|
77
|
+
```python
|
|
78
|
+
from primeguardia import PrimeGuardia
|
|
79
|
+
|
|
80
|
+
client = PrimeGuardia(api_key="your-api-key-here")
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### 3. Screen an entity
|
|
84
|
+
|
|
85
|
+
```python
|
|
86
|
+
result = client.screen(name="John Doe", email="john@example.com")
|
|
87
|
+
|
|
88
|
+
if result.should_block:
|
|
89
|
+
print("⚠️ SHOULD BE BLOCKED — review required")
|
|
90
|
+
print(f"Risk level: {result.risk_assessment}")
|
|
91
|
+
print(f"Matches: {result.matches}")
|
|
92
|
+
else:
|
|
93
|
+
print("✅ Clear - no matches found")
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
That's it! You're screening entities in 3 simple steps.
|
|
97
|
+
|
|
98
|
+
## Usage Examples
|
|
99
|
+
|
|
100
|
+
### Basic Screening
|
|
101
|
+
|
|
102
|
+
```python
|
|
103
|
+
from primeguardia import PrimeGuardia
|
|
104
|
+
|
|
105
|
+
client = PrimeGuardia(api_key="your-api-key")
|
|
106
|
+
|
|
107
|
+
# Screen by name
|
|
108
|
+
result = client.screen(name="Vladimir Putin")
|
|
109
|
+
print(f"Match: {result.match}")
|
|
110
|
+
print(f"Confidence: {result.confidence}")
|
|
111
|
+
print(f"Risk: {result.risk_assessment}")
|
|
112
|
+
|
|
113
|
+
# Use convenience properties
|
|
114
|
+
if result.is_high_risk:
|
|
115
|
+
print("⚠️ HIGH RISK DETECTED!")
|
|
116
|
+
|
|
117
|
+
if result.is_clear:
|
|
118
|
+
print("✅ All clear")
|
|
119
|
+
|
|
120
|
+
# Screen with additional context
|
|
121
|
+
result = client.screen(
|
|
122
|
+
name="John Smith",
|
|
123
|
+
email="john@example.com",
|
|
124
|
+
country="US",
|
|
125
|
+
date_of_birth="1980-01-01",
|
|
126
|
+
metadata={"customer_id": "CUST-12345"}
|
|
127
|
+
)
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### Context Manager
|
|
131
|
+
|
|
132
|
+
```python
|
|
133
|
+
# Automatically closes connection when done
|
|
134
|
+
with PrimeGuardia(api_key="your-key") as client:
|
|
135
|
+
result = client.screen(name="John Doe")
|
|
136
|
+
print(result.risk_assessment)
|
|
137
|
+
# Connection closed automatically
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
### Bulk Screening
|
|
141
|
+
|
|
142
|
+
```python
|
|
143
|
+
# Screen up to 1,000 entities in one request
|
|
144
|
+
results = client.bulk_screen(
|
|
145
|
+
names=["John Doe", "Jane Smith", "Vladimir Putin"],
|
|
146
|
+
emails=["john@example.com", "jane@example.com", "president@kremlin.ru"]
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
print(f"Processed {results.processed} entities")
|
|
150
|
+
print(f"Time: {results.processing_time_ms}ms")
|
|
151
|
+
print(f"High-risk matches: {results.high_risk_count}")
|
|
152
|
+
print(f"Total matches: {results.matches_count}")
|
|
153
|
+
|
|
154
|
+
# Iterate through results
|
|
155
|
+
for result in results.results:
|
|
156
|
+
if result.match:
|
|
157
|
+
print(f"⚠️ {result.name}: {result.risk_level} risk (score: {result.score})")
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
### Async Client
|
|
161
|
+
|
|
162
|
+
```python
|
|
163
|
+
import asyncio
|
|
164
|
+
from primeguardia import AsyncPrimeGuardia
|
|
165
|
+
|
|
166
|
+
async def main():
|
|
167
|
+
async with AsyncPrimeGuardia(api_key="your-key") as client:
|
|
168
|
+
# All methods support await
|
|
169
|
+
result = await client.screen(name="John Doe")
|
|
170
|
+
|
|
171
|
+
if result.should_block:
|
|
172
|
+
print(f"Blocked! Risk: {result.risk_assessment}")
|
|
173
|
+
|
|
174
|
+
# Concurrent requests
|
|
175
|
+
tasks = [
|
|
176
|
+
client.screen(name="Person 1"),
|
|
177
|
+
client.screen(name="Person 2"),
|
|
178
|
+
client.screen(name="Person 3"),
|
|
179
|
+
]
|
|
180
|
+
results = await asyncio.gather(*tasks)
|
|
181
|
+
|
|
182
|
+
for result in results:
|
|
183
|
+
print(f"Match: {result.match}, Score: {result.score}")
|
|
184
|
+
|
|
185
|
+
# Run async code
|
|
186
|
+
asyncio.run(main())
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
### Search Database
|
|
190
|
+
|
|
191
|
+
```python
|
|
192
|
+
# Search sanctions database
|
|
193
|
+
results = client.search(
|
|
194
|
+
query="putin",
|
|
195
|
+
limit=20,
|
|
196
|
+
sources=["ofac", "eu_sanctions"]
|
|
197
|
+
)
|
|
198
|
+
|
|
199
|
+
print(f"Found {results.total} matches")
|
|
200
|
+
|
|
201
|
+
# Check if there are more results
|
|
202
|
+
if results.has_more:
|
|
203
|
+
print("More results available. Increase limit or offset.")
|
|
204
|
+
|
|
205
|
+
# Iterate through entities
|
|
206
|
+
for entity in results.results:
|
|
207
|
+
print(f"{entity.name}")
|
|
208
|
+
print(f" Sources: {', '.join(entity.source_dataset)}")
|
|
209
|
+
print(f" Countries: {', '.join(entity.countries or [])}")
|
|
210
|
+
|
|
211
|
+
# Get specific entity
|
|
212
|
+
entity = client.get_entity(12345)
|
|
213
|
+
print(entity.name, entity.source_dataset)
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
### Continuous Monitoring
|
|
217
|
+
|
|
218
|
+
```python
|
|
219
|
+
# Add entity to monitoring
|
|
220
|
+
monitored = client.add_monitoring(
|
|
221
|
+
name="Suspicious Person",
|
|
222
|
+
email="suspicious@example.com",
|
|
223
|
+
frequency=24, # Check every 24 hours
|
|
224
|
+
metadata={"internal_id": "CUST-12345"}
|
|
225
|
+
)
|
|
226
|
+
|
|
227
|
+
print(f"Now monitoring entity {monitored.id}")
|
|
228
|
+
|
|
229
|
+
# Get all monitored entities
|
|
230
|
+
entities = client.get_monitored_entities()
|
|
231
|
+
print(f"Monitoring {len(entities)} entities")
|
|
232
|
+
|
|
233
|
+
# Note: Full monitoring API coming in next version
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
### Account Management
|
|
237
|
+
|
|
238
|
+
```python
|
|
239
|
+
# Get profile
|
|
240
|
+
profile = client.get_profile()
|
|
241
|
+
print(f"Client: {profile.client_name}")
|
|
242
|
+
print(f"Tier: {profile.tier}")
|
|
243
|
+
print(f"Status: {profile.subscription_status}")
|
|
244
|
+
print(f"Usage: {profile.usage_percentage:.1f}%")
|
|
245
|
+
|
|
246
|
+
# Check if subscription is active
|
|
247
|
+
if profile.is_active:
|
|
248
|
+
print("✅ Subscription active")
|
|
249
|
+
|
|
250
|
+
# Check quota
|
|
251
|
+
quota = client.get_quota_status()
|
|
252
|
+
print(f"Used: {quota.used}/{quota.limit} ({quota.percentage}%)")
|
|
253
|
+
print(f"Remaining: {quota.remaining} calls")
|
|
254
|
+
|
|
255
|
+
# Check quota status with convenience methods
|
|
256
|
+
if quota.is_critical:
|
|
257
|
+
print("⚠️ CRITICAL: >95% of quota used!")
|
|
258
|
+
elif quota.is_low:
|
|
259
|
+
print("⚠️ Warning: >80% of quota used")
|
|
260
|
+
|
|
261
|
+
if quota.is_exceeded:
|
|
262
|
+
print("❌ Quota exceeded!")
|
|
263
|
+
|
|
264
|
+
# Get available datasets
|
|
265
|
+
datasets = client.get_datasets()
|
|
266
|
+
for dataset in datasets:
|
|
267
|
+
status = "✅" if dataset.available else "❌ Upgrade required"
|
|
268
|
+
print(f"{status} {dataset.name}: {dataset.record_count:,} records")
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
## Error Handling
|
|
272
|
+
|
|
273
|
+
The SDK provides typed exceptions for precise error handling:
|
|
274
|
+
|
|
275
|
+
```python
|
|
276
|
+
from primeguardia import (
|
|
277
|
+
PrimeGuardia,
|
|
278
|
+
AuthenticationError,
|
|
279
|
+
QuotaExceededError,
|
|
280
|
+
RateLimitError,
|
|
281
|
+
ValidationError,
|
|
282
|
+
PrimeGuardiaError
|
|
283
|
+
)
|
|
284
|
+
|
|
285
|
+
client = PrimeGuardia(api_key="your-key")
|
|
286
|
+
|
|
287
|
+
try:
|
|
288
|
+
result = client.screen(name="John Doe")
|
|
289
|
+
except AuthenticationError as e:
|
|
290
|
+
print(f"Invalid API key: {e}")
|
|
291
|
+
# Update API key
|
|
292
|
+
except QuotaExceededError as e:
|
|
293
|
+
print(f"Quota exceeded: {e}")
|
|
294
|
+
# Upgrade plan or wait for reset
|
|
295
|
+
except RateLimitError as e:
|
|
296
|
+
print(f"Rate limit exceeded: {e}")
|
|
297
|
+
if e.retry_after:
|
|
298
|
+
print(f"Retry after {e.retry_after} seconds")
|
|
299
|
+
time.sleep(e.retry_after)
|
|
300
|
+
except ValidationError as e:
|
|
301
|
+
print(f"Validation error: {e}")
|
|
302
|
+
if e.details:
|
|
303
|
+
print(f"Details: {e.details}")
|
|
304
|
+
except PrimeGuardiaError as e:
|
|
305
|
+
print(f"API error ({e.status_code}): {e}")
|
|
306
|
+
except Exception as e:
|
|
307
|
+
print(f"Unexpected error: {e}")
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
## Configuration
|
|
311
|
+
|
|
312
|
+
```python
|
|
313
|
+
client = PrimeGuardia(
|
|
314
|
+
api_key="your-api-key", # Required
|
|
315
|
+
base_url="https://api.primeguardia.com", # Optional
|
|
316
|
+
timeout=30.0, # Request timeout in seconds
|
|
317
|
+
max_retries=3, # Max retry attempts
|
|
318
|
+
debug=False # Enable debug logging
|
|
319
|
+
)
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
## Type Safety
|
|
323
|
+
|
|
324
|
+
Full type hints for excellent IDE support:
|
|
325
|
+
|
|
326
|
+
```python
|
|
327
|
+
from primeguardia import (
|
|
328
|
+
PrimeGuardia,
|
|
329
|
+
ScreeningResult,
|
|
330
|
+
BulkScreeningResult,
|
|
331
|
+
ClientProfile,
|
|
332
|
+
ConfidenceLevel,
|
|
333
|
+
RiskLevel,
|
|
334
|
+
)
|
|
335
|
+
|
|
336
|
+
client: PrimeGuardia = PrimeGuardia(api_key="your-key")
|
|
337
|
+
|
|
338
|
+
# Type checking works perfectly
|
|
339
|
+
result: ScreeningResult = client.screen(name="John Doe")
|
|
340
|
+
confidence: ConfidenceLevel = result.confidence # "high" | "medium" | "low" | "none"
|
|
341
|
+
risk: RiskLevel = result.risk_assessment # "HIGH" | "MEDIUM" | "LOW" | "CLEAR"
|
|
342
|
+
|
|
343
|
+
# Dataclass properties
|
|
344
|
+
profile: ClientProfile = client.get_profile()
|
|
345
|
+
usage_pct: float = profile.usage_percentage
|
|
346
|
+
is_active: bool = profile.is_active
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
## Framework Integration
|
|
350
|
+
|
|
351
|
+
### Django View
|
|
352
|
+
|
|
353
|
+
```python
|
|
354
|
+
from django.http import JsonResponse
|
|
355
|
+
from django.views import View
|
|
356
|
+
from primeguardia import PrimeGuardia, PrimeGuardiaError
|
|
357
|
+
|
|
358
|
+
class ScreeningView(View):
|
|
359
|
+
def __init__(self, *args, **kwargs):
|
|
360
|
+
super().__init__(*args, **kwargs)
|
|
361
|
+
self.client = PrimeGuardia(api_key=settings.PRIMEGUARDIA_API_KEY)
|
|
362
|
+
|
|
363
|
+
def post(self, request):
|
|
364
|
+
name = request.POST.get('name')
|
|
365
|
+
email = request.POST.get('email')
|
|
366
|
+
|
|
367
|
+
try:
|
|
368
|
+
result = self.client.screen(name=name, email=email)
|
|
369
|
+
|
|
370
|
+
if result.should_block:
|
|
371
|
+
return JsonResponse({
|
|
372
|
+
'allowed': False,
|
|
373
|
+
'reason': 'Sanctions screening failed',
|
|
374
|
+
'risk_level': result.risk_assessment
|
|
375
|
+
}, status=403)
|
|
376
|
+
|
|
377
|
+
return JsonResponse({'allowed': True})
|
|
378
|
+
|
|
379
|
+
except PrimeGuardiaError as e:
|
|
380
|
+
return JsonResponse({'error': str(e)}, status=500)
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
### Flask API
|
|
384
|
+
|
|
385
|
+
```python
|
|
386
|
+
from flask import Flask, request, jsonify
|
|
387
|
+
from primeguardia import PrimeGuardia
|
|
388
|
+
import os
|
|
389
|
+
|
|
390
|
+
app = Flask(__name__)
|
|
391
|
+
client = PrimeGuardia(api_key=os.getenv('PRIMEGUARDIA_API_KEY'))
|
|
392
|
+
|
|
393
|
+
@app.route('/api/screen', methods=['POST'])
|
|
394
|
+
def screen():
|
|
395
|
+
data = request.get_json()
|
|
396
|
+
|
|
397
|
+
result = client.screen(
|
|
398
|
+
name=data.get('name'),
|
|
399
|
+
email=data.get('email')
|
|
400
|
+
)
|
|
401
|
+
|
|
402
|
+
return jsonify({
|
|
403
|
+
'match': result.match,
|
|
404
|
+
'should_block': result.should_block,
|
|
405
|
+
'risk_assessment': result.risk_assessment,
|
|
406
|
+
'confidence': result.confidence,
|
|
407
|
+
'score': result.score
|
|
408
|
+
})
|
|
409
|
+
|
|
410
|
+
@app.route('/api/health')
|
|
411
|
+
def health():
|
|
412
|
+
try:
|
|
413
|
+
client.test_connection()
|
|
414
|
+
return jsonify({'status': 'ok'})
|
|
415
|
+
except Exception as e:
|
|
416
|
+
return jsonify({'status': 'error', 'message': str(e)}), 500
|
|
417
|
+
```
|
|
418
|
+
|
|
419
|
+
### FastAPI
|
|
420
|
+
|
|
421
|
+
```python
|
|
422
|
+
from fastapi import FastAPI, HTTPException
|
|
423
|
+
from pydantic import BaseModel
|
|
424
|
+
from primeguardia import AsyncPrimeGuardia, PrimeGuardiaError
|
|
425
|
+
import os
|
|
426
|
+
|
|
427
|
+
app = FastAPI()
|
|
428
|
+
client = AsyncPrimeGuardia(api_key=os.getenv('PRIMEGUARDIA_API_KEY'))
|
|
429
|
+
|
|
430
|
+
class ScreenRequest(BaseModel):
|
|
431
|
+
name: str
|
|
432
|
+
email: str | None = None
|
|
433
|
+
|
|
434
|
+
@app.post("/api/screen")
|
|
435
|
+
async def screen(request: ScreenRequest):
|
|
436
|
+
try:
|
|
437
|
+
result = await client.screen(
|
|
438
|
+
name=request.name,
|
|
439
|
+
email=request.email
|
|
440
|
+
)
|
|
441
|
+
|
|
442
|
+
return {
|
|
443
|
+
"match": result.match,
|
|
444
|
+
"should_block": result.should_block,
|
|
445
|
+
"risk_assessment": result.risk_assessment,
|
|
446
|
+
"confidence": result.confidence
|
|
447
|
+
}
|
|
448
|
+
except PrimeGuardiaError as e:
|
|
449
|
+
raise HTTPException(status_code=500, detail=str(e))
|
|
450
|
+
|
|
451
|
+
@app.on_event("shutdown")
|
|
452
|
+
async def shutdown():
|
|
453
|
+
await client.close()
|
|
454
|
+
```
|
|
455
|
+
|
|
456
|
+
### Celery Task
|
|
457
|
+
|
|
458
|
+
```python
|
|
459
|
+
from celery import Celery
|
|
460
|
+
from primeguardia import PrimeGuardia
|
|
461
|
+
import os
|
|
462
|
+
|
|
463
|
+
app = Celery('tasks', broker='redis://localhost:6379')
|
|
464
|
+
client = PrimeGuardia(api_key=os.getenv('PRIMEGUARDIA_API_KEY'))
|
|
465
|
+
|
|
466
|
+
@app.task
|
|
467
|
+
def screen_user(user_id, name, email):
|
|
468
|
+
"""Background task to screen a user"""
|
|
469
|
+
result = client.screen(name=name, email=email)
|
|
470
|
+
|
|
471
|
+
if result.should_block:
|
|
472
|
+
# Handle blocked user
|
|
473
|
+
send_alert(user_id, result.risk_assessment)
|
|
474
|
+
block_user_account(user_id)
|
|
475
|
+
|
|
476
|
+
return {
|
|
477
|
+
'user_id': user_id,
|
|
478
|
+
'should_block': result.should_block,
|
|
479
|
+
'score': result.score
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
@app.task
|
|
483
|
+
def bulk_screen_users(users):
|
|
484
|
+
"""Background task to screen multiple users"""
|
|
485
|
+
names = [u['name'] for u in users]
|
|
486
|
+
emails = [u['email'] for u in users]
|
|
487
|
+
|
|
488
|
+
results = client.bulk_screen(names=names, emails=emails)
|
|
489
|
+
|
|
490
|
+
# Process results
|
|
491
|
+
for user, result in zip(users, results.results):
|
|
492
|
+
if result.match:
|
|
493
|
+
handle_match(user['id'], result)
|
|
494
|
+
```
|
|
495
|
+
|
|
496
|
+
## Best Practices
|
|
497
|
+
|
|
498
|
+
### 1. Use Environment Variables
|
|
499
|
+
|
|
500
|
+
```python
|
|
501
|
+
import os
|
|
502
|
+
from primeguardia import PrimeGuardia
|
|
503
|
+
|
|
504
|
+
# ✅ Good
|
|
505
|
+
client = PrimeGuardia(api_key=os.getenv('PRIMEGUARDIA_API_KEY'))
|
|
506
|
+
|
|
507
|
+
# ❌ Bad - never hardcode
|
|
508
|
+
client = PrimeGuardia(api_key='abc123...')
|
|
509
|
+
```
|
|
510
|
+
|
|
511
|
+
### 2. Use Context Managers
|
|
512
|
+
|
|
513
|
+
```python
|
|
514
|
+
# ✅ Good - automatically closes connection
|
|
515
|
+
with PrimeGuardia(api_key=api_key) as client:
|
|
516
|
+
result = client.screen(name="John Doe")
|
|
517
|
+
|
|
518
|
+
# ❌ Less optimal - manual cleanup
|
|
519
|
+
client = PrimeGuardia(api_key=api_key)
|
|
520
|
+
result = client.screen(name="John Doe")
|
|
521
|
+
client.close() # Easy to forget!
|
|
522
|
+
```
|
|
523
|
+
|
|
524
|
+
### 3. Cache Results
|
|
525
|
+
|
|
526
|
+
```python
|
|
527
|
+
from functools import lru_cache
|
|
528
|
+
from primeguardia import PrimeGuardia
|
|
529
|
+
|
|
530
|
+
client = PrimeGuardia(api_key="your-key")
|
|
531
|
+
|
|
532
|
+
@lru_cache(maxsize=1000)
|
|
533
|
+
def screen_cached(name: str, email: str):
|
|
534
|
+
"""Cache screening results for 1000 unique entities"""
|
|
535
|
+
result = client.screen(name=name, email=email)
|
|
536
|
+
return result.should_block, result.risk_assessment
|
|
537
|
+
|
|
538
|
+
# Or use Redis/Memcached for distributed caching
|
|
539
|
+
```
|
|
540
|
+
|
|
541
|
+
### 4. Handle Errors Gracefully
|
|
542
|
+
|
|
543
|
+
```python
|
|
544
|
+
def safe_screen(name, email):
|
|
545
|
+
"""Fail-safe screening with fallback"""
|
|
546
|
+
try:
|
|
547
|
+
result = client.screen(name=name, email=email)
|
|
548
|
+
return result.should_block
|
|
549
|
+
except QuotaExceededError:
|
|
550
|
+
logger.error("Quota exceeded!")
|
|
551
|
+
# Fail safely - don't block legitimate users
|
|
552
|
+
return False
|
|
553
|
+
except PrimeGuardiaError as e:
|
|
554
|
+
logger.error(f"Screening failed: {e}")
|
|
555
|
+
# Decide on failure mode based on compliance requirements
|
|
556
|
+
return False # or True for fail-closed
|
|
557
|
+
```
|
|
558
|
+
|
|
559
|
+
### 5. Use Bulk Operations
|
|
560
|
+
|
|
561
|
+
```python
|
|
562
|
+
# ✅ Good - bulk operation
|
|
563
|
+
users = [{"name": "User 1", "email": "user1@example.com"}, ...]
|
|
564
|
+
results = client.bulk_screen(
|
|
565
|
+
names=[u["name"] for u in users],
|
|
566
|
+
emails=[u["email"] for u in users]
|
|
567
|
+
)
|
|
568
|
+
|
|
569
|
+
# ❌ Less efficient - individual calls
|
|
570
|
+
for user in users:
|
|
571
|
+
result = client.screen(name=user["name"], email=user["email"])
|
|
572
|
+
```
|
|
573
|
+
|
|
574
|
+
## Development
|
|
575
|
+
|
|
576
|
+
### Running Tests
|
|
577
|
+
|
|
578
|
+
```bash
|
|
579
|
+
pytest
|
|
580
|
+
```
|
|
581
|
+
|
|
582
|
+
### Type Checking
|
|
583
|
+
|
|
584
|
+
```bash
|
|
585
|
+
mypy src/primeguardia
|
|
586
|
+
```
|
|
587
|
+
|
|
588
|
+
### Code Formatting
|
|
589
|
+
|
|
590
|
+
```bash
|
|
591
|
+
black src/
|
|
592
|
+
ruff check src/
|
|
593
|
+
```
|
|
594
|
+
|
|
595
|
+
## Troubleshooting
|
|
596
|
+
|
|
597
|
+
### Timeout Issues
|
|
598
|
+
|
|
599
|
+
```python
|
|
600
|
+
# Increase timeout for slow connections
|
|
601
|
+
client = PrimeGuardia(api_key="your-key", timeout=60.0)
|
|
602
|
+
```
|
|
603
|
+
|
|
604
|
+
### Debug Mode
|
|
605
|
+
|
|
606
|
+
```python
|
|
607
|
+
# Enable debug logging
|
|
608
|
+
client = PrimeGuardia(api_key="your-key", debug=True)
|
|
609
|
+
```
|
|
610
|
+
|
|
611
|
+
### Test Connection
|
|
612
|
+
|
|
613
|
+
```python
|
|
614
|
+
try:
|
|
615
|
+
client.test_connection()
|
|
616
|
+
print("✅ Connection successful")
|
|
617
|
+
except AuthenticationError:
|
|
618
|
+
print("❌ Invalid API key")
|
|
619
|
+
except Exception as e:
|
|
620
|
+
print(f"❌ Connection failed: {e}")
|
|
621
|
+
```
|
|
622
|
+
|
|
623
|
+
## Requirements
|
|
624
|
+
|
|
625
|
+
- Python 3.8+
|
|
626
|
+
- httpx >= 0.24.0
|
|
627
|
+
|
|
628
|
+
## Support
|
|
629
|
+
|
|
630
|
+
- 📧 Email: support@primeguardia.com
|
|
631
|
+
- 📚 Documentation: https://docs.primeguardia.com
|
|
632
|
+
- 🐛 Issues: https://github.com/primeguardia/sanctions-sdk-python/issues
|
|
633
|
+
|
|
634
|
+
## License
|
|
635
|
+
|
|
636
|
+
MIT © PrimeGuardia
|
|
637
|
+
|
|
638
|
+
## Contributing
|
|
639
|
+
|
|
640
|
+
Contributions welcome! Please see [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
|