Welcome to Ravn Docs
Everything you need to integrate, configure, and get the most out of Ravn — lightweight error monitoring and performance tracking for Python.
ravn.init() and SDK setup parameters.Ravn Documentation
Lightweight error monitoring and performance tracking for Python applications.
Ravn captures exceptions, logs, and slow function calls from your application and surfaces them in a clean dashboard with AI-powered root cause analysis.
What Ravn does
- Automatic exception capture — unhandled exceptions are reported the moment they happen
- Manual capture — send specific exceptions or log messages with custom metadata
- Performance monitoring — track function execution time and flag slow operations via @ravn.measure
- Error grouping — similar errors are grouped by stacktrace fingerprint
- AI root cause analysis — get a concrete diagnosis with actionable fix steps (learn more)
- Alerts — email notifications when new errors appear
- Team collaboration — invite members with role-based access control
Two ways to integrate
You can use Ravn via the Python SDK (recommended) or by sending events directly to the HTTP API. Both methods use the same ingest endpoint and produce the same results in the dashboard.
Requirements
| Component | Requirement |
|---|---|
| SDK | Python ≥ 3.8, requests ≥ 2.28 |
| HTTP API | Any language that can send HTTP POST requests |
Quickstart
Get from zero to error monitoring in under a minute.
1. Install the SDK
pip install ravn-sdk
2. Initialize
Add this at the entry point of your application, before any other code runs:
import ravn
ravn.init(api_key="sk_live_your_api_key")
You can find your API key in the Ravn dashboard under Project Settings. The key is shown once when you create a project and cannot be retrieved afterwards. See Projects API to learn how to rotate keys.
3. Done
Every unhandled exception in your application is now automatically captured and sent to Ravn. Open your dashboard to see incoming errors.
Verify the integration
Trigger a test error to confirm everything works:
import ravn
ravn.init(api_key="sk_live_your_api_key")
raise Exception("Hello from Ravn!")
Check the dashboard — you should see the error appear within a few seconds.
Configuration
All configuration options for ravn.init().
ravn.init(
api_key="sk_live_your_api_key",
api_url="https://app.getravn.com/api/v1/ingest",
slow_threshold_ms=1000,
)
| Parameter | Type | Default | Description |
|---|---|---|---|
api_key |
str |
— | Your project API key (required). Starts with sk_live_. |
api_url |
str |
https://app.getravn.com/api/v1/ingest |
Override the ingest endpoint. Useful for testing or pointing to a custom backend. |
slow_threshold_ms |
int |
1000 |
Threshold in milliseconds for the @ravn.measure decorator. Functions exceeding this duration trigger a warning event. |
ravn.init() must be called before any other Ravn function. Calling capture_exception() or capture_message() without initialization will fail silently.
Initialization
How ravn.init() configures the SDK.
Signature
ravn.init(
api_key: str,
api_url: Optional[str] = None,
slow_threshold_ms: int = 1000,
) -> None
What happens on init
- The API key and endpoint URL are stored internally.
- Python's
sys.excepthookis replaced with Ravn's handler to capture all unhandled exceptions. - A single-worker background thread pool is initialized for non-blocking event delivery.
Authentication
The API key is sent as an x-api-key HTTP header with every request. It is hashed server-side and matched against the stored project key hash. Keys are never stored in plaintext on the server. See API authentication for details.
Example: Custom endpoint
ravn.init(
api_key="sk_live_abc123",
api_url="https://ravn.yourcompany.com/api/v1/ingest",
)
Exception Capture
Capture handled and unhandled exceptions.
Automatic capture
After calling ravn.init(), all unhandled exceptions are automatically captured via sys.excepthook. No additional code is needed.
import ravn
ravn.init(api_key="sk_live_abc123")
# This exception is captured automatically:
result = 1 / 0 # ZeroDivisionError
Manual capture
ravn.capture_exception(
exc: Exception,
metadata: Optional[dict] = None,
) -> None
Use capture_exception() when you handle an exception yourself but still want it reported. For automatic capture see Initialization. The error grouping system uses the stacktrace to cluster similar events together.
try:
process_payment(order)
except PaymentError as e:
ravn.capture_exception(e, metadata={
"order_id": order.id,
"amount": order.total,
})
# Handle the error gracefully
show_error_to_user()
| Parameter | Type | Description |
|---|---|---|
exc |
Exception |
The exception object to capture. |
metadata |
dict | None |
Additional context sent with the event. Displayed in the dashboard. |
What gets sent
The SDK automatically includes:
- message — the exception's string representation
- level —
"error" - stack_trace — full Python traceback
- mechanism —
"unhandled_exception"or"manual" - hostname, platform, python_version, sdk_version
Log Messages
Send arbitrary log messages to Ravn.
Signature
ravn.capture_message(
message: str,
level: str = "info",
metadata: Optional[dict] = None,
) -> None
API Overview
The API Overview page explains authentication and rate limits. For sending events directly without the SDK, see Using Without SDK.
Supported levels
| Level | Use case |
|---|---|
info | Informational events (default) |
warning | Non-critical issues that may need attention |
error | Errors without an exception object |
Examples
# Track a business event
ravn.capture_message(
"Payment processed",
level="info",
metadata={"amount": 99.99, "currency": "EUR"},
)
# Warn about a deprecation
ravn.capture_message(
"Legacy API v1 endpoint called",
level="warning",
metadata={"endpoint": "/api/v1/users"},
)
# Report an error condition without an exception
ravn.capture_message(
"Redis connection pool exhausted",
level="error",
)
Performance Monitoring
Track slow functions with @ravn.measure.
Usage
import ravn
ravn.init(api_key="sk_live_abc123")
@ravn.measure
def sync_inventory():
# If this takes longer than slow_threshold_ms,
# a warning event is sent to Ravn.
fetch_from_warehouse_api()
update_local_database()
How it works
- The decorator wraps the function with timing logic.
- After the function completes, the execution time is compared against
slow_threshold_ms(default: 1000ms). - If the function exceeded the threshold, a
warning-level event is sent with metadata including the function name, module, duration, and threshold. - The decorated function's return value and exceptions are not affected.
Customizing the threshold
ravn.init(
api_key="sk_live_abc123",
slow_threshold_ms=500, # warn if any measured function takes > 500ms
)
When a slow function is detected, a warning-level event is sent. These events appear in your dashboard alongside regular errors. See Events for how to query them via API.
Event metadata
When a slow function is detected, the following metadata is sent:
| Field | Example |
|---|---|
function | sync_inventory |
module | app.tasks |
duration_ms | 1523.4 |
threshold_ms | 1000 |
API Overview
REST API reference for Ravn.
Base URL
https://app.getravn.com/api/v1
Authentication
The API supports two authentication methods:
API Key (for ingest and read endpoints)
Send your project API key in the x-api-key header:
curl -X POST https://app.getravn.com/api/v1/ingest \
-H "x-api-key: sk_live_your_api_key" \
-H "Content-Type: application/json" \
-d '{"message": "test", "level": "error"}'
Bearer Token (for dashboard endpoints)
Authenticate via /auth/token and use the JWT in the Authorization header:
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
Rate Limits
| Endpoint | Limit |
|---|---|
Ingest (POST /ingest) | 60 requests/minute per API key |
Auth (POST /auth/token) | 10 requests/minute |
Registration (POST /auth/register) | 5 requests/minute |
Response format
All responses are JSON. Errors follow this structure:
{
"detail": "Error description"
}
Ingest API
Send error events and log messages to Ravn.
POST /api/v1/ingest
Headers
| Header | Value |
|---|---|
x-api-key | Your project API key |
Content-Type | application/json |
Request body
| Field | Type | Required | Description |
|---|---|---|---|
message | string | Yes | Error message or log text. Max 5000 characters. |
level | string | Yes | "error", "warning", or "info" |
metadata | object | No | Additional context. Max 50 keys, max nesting depth 5. |
Metadata conventions
The following metadata keys are recognized by Ravn for enhanced processing:
| Key | Purpose |
|---|---|
stack_trace | Full stacktrace string. Used for error grouping and AI analysis. |
error_type | Exception class name (e.g. ValueError). Used by AI analysis. |
Example request
curl -X POST https://app.getravn.com/api/v1/ingest \
-H "x-api-key: sk_live_abc123" \
-H "Content-Type: application/json" \
-d '{
"message": "KeyError: user_id",
"level": "error",
"metadata": {
"stack_trace": "Traceback (most recent call last):\n File \"app.py\", line 42, in handle_request\n user = data[\"user_id\"]\nKeyError: \"user_id\"",
"error_type": "KeyError",
"request_id": "req_abc123"
}
}'
Response
{
"status": "accepted",
"group_id": "550e8400-e29b-41d4-a716-446655440000",
"new_group": true
}
| Field | Description |
|---|---|
status | Always "accepted" on success. |
group_id | UUID of the error group this event was assigned to. |
new_group | true if a new group was created, false if the event was added to an existing group. |
Error Groups
List, view, and manage error groups.
GET /api/v1/projects/{project_id}/groups
List all error groups for a project, sorted by most recent occurrence.
Auth: API key or Bearer token (viewer role or above)
Response
[
{
"id": "550e8400-e29b-...",
"title": "KeyError: user_id",
"level": "error",
"status": "active",
"occurrences": 42,
"first_seen": "2026-03-28T10:00:00",
"last_seen": "2026-03-31T14:30:00",
"ai_analysis": "...",
"solution_suggestion": "..."
}
]
GET /api/v1/groups/{group_id}
Get a single group with its 50 most recent events.
Auth: API key or Bearer token
PATCH /api/v1/groups/{group_id}
Update a group's status.
Auth: Bearer token (member role or above)
Request body
{
"status": "resolved" // "active", "resolved", or "ignored"
}
POST /api/v1/groups/{group_id}/analyze
Trigger AI analysis for a specific error group. Counts against your monthly AI budget.
Auth: Bearer token (member role or above)
Response
{
"status": "queued",
"calls_used": 26,
"budget": 100
}
Events
Query raw event logs and statistics.
GET /api/v1/projects/{project_id}/events
List raw events for a project.
Auth: API key or Bearer token
Query parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
level | string | "all" | Filter by level: error, warning, info, or all |
limit | int | 100 | Number of events to return (1–200) |
offset | int | 0 | Pagination offset |
GET /api/v1/projects/{project_id}/stats
Get time-series event counts for charting.
Auth: API key or Bearer token
Query parameters
| Parameter | Default | Description |
|---|---|---|
hours | 24 | Time window in hours |
Bucketing is automatic: minute-level for ≤2h, hour-level for ≤48h, day-level for >48h.
GET /api/v1/projects/{project_id}/usage
Get AI budget usage and event breakdown.
Response
{
"ai_budget_monthly": 100,
"ai_calls_this_month": 25,
"analyzed_groups": 10,
"event_by_level": { "error": 150, "warning": 50 },
"daily_events": [
{ "day": "2026-03-31", "events": 42 }
]
}
Projects
Create and manage projects.
GET /api/v1/projects
List all projects for the authenticated user.
Auth: Bearer token
POST /api/v1/projects
Create a new project.
Auth: Bearer token
Request body
{
"name": "My App"
}
Response
{
"project": { /* project object */ },
"new_key": "sk_live_xxxxxxxxxxxxxx"
}
PATCH /api/v1/projects/{project_id}
Update project settings. See also Alerts configuration and AI Analysis.
Auth: Bearer token (admin role or above)
Request body (all fields optional)
| Field | Type | Description |
|---|---|---|
auto_analyze | bool | Enable automatic AI analysis on new errors |
alerts_enabled | bool | Enable email alerts |
alert_email | string | Email address for alerts |
alert_min_level | string | Minimum level for alerts: "error" or "warning" |
alert_on_regression | bool | Alert on resolved errors that reoccur |
webhook_url | string | Webhook URL for notifications |
POST /api/v1/projects/{project_id}/rotate-key
Rotate the API key. The old key is invalidated immediately.
Auth: Bearer token (owner only)
Team Members
Invite, manage, and remove team members.
Roles
| Role | Permissions |
|---|---|
owner | Full access. Can rotate keys, delete project, transfer ownership. |
admin | Manage settings, invite/remove members, trigger analysis. |
member | View data, resolve/ignore errors, trigger analysis. |
viewer | Read-only access to all project data. |
GET /api/v1/projects/{project_id}/members
List active members and pending invitations.
POST /api/v1/projects/{project_id}/members/invite
Send a team invitation. Invitations expire after 7 days.
Auth: Bearer token (admin role or above)
Request body
{
"email": "colleague@company.com",
"role": "member"
}
PATCH /api/v1/projects/{project_id}/members/{member_id}
Change a member's role.
DELETE /api/v1/projects/{project_id}/members/{member_id}
Remove a member from the project.
POST /api/v1/invites/accept
Accept a pending invitation.
{
"token": "invite_token_here"
}
AI Analysis
Automatic root cause analysis for your errors.
How it works
- When a new error group is created (or manually triggered), Ravn sends the error message and stacktrace to its AI engine.
- The AI produces a structured analysis: root cause, explanation, and up to 3 concrete fix steps.
- The result is validated for quality — vague or generic responses are automatically rejected and regenerated.
- For common error patterns (e.g.
KeyError,TypeError,IndexError), Ravn uses deterministic templates instead of AI for instant, reliable results. See Common Errors for a list of frequently encountered patterns.
Enabling automatic analysis
Toggle Auto-Analyze in your project settings, or via the API:
curl -X PATCH https://app.getravn.com/api/v1/projects/{id} \
-H "Authorization: Bearer {token}" \
-H "Content-Type: application/json" \
-d '{"auto_analyze": true}'
Manual analysis
Trigger analysis for a specific error group from the dashboard or via the API:
curl -X POST https://app.getravn.com/api/v1/groups/{group_id}/analyze \
-H "Authorization: Bearer {token}"
AI budget
Each account has a monthly AI analysis budget. The default is 100 analyses per month. Usage is tracked at the account level and shared across all projects.
Analysis output format
Each analysis contains:
- Root Cause — 1–2 sentences identifying the concrete cause
- Why it happens — brief technical explanation
- Fix — up to 3 actionable steps (no generic advice)
- Hint — optional, only if genuinely useful
Alerts
Get notified when things break.
Configuring alerts
Enable alerts in your project settings:
| Setting | Description |
|---|---|
alerts_enabled | Master toggle for email alerts |
alert_email | Recipient email address |
alert_min_level | Minimum severity: "error" or "warning" |
alert_on_regression | Alert when a resolved error reoccurs |
When alerts are sent
- New error group — the first time an error with a new fingerprint appears
- Regression — a previously resolved error group receives new events (if
alert_on_regressionis enabled)
Webhooks
Set a webhook_url in project settings to receive POST notifications with the error group payload whenever an alert is triggered. You can test it with:
curl -X POST https://app.getravn.com/api/v1/projects/{id}/test-notify \
-H "Authorization: Bearer {token}"
Error Grouping
How Ravn groups similar errors together.
Fingerprinting
Every incoming event is assigned a fingerprint that determines which error group it belongs to. Events with the same fingerprint are grouped together.
With stacktrace
If the event includes a stack_trace in metadata (sent automatically by the Python SDK), the stacktrace is normalized and hashed. This means the same error thrown from the same code path is always grouped together, even if variable values (IDs, paths, timestamps) differ.
Without stacktrace
If there is no stacktrace, the first 500 characters of the message are normalized and hashed.
Normalization rules
Before hashing, the following values are replaced with placeholders:
| Pattern | Replaced with |
|---|---|
| IPv4 addresses | <ip> |
| UUIDs | <uuid> |
| Hex hashes (≥8 chars) | <hex> |
| File paths | <path> |
| Floats | <num> |
| Integers | <num> |
Group statuses
| Status | Meaning |
|---|---|
active | Default. New events are accepted. |
resolved | Marked as fixed. If new events arrive, status changes to regression. |
ignored | New events are still recorded but the group is hidden from default views. |
Common Errors
Troubleshooting common issues with the SDK and API.
SDK errors
Events not appearing in the dashboard
- Confirm
ravn.init()is called before the error occurs. - Verify the API key is correct — copy it directly from project settings.
- If using a self-hosted instance, confirm
api_urlpoints to the correct ingest endpoint. - The SDK silently catches its own errors. Run with
PYTHONUNBUFFERED=1and watch stdout for any SDK log output.
requests.exceptions.ConnectionError
The SDK cannot reach the Ravn server. Check network connectivity and firewall rules. If self-hosted, ensure the backend is running.
@ravn.measure not reporting slow functions
- The function must actually exceed
slow_threshold_ms(default: 1000ms). - Make sure
ravn.init()was called before the decorated function runs. - Lower the threshold for testing:
ravn.init(api_key="...", slow_threshold_ms=100)
API errors
401 Unauthorized
- Ingest endpoint: Invalid or missing
x-api-keyheader. See Authentication. - Dashboard endpoints: Expired or missing Bearer token. Refresh via
POST /auth/refresh.
403 Forbidden
Your role on the project is insufficient for the requested action. See the Roles table for required permissions.
422 Unprocessable Entity
The request body failed validation. Common causes:
messageexceeds 5000 charactersmetadatahas more than 50 top-level keysmetadatanesting exceeds depth 5
429 Too Many Requests
Rate limit exceeded. Wait and retry. The ingest endpoint allows 60 requests/minute per API key.
AI budget exhausted
The monthly AI analysis limit has been reached. The budget resets on the first day of each month. You can see current usage via GET /projects/{id}/usage.
Using Ravn Without the SDK
Send events directly via HTTP from any language.
If you're not using Python, or prefer not to install the SDK, you can post events directly to the ingest endpoint using any HTTP client. The only requirement is an x-api-key header and a JSON body.
See the Ingest API reference for the full request schema.
Pattern
The recommended pattern is to wrap your code in a try/except block, capture the traceback, build the payload, and send it. Ravn should never cause your application to crash, so always wrap the send call in its own try/except.
Python (without the SDK)
import requests
import traceback
RAVN_URL = "https://app.getravn.com/api/v1/ingest"
API_KEY = "sk_live_your_api_key"
def report_to_ravn(exc):
try:
requests.post(
RAVN_URL,
json={
"message": str(exc),
"level": "error",
"metadata": {
"stack": traceback.format_exc(),
"module": __name__,
},
},
headers={"x-api-key": API_KEY},
timeout=5,
)
except Exception:
pass # never let monitoring crash the app
try:
result = 200 / 0
except Exception as e:
report_to_ravn(e)202 Accepted. Any other status code means the event was rejected — check the response body for details.
cURL
curl -X POST https://app.getravn.com/api/v1/ingest \
-H "x-api-key: sk_live_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"message": "division by zero",
"level": "error",
"metadata": {
"stack": "Traceback ...",
"module": "myapp"
}
}'JavaScript (Fetch)
async function reportToRavn(error) {
try {
await fetch("https://app.getravn.com/api/v1/ingest", {
method: "POST",
headers: {
"Content-Type": "application/json",
"x-api-key": "sk_live_your_api_key",
},
body: JSON.stringify({
message: error.message,
level: "error",
metadata: { stack: error.stack },
}),
});
} catch (_) {} // never let monitoring crash the app
}What to include in metadata
stack— The full traceback string. Used for error grouping and AI analysis.module— The file or module name where the error occurred.function— The function name (optional but useful).- Any additional context: user IDs, request IDs, environment flags.
How to Monitor Python Exceptions
Capture, group, and understand every exception your Python application throws.
Python exceptions are how your application tells you something went wrong. The built-in traceback is useful locally, but in production you need to capture exceptions automatically, store them somewhere persistent, and get notified when they happen. This guide shows you how to do that with Ravn.
Why not just log exceptions?
Logging with logging.exception() or writing to a file works up to a point. The problems start at scale:
- Logs don't deduplicate — the same bug creates thousands of log lines
- Logs don't alert you — you have to check them manually
- Logs don't tell you why something broke — just that it did
- Logs across multiple processes or containers are hard to correlate
Error monitoring solves all of these. Ravn captures exceptions automatically, groups identical errors together, and provides an AI-generated explanation for each one.
Setup: two lines
Install the SDK:
pip install ravn-sdkAdd this at the top of your entry point (before any other imports that might raise):
import ravn
ravn.init(api_key="sk_live_your_api_key")That's it. Every unhandled exception in your process is now captured automatically. The SDK installs a global exception hook — no try/except wrappers needed.
Capturing handled exceptions
For exceptions you catch yourself but still want to track:
try:
result = process_payment(order)
except PaymentError as e:
ravn.capture_exception(e, metadata={"order_id": order.id})
# handle gracefully...The metadata dict accepts any JSON-serializable values. User IDs, request IDs, environment names — anything that helps you reproduce the issue later.
What happens to captured exceptions
- The exception is sent to Ravn in a background thread (no impact on your app's response time)
- Ravn groups it by stacktrace fingerprint — the same bug from the same code path is counted, not duplicated
- If it's a new error group, you receive an email alert
- An AI explanation is generated: what broke, why, and how to fix it
What gets captured automatically
When an exception is captured (manually or automatically), Ravn records:
- Exception type and message
- Full stack trace
- Python version, OS, hostname
- Timestamp and severity
- Any custom metadata you attach