Skip to main content

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}")
ExceptionStatusWhen
NopeAuthError401Invalid or missing API key
NopeValidationError400Invalid request format
NopeRateLimitError429Rate limit exceeded (retry_after in seconds)
NopeServerError5xxServer-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:

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

See Also