Python SDK
Official Python SDK for the NOPE API. Supports both sync and async clients. Built with Pydantic for type safety.
Package: nope-net on PyPI | Requires: Python 3.8+
Installation
pip install nope-net Client Initialization
from nope_net import NopeClient, AsyncNopeClient
# Synchronous client
client = NopeClient(
api_key="nope_live_...",
timeout=30.0, # Optional: request timeout in seconds (default: 30.0)
base_url="https://api.nope.net", # Optional: custom API URL
)
# Demo mode (no API key required, uses /v1/try/* endpoints)
demo_client = NopeClient(
api_key=None,
demo=True,
timeout=30.0,
)
# Async client
async_client = AsyncNopeClient(api_key="nope_live_...") Context Manager (Recommended)
Use context managers for automatic connection cleanup:
# Recommended: use as context manager for automatic cleanup
with NopeClient(api_key="nope_live_...") as client:
result = client.evaluate(
messages=[{"role": "user", "content": "Hello"}]
)
# Async context manager
async with AsyncNopeClient(api_key="nope_live_...") as client:
result = await client.evaluate(
messages=[{"role": "user", "content": "Hello"}]
) Methods
evaluate()
Full risk assessment across all 9 risk types with clinical features, protective factors, and crisis resources. See Evaluate Guide for response semantics.
from nope_net import NopeClient
client = NopeClient(api_key="nope_live_...")
# With messages array
result = client.evaluate(
messages=[
{"role": "user", "content": "I've been feeling really down lately"},
{"role": "assistant", "content": "I'm sorry to hear that. Can you tell me more?"},
{"role": "user", "content": "I just feel hopeless, like nothing will get better"},
],
config={"user_country": "US"},
)
print(result.summary.speaker_severity) # 'none' | 'mild' | 'moderate' | 'high' | 'critical'
print(result.summary.speaker_imminence) # 'not_applicable' | 'chronic' | 'subacute' | 'urgent' | 'emergency'
# With plain text
text_result = client.evaluate(
text="Patient expressed feelings of hopelessness during session.",
config={"user_country": "US"},
) screen()
Lightweight crisis triage. Returns severity, imminence, and matched resources for all 9 risk types. See Screen Guide for response semantics.
result = client.screen(
messages=[{"role": "user", "content": "I don't want to be here anymore"}],
config={"country": "US"},
)
if result.show_resources:
print(f"Crisis detected: {result.rationale}")
print(f"Primary resource: {result.resources.primary.name}")
print(f"Call: {result.resources.primary.phone}")
# Access expanded risks array (all 9 risk types)
for risk in result.risks:
print(f"{risk.type}: {risk.severity} (subject: {risk.subject})") oversight_analyze()
Analyze AI conversations for harmful behaviors (87 behavior types across 13 categories). See Oversight Guide and AI Behavior Taxonomy.
result = client.oversight_analyze(
conversation={
"conversation_id": "conv_123",
"messages": [
{"role": "user", "content": "I've been feeling really lonely lately"},
{"role": "assistant", "content": "I understand. I'm always here for you."},
{"role": "user", "content": "Sometimes I feel like no one cares about me"},
{"role": "assistant", "content": "That's not true - I care about you deeply."},
],
"metadata": {
"user_is_minor": False,
"platform": "my-app",
},
},
)
print(f"Concern level: {result.result.overall_concern}") # 'none' | 'low' | 'medium' | 'high' | 'critical'
print(f"Trajectory: {result.result.trajectory}") # 'improving' | 'stable' | 'worsening'
for behavior in result.result.detected_behaviors:
print(f" {behavior.code}: {behavior.severity}") resources(), resources_smart(), resources_countries(), detect_country()
Crisis resource lookup and AI-ranked recommendations. See Resources Guide and Service Taxonomy.
# Get crisis resources by country
resources = client.resources(
country="US",
config={"scopes": ["suicide", "crisis"], "urgent": True},
)
print(f"Found {resources.count} resources")
for resource in resources.resources:
print(f" {resource.name}: {resource.phone}")
# AI-ranked resources (requires demo mode or auth)
ranked = client.resources_smart(
country="US",
query="teen struggling with eating disorder",
)
for item in ranked.ranked:
print(f"{item.rank}. {item.resource.name}")
print(f" Why: {item.why}")
# List supported countries
countries = client.resources_countries()
print(f"Supported: {', '.join(countries.countries)}")
# Detect user's country from request headers
detected = client.detect_country()
print(f"Detected country: {detected.country_code}") Async Client
Use AsyncNopeClient for async/await support:
import asyncio
from nope_net import AsyncNopeClient
async def analyze_message():
async with AsyncNopeClient(api_key="nope_live_...") as client:
result = await client.evaluate(
messages=[{"role": "user", "content": "I feel hopeless"}],
config={"user_country": "US"},
)
return result
# Run async
result = asyncio.run(analyze_message())
print(result.summary.speaker_severity) Error Handling
All errors extend the base NopeError class with status_code and message properties.
from nope_net import (
NopeClient,
NopeAuthError,
NopeValidationError,
NopeRateLimitError,
NopeServerError,
NopeConnectionError,
)
client = NopeClient(api_key="nope_live_...")
try:
result = client.evaluate(
messages=[{"role": "user", "content": "Hello"}]
)
except NopeAuthError as e:
# 401: Invalid or missing API key
print(f"Auth failed: {e}")
except NopeValidationError as e:
# 400: Invalid request (missing fields, bad format)
print(f"Validation error: {e}")
except NopeRateLimitError as e:
# 429: Rate limit exceeded
print(f"Rate limited, retry after: {e.retry_after} seconds")
except NopeServerError as e:
# 5xx: Server error
print(f"Server error: {e.status_code}")
except NopeConnectionError as e:
# Network error (timeout, DNS, etc.)
print(f"Connection failed: {e}") | Exception | Status | When |
|---|---|---|
NopeAuthError | 401 | Invalid or missing API key |
NopeValidationError | 400 | Invalid request format |
NopeRateLimitError | 429 | Rate limit exceeded (retry_after in seconds) |
NopeServerError | 5xx | Server-side error |
NopeConnectionError | — | Network failure (timeout, DNS) |
Webhook Verification
Verify webhook signatures to ensure requests are from NOPE. See Webhooks Guide for setup.
from nope_net import Webhook, WebhookSignatureError
webhook = Webhook("whsec_your_webhook_secret")
# In your webhook handler (Flask, FastAPI, etc.)
@app.post("/webhook")
def handle_webhook(request):
signature = request.headers.get("x-nope-signature")
timestamp = request.headers.get("x-nope-timestamp")
body = request.get_data(as_text=True) # raw body string
try:
payload = webhook.verify(
body,
signature,
timestamp,
tolerance=300, # Optional: max age in seconds (default: 300)
)
# payload is typed as WebhookPayload
if payload.event == "evaluate.alert":
print(f"Risk alert: {payload.risk_summary.speaker_severity}")
elif payload.event == "oversight.alert":
print(f"AI concern: {payload.overall_concern}")
return "OK", 200
except WebhookSignatureError:
print("Invalid signature")
return "Invalid signature", 401 Types
All response types are Pydantic models with full validation:
from nope_net import (
# Response types
EvaluateResponse,
ScreenResponse,
OversightAnalyzeResponse,
ResourcesResponse,
# Core types
Risk,
Summary,
CrisisResource,
# Screen types
ScreenRisk,
ScreenCrisisResources,
# Oversight types
OversightAnalysisResult,
DetectedBehavior,
# Webhook types
WebhookPayload,
)
# All types are Pydantic models with full validation
# Use extra="allow" for forward compatibility with new API fields For type semantics (severity levels, risk types, etc.), see:
- User Risk Taxonomy — 9 risk types, severity/imminence scales
- AI Behavior Taxonomy — 87 Oversight behaviors
- Service Taxonomy — Resource scopes and populations
Utility Functions
Helper functions for working with risk assessments:
from nope_net import (
calculate_speaker_severity,
calculate_speaker_imminence,
has_third_party_risk,
SEVERITY_SCORES,
IMMINENCE_SCORES,
)
# Calculate aggregate severity from risks array
severity = calculate_speaker_severity(result.risks)
# Check if any third-party risk exists
has_third_party = has_third_party_risk(result.risks)
# Severity/imminence as numeric scores for comparison
print(SEVERITY_SCORES["critical"]) # 4
print(IMMINENCE_SCORES["emergency"]) # 4