Python SDK for OutScope API
Project description
OutScope SDK
Python client library for the OutScope API.
Features
- ✅ Easy to use - Simple, intuitive API
- ✅ Pagination support - Automatic and manual pagination
- ✅ Error handling - Detailed error information with retry capabilities
- ✅ Usage monitoring - Query limits and usage in real-time
- ✅ Type hints - Full type annotation support
- ✅ Context manager - Automatic resource cleanup
Installation
pip install outscope-sdk
Or install from source:
cd api_client
pip install -e .
Quick Start
from outscope_sdk import Client
# Create a client
client = Client(api_key="your_api_key_here")
# Create a security check
check = client.checks.create(
fqdn="example.com",
paths=["/", "/api"],
ports=[443]
)
print(f"Check created: {check.job_id}")
# Get check status
status = client.checks.get(check.job_id)
print(f"Status: {status['status']}")
# List all checks with automatic pagination
for check in client.checks.list_all():
print(f"{check['job_id']}: {check['status']}")
# Check your usage
usage = client.usage.get()
print(f"Used: {usage['usage']['checks_used']}/{usage['usage']['checks_limit']}")
# Close the client
client.close()
Authentication
The SDK requires an API key for authentication. You can generate one from your OutScope dashboard.
from outscope_sdk import Client
# Option 1: Direct in code
client = Client(api_key="your_api_key_here")
# Option 2: From environment variable
import os
client = Client(api_key=os.getenv("OUTSCOPE_API_KEY"))
Usage Guide
Creating Checks
# Basic check
check = client.checks.create(
fqdn="example.com",
paths=["/"],
ports=[443]
)
# Advanced check with multiple paths and ports
check = client.checks.create(
fqdn="api.example.com",
paths=["/", "/api/v1", "/health"],
ports=[80, 443, 8080],
redirects=True
)
# Privacy-aware check with content sample
check = client.checks.create(
fqdn="secure.example.com",
paths=["/"],
ports=[443],
include_content_sample=True, # Enable content sample capture
content_sample_retention_days=7 # Retain for 7 days (default: 7, max: 30)
)
print(f"Check ID: {check.job_id}")
print(f"Status: {check.status}")
Creating Multiple Checks (Batch)
The SDK includes built-in batch processing with automatic rate limit handling:
# Simple batch creation
result = client.checks.create_batch(
domains=["example.com", "example.org", "example.net"]
)
print(f"Created: {result['stats']['created']}")
print(f"Failed: {result['stats']['failed']}")
print(f"Duration: {result['duration']:.1f}s")
# Access results
for check in result['results']:
print(f"{check['domain']}: {check['job_id']}")
Large Batch with Progress
# Create 1000 checks with automatic handling of all limits
def show_progress(current, total, stats):
if current % 50 == 0: # Update every 50 checks
print(f"Progress: {current}/{total} - Created: {stats['created']}, Failed: {stats['failed']}")
result = client.checks.create_batch(
domains=[f"site{i}.com" for i in range(1000)],
paths=["/", "/api"],
ports=[443],
include_content_sample=False, # Don't capture content samples for privacy
check_usage_first=True, # Verify quota before starting
wait_on_limits=True, # Wait when hitting limits (recommended)
max_retries=5, # Retry up to 5 times per check
progress_callback=show_progress
)
# Results
print(f"\n✅ Successfully created {result['stats']['created']} checks")
print(f"⏱️ Total time: {result['duration']/60:.1f} minutes")
print(f"📊 Rate limited: {result['stats']['rate_limited']} times")
print(f"📊 Inflight limited: {result['stats']['inflight_limited']} times")
What create_batch() does automatically:
- ✅ Checks your quota before starting
- ✅ Waits when rate limit is hit (30-120 req/min depending on plan)
- ✅ Waits when inflight limit is hit (5-30 concurrent checks depending on plan)
- ✅ Retries automatically on temporary errors
- ✅ Stops if monthly quota is exceeded
- ✅ Provides detailed statistics
Estimated times for 1000 checks:
- Starter plan (30/min): ~35 minutes
- Pro plan (60/min): ~18 minutes
- Team plan (120/min): ~9 minutes
Getting Check Status
# Get a specific check
check = client.checks.get("check_id_here")
print(f"Status: {check['status']}")
print(f"Created: {check['created_at']}")
if check['status'] == 'done':
print(f"Results: {check['result']}")
Listing Checks
Manual Pagination
# Get first page (50 items)
response = client.checks.list(page=1, limit=50)
print(f"Total checks: {response['total']}")
print(f"Page {response['page']} of {response['total'] // response['per_page'] + 1}")
for check in response['items']:
print(f"- {check['job_id']}: {check['status']}")
# Get next page
response = client.checks.list(page=2, limit=50)
Automatic Pagination (Recommended)
# Iterate through ALL checks automatically
for check in client.checks.list_all(limit=100):
print(f"{check['job_id']}: {check['status']}")
Filtering
# Filter by FQDN
for check in client.checks.list_all(fqdn="example.com"):
print(f"{check['job_id']}: {check['fqdn_normalized']}")
Monitoring Usage and Limits
# Get current usage
usage = client.usage.get()
# Tenant info
print(f"Tenant: {usage['tenant']['name']}")
print(f"Plan: {usage['tenant']['plan']}")
# Current usage
print(f"\nChecks used: {usage['usage']['checks_used']}/{usage['usage']['checks_limit']}")
print(f"In progress: {usage['usage']['inflight']}")
# Limits
print(f"\nRate limit: {usage['limits']['rate_per_minute']}/min")
print(f"Max inflight: {usage['limits']['max_inflight']}")
print(f"Retention: {usage['limits']['retention_days']} days")
# Check if approaching limits
checks_percent = (usage['usage']['checks_used'] / usage['usage']['checks_limit']) * 100
if checks_percent > 80:
print("⚠️ Warning: You've used over 80% of your monthly quota!")
Error Handling
The SDK provides detailed error information, especially for rate limiting scenarios.
Basic Error Handling
from outscope_sdk import Client
from outscope_sdk.exceptions import (
RateLimitError,
AuthenticationError,
NotFoundError,
ValidationError
)
client = Client(api_key="...")
try:
check = client.checks.create(fqdn="example.com", paths=["/"], ports=[443])
except AuthenticationError as e:
print(f"Authentication failed: {e.message}")
except NotFoundError as e:
print(f"Resource not found: {e.message}")
except ValidationError as e:
print(f"Validation error: {e.message}")
print(f"Details: {e.details}")
except RateLimitError as e:
print(f"Rate limit exceeded: {e.message}")
# Detailed error handling below
Rate Limit Error Handling
The RateLimitError provides detailed information about the rate limit:
from outscope_sdk.exceptions import RateLimitError
import time
try:
check = client.checks.create(...)
except RateLimitError as e:
print(f"Error: {e.message}")
# Check error type
if e.code == "rate_limit_exceeded":
# Too many requests per minute
print(f"Rate limit: {e.limit}/min")
print(f"Remaining: {e.remaining}")
print(f"Retry after: {e.retry_after}s")
print(f"Resets at: {time.ctime(e.reset_at)}")
# Wait and retry
time.sleep(e.retry_after)
check = client.checks.create(...)
elif e.code == "inflight_limit":
# Too many concurrent checks
print(f"In progress: {e.current}/{e.limit}")
print(f"Wait for some checks to complete")
print(f"Suggested retry: {e.retry_after}s")
elif e.code == "checks_limit":
# Monthly quota exceeded
print(f"Monthly quota: {e.used}/{e.limit} for {e.period}")
print(f"Wait until next month or upgrade your plan")
Automatic Retry
Implement automatic retry with exponential backoff:
def create_check_with_retry(client, fqdn, max_retries=3):
"""Create a check with automatic retry on rate limit"""
for attempt in range(max_retries):
try:
return client.checks.create(fqdn=fqdn, paths=["/"], ports=[443])
except RateLimitError as e:
if attempt == max_retries - 1:
raise # Last attempt, re-raise the error
retry_after = e.retry_after or 60
print(f"Rate limited. Retrying in {retry_after}s... (attempt {attempt+1}/{max_retries})")
time.sleep(retry_after)
# Use it
check = create_check_with_retry(client, "example.com")
Proactive Limit Checking
Check limits before making requests to avoid errors:
# Check usage before creating checks
usage = client.usage.get()
# Check monthly quota
if usage['usage']['checks_used'] >= usage['usage']['checks_limit']:
print("❌ Monthly quota exceeded")
print(" Upgrade your plan or wait until next month")
exit(1)
# Check concurrent limit
if usage['usage']['inflight'] >= usage['limits']['max_inflight']:
print(f"❌ Too many checks in progress ({usage['usage']['inflight']}/{usage['limits']['max_inflight']})")
print(" Wait for some checks to complete")
exit(1)
# Calculate available quota
remaining = usage['usage']['checks_limit'] - usage['usage']['checks_used']
print(f"✅ You can create {remaining} more checks this month")
# Now create the check
check = client.checks.create(...)
Advanced Usage
Using Context Manager
The recommended way to use the client is with a context manager for automatic cleanup:
from outscope_sdk import Client
with Client(api_key="...") as client:
# Your code here
check = client.checks.create(...)
usage = client.usage.get()
# Client is automatically closed here
Custom Timeout
# Increase timeout for long-running operations
client = Client(
api_key="...",
timeout=120.0 # 2 minutes
)
Batch Operations
# Check multiple domains
domains = ["example.com", "example.org", "example.net"]
for domain in domains:
try:
check = client.checks.create(fqdn=domain, paths=["/"], ports=[443])
print(f"✅ {domain}: {check.job_id}")
except RateLimitError as e:
print(f"⚠️ {domain}: Rate limited, waiting {e.retry_after}s...")
time.sleep(e.retry_after)
check = client.checks.create(fqdn=domain, paths=["/"], ports=[443])
print(f"✅ {domain}: {check.job_id} (retried)")
API Reference
Client
Client(api_key: str, base_url: Optional[str] = None, timeout: float = 30.0)
Attributes
checks: ChecksResource - Manage security checksusage: UsageResource - Query usage and limits
Methods
close(): Close the HTTP client__enter__(): Context manager entry__exit__(): Context manager exit
ChecksResource
Methods
create()
create(
fqdn: str,
paths: Optional[List[str]] = None,
ports: Optional[List[int]] = None,
redirects: Optional[bool] = None,
include_content_sample: Optional[bool] = None,
content_sample_retention_days: Optional[int] = None
) -> Check
Create a new security check.
Parameters:
fqdn: Fully qualified domain name to checkpaths: Paths to check (default:["/"])ports: Ports to check (default:[443])redirects: Follow redirects (optional)include_content_sample: Capture HTTP response content sample (default:False)content_sample_retention_days: Days to retain content sample (default:7, max:30)
Returns: Check object with job_id and status
Note: Content samples may contain sensitive data. Only enable when necessary and use appropriate retention periods.
create_batch()
create_batch(
domains: List[str],
paths: Optional[List[str]] = None,
ports: Optional[List[int]] = None,
redirects: Optional[bool] = None,
include_content_sample: Optional[bool] = None,
content_sample_retention_days: Optional[int] = None,
check_usage_first: bool = True,
wait_on_limits: bool = True,
max_retries: int = 5,
progress_callback: Optional[Callable[[int, int, Dict], None]] = None
) -> Dict[str, Any]
Create multiple checks in batch with automatic rate limit handling.
Parameters:
domains: List of FQDNs to checkpaths: Paths to check (default:["/"])ports: Ports to check (default:[443])redirects: Follow redirects (optional)include_content_sample: Capture content samples (default:False)content_sample_retention_days: Days to retain content samples (default:7, max:30)check_usage_first: Verify quota before starting (default:True)wait_on_limits: Wait when hitting limits (default:True)max_retries: Max retries per check (default:5)progress_callback: Optional callback for progress updates
Returns: Dict with:
results: List of created checksstats: Statistics dict (created, failed, rate_limited, etc.)duration: Total time in seconds
get()
get(check_id: str) -> Dict[str, Any]
Get details of a specific check.
Returns: Dict with check details
list()
list(
page: int = 1,
limit: int = 50,
fqdn: Optional[str] = None
) -> Dict[str, Any]
List checks with pagination.
Returns: Dict with items, total, page, per_page
list_all()
list_all(
limit: int = 50,
fqdn: Optional[str] = None
) -> Iterator[Dict[str, Any]]
Iterate through all checks automatically.
Yields: Individual check items
UsageResource
Methods
get()
get() -> Dict[str, Any]
Get current usage and limits.
Returns: Dict with tenant, period, usage, limits, plan
Exceptions
ApiError
Base exception for all API errors.
Attributes:
message: Error messagestatus_code: HTTP status codedetails: Additional error details
RateLimitError
Raised when rate limit is exceeded.
Attributes:
code: Error code (rate_limit_exceeded,inflight_limit,checks_limit)message: Descriptive error messageretry_after: Seconds to wait before retryinglimit: The limit that was exceededremaining: Remaining requests (for rate_limit_exceeded)reset_at: Unix timestamp when rate limit resets (for rate_limit_exceeded)current: Current value (for inflight_limit)used: Used quota (for checks_limit)period: Period for the quota (for checks_limit)
AuthenticationError
Raised when authentication fails (401).
NotFoundError
Raised when resource is not found (404).
ValidationError
Raised when request validation fails (400).
ServerError
Raised when server error occurs (500+).
Changelog
v0.1.0
- Privacy controls for content samples
- Configurable retention periods
- Enhanced batch operations
- Improved documentation
- Pagination support
- Automatic iteration with
list_all() - Enhanced error handling with detailed rate limit info
- Usage monitoring
- Batch operations with automatic rate limit handling
- Basic CRUD operations
- API key authentication
- Initial release
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
License
MIT License - see LICENSE file for details.
Support
- Documentation: https://docs.outscope.es
- Issues: GitHub Issues
- Email: support@outscope.es
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file outscope_sdk-0.1.1.tar.gz.
File metadata
- Download URL: outscope_sdk-0.1.1.tar.gz
- Upload date:
- Size: 10.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
cccbbb70ec855b6b2befa8654c727462e02638f123da5b826514cb9e51653884
|
|
| MD5 |
4eb94ab4988e52a0d9e9bd27b6dcf00a
|
|
| BLAKE2b-256 |
7f7e28cfcc6488822fe354f6a831a0a519278b9c73ac9d3da47a7854b6d17769
|
File details
Details for the file outscope_sdk-0.1.1-py3-none-any.whl.
File metadata
- Download URL: outscope_sdk-0.1.1-py3-none-any.whl
- Upload date:
- Size: 12.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
196288cfc59f5ef0ab41170f668a8b32fdac2907dc18a96c9e562fd8d904dda8
|
|
| MD5 |
d7984cae705281c00eaad4b73c692755
|
|
| BLAKE2b-256 |
8ec41bb5dab4de1c69558a09c0b7612dcf718bba1250d9f37a4845899b5f0536
|