Skip to main content

Python SDK

The official Python client for CTWiseAPI.

Installation​

pip install ctwise

Requirements:

  • Python 3.8+
  • requests library (installed automatically)

Quick Start​

from ctwise import CTWiseClient

# Initialize with your API key
client = CTWiseClient(api_key="ctwise_sk_live_xxx")

# Search for regulatory requirements
results = client.requirements.search(
therapeutic_area="Oncology",
authorities=["FDA", "EMA"],
phase="III"
)

# Print results
for req in results.requirements:
print(f"[{req.authority}] {req.title}")
print(f" Type: {req.requirement_type}")
print(f" Reference: {req.regulatory_reference.document}")
print()

Configuration​

Basic Configuration​

from ctwise import CTWiseClient

client = CTWiseClient(
api_key="ctwise_sk_live_xxx",
base_url="https://api.ctwise.ai", # Default
timeout=30, # Request timeout in seconds
max_retries=3, # Number of retries on failure
)

Environment Variables​

export CTWISE_API_KEY="ctwise_sk_live_xxx"
from ctwise import CTWiseClient

# API key is read from CTWISE_API_KEY environment variable
client = CTWiseClient()

API Reference​

Search for regulatory requirements across multiple authorities.

results = client.requirements.search(
# Search criteria
therapeutic_area="Oncology",
requirement_types=["endpoint_definition", "safety_monitoring"],
authorities=["FDA", "ICH", "EMA"],
phase="III",
keywords=["primary endpoint", "overall survival"],

# Options
include_guidance=True,
include_examples=False,
max_results=100,
)

# Access results
print(f"Found {results.total_count} requirements")

for req in results.requirements:
print(req.requirement_id)
print(req.title)
print(req.description)
print(req.implementation_guidance) # Starter tier only

Amendment Patterns​

Get historical amendment patterns for a therapeutic area.

patterns = client.patterns.get(
therapeutic_area="Oncology",
time_period="5_years"
)

for pattern in patterns.patterns:
print(f"Pattern: {pattern.pattern_type}")
print(f"Frequency: {pattern.frequency}%")
print(f"Risk Level: {pattern.risk_level}")

Prevention Strategies​

Get prevention strategies to avoid common protocol issues.

strategies = client.prevention.get_strategies(
therapeutic_area="Oncology",
phase="III"
)

for strategy in strategies.strategies:
print(f"Strategy: {strategy.title}")
print(f"Effectiveness: {strategy.effectiveness}%")
for action in strategy.actions:
print(f" - {action}")

Protocol Validation​

Validate a protocol against regulatory requirements.

validation = client.rules.validate(
protocol={
"title": "Phase III Oncology Study",
"therapeutic_area": "Oncology",
"phase": "III",
"primary_endpoint": "Overall survival",
"sample_size": 500,
}
)

print(f"Compliance Score: {validation.compliance_score}%")
print(f"Status: {validation.status}")

for issue in validation.issues:
print(f"[{issue.severity}] {issue.description}")
print(f" Recommendation: {issue.recommendation}")

Error Handling​

from ctwise import CTWiseClient
from ctwise.exceptions import (
CTWiseError,
AuthenticationError,
RateLimitError,
ValidationError,
NotFoundError,
InsufficientTierError,
)

client = CTWiseClient(api_key="ctwise_sk_live_xxx")

try:
results = client.requirements.search(therapeutic_area="Oncology")
except AuthenticationError:
print("Invalid API key")
except RateLimitError as e:
print(f"Rate limited. Retry after {e.retry_after} seconds")
except InsufficientTierError:
print("This feature requires a higher tier")
except ValidationError as e:
print(f"Invalid request: {e.message}")
except CTWiseError as e:
print(f"API error: {e.message}")

Pagination​

For large result sets, use pagination:

# Get first page
results = client.requirements.search(
therapeutic_area="Oncology",
max_results=50,
)

# Iterate through all pages
all_requirements = []
for page in client.requirements.search_iter(therapeutic_area="Oncology"):
all_requirements.extend(page.requirements)

print(f"Total requirements: {len(all_requirements)}")

Async Support​

For async/await applications:

import asyncio
from ctwise import AsyncCTWiseClient

async def main():
async with AsyncCTWiseClient(api_key="ctwise_sk_live_xxx") as client:
results = await client.requirements.search(
therapeutic_area="Oncology"
)

for req in results.requirements:
print(req.title)

asyncio.run(main())

Type Hints​

The SDK includes full type hints for IDE support:

from ctwise import CTWiseClient
from ctwise.types import RequirementsSearchResponse, Requirement

client = CTWiseClient(api_key="xxx")
results: RequirementsSearchResponse = client.requirements.search(
therapeutic_area="Oncology"
)

req: Requirement = results.requirements[0]
print(req.title) # IDE autocomplete works!

Logging​

Enable debug logging:

import logging

logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger("ctwise")
logger.setLevel(logging.DEBUG)

# Now API calls will log request/response details
client = CTWiseClient(api_key="xxx")

Best Practices​

1. Reuse Client Instances​

# Good - reuse client
client = CTWiseClient(api_key="xxx")
results1 = client.requirements.search(therapeutic_area="Oncology")
results2 = client.requirements.search(therapeutic_area="Psychiatry")

# Bad - create new client each time
results1 = CTWiseClient(api_key="xxx").requirements.search(...)
results2 = CTWiseClient(api_key="xxx").requirements.search(...)

2. Handle Rate Limits​

from ctwise.exceptions import RateLimitError
import time

def search_with_backoff(client, **kwargs):
for attempt in range(3):
try:
return client.requirements.search(**kwargs)
except RateLimitError as e:
time.sleep(e.retry_after)
raise Exception("Max retries exceeded")

3. Use Environment Variables for Keys​

import os
from ctwise import CTWiseClient

# Never hardcode API keys
client = CTWiseClient(api_key=os.getenv("CTWISE_API_KEY"))

Examples​

Integration with Pandas​

import pandas as pd
from ctwise import CTWiseClient

client = CTWiseClient()

results = client.requirements.search(
therapeutic_area="Oncology",
max_results=100
)

# Convert to DataFrame
df = pd.DataFrame([
{
"id": r.requirement_id,
"title": r.title,
"authority": r.authority,
"type": r.requirement_type,
"effective_date": r.effective_date,
}
for r in results.requirements
])

print(df.head())

Flask Integration​

from flask import Flask, jsonify
from ctwise import CTWiseClient

app = Flask(__name__)
client = CTWiseClient()

@app.route("/search")
def search():
results = client.requirements.search(
therapeutic_area="Oncology"
)
return jsonify({
"count": results.total_count,
"requirements": [r.dict() for r in results.requirements]
})

REST API Reference​

If you prefer to call the REST API directly without the SDK, here are the available endpoints:

Public Endpoints (No Authentication Required)​

EndpointDescription
GET /v1/catalog/sourcesList all available regulatory sources

Protected Endpoints (API Key Required)​

EndpointDescription
GET /v1/rulesSearch rules by keyword, source, category
GET /v1/rules/{id}Get specific rule details
POST /v1/semantic-searchAI-powered natural language search
GET /v1/rules/searchAlternative GET-based semantic search

Semantic Search Endpoints​

The SDK's search() method uses these endpoints under the hood:

# SDK method
results = client.rules.search(query="informed consent requirements")

# Equivalent REST API call (POST method - recommended)
import requests
response = requests.post(
"https://api.ctwise.ai/v1/semantic-search",
headers={"x-api-key": "YOUR_API_KEY"},
json={"query": "informed consent requirements", "limit": 10}
)

# Equivalent REST API call (GET method - alternative)
response = requests.get(
"https://api.ctwise.ai/v1/rules/search",
headers={"x-api-key": "YOUR_API_KEY"},
params={"q": "informed consent requirements", "limit": 10}
)

Troubleshooting​

Common Errors​

Error CodeHTTP StatusCauseSolution
INVALID_API_KEY401Invalid or expired API keyVerify your API key is correct and active
MISSING_API_KEY401No API key providedInclude x-api-key header in requests
RATE_LIMIT_EXCEEDED429Too many requestsWait and retry with exponential backoff
INSUFFICIENT_TIER403Feature requires higher tierUpgrade your subscription
INVALID_REQUEST400Malformed request bodyCheck request parameters
NOT_FOUND404Resource doesn't existVerify the rule ID or endpoint path

Debugging Tips​

  1. Enable verbose logging to see request/response details:

    import logging
    logging.basicConfig(level=logging.DEBUG)
  2. Test connectivity with the catalog endpoint (no auth required):

    response = requests.get("https://api.ctwise.ai/v1/catalog/sources")
    print(response.status_code) # Should be 200
  3. Verify API key format: Keys should start with ctw_ prefix

  4. Check tier access: Some sources (ICH, EMA, WHO) require Starter tier or higher

Support​