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.
@@ -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,3 @@
1
+ include LICENSE
2
+ include README.md
3
+ include src/primeguardia/py.typed
@@ -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
+ [![PyPI version](https://img.shields.io/pypi/v/primeguardia.svg)](https://pypi.org/project/primeguardia/)
41
+ [![Python versions](https://img.shields.io/pypi/pyversions/primeguardia.svg)](https://pypi.org/project/primeguardia/)
42
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](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.