Documentation

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 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)
  • Alertsemail 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

ComponentRequirement
SDKPython ≥ 3.8, requests ≥ 2.28
HTTP APIAny 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.

Note The SDK sends events asynchronously in a background thread. Your application's performance is not affected. See Initialization for details.

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,
)
ParameterTypeDefaultDescription
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.
Important 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

  1. The API key and endpoint URL are stored internally.
  2. Python's sys.excepthook is replaced with Ravn's handler to capture all unhandled exceptions.
  3. 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()
ParameterTypeDescription
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

LevelUse case
infoInformational events (default)
warningNon-critical issues that may need attention
errorErrors 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

  1. The decorator wraps the function with timing logic.
  2. After the function completes, the execution time is compared against slow_threshold_ms (default: 1000ms).
  3. If the function exceeded the threshold, a warning-level event is sent with metadata including the function name, module, duration, and threshold.
  4. 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:

FieldExample
functionsync_inventory
moduleapp.tasks
duration_ms1523.4
threshold_ms1000

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

EndpointLimit
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

HeaderValue
x-api-keyYour project API key
Content-Typeapplication/json

Request body

FieldTypeRequiredDescription
messagestringYesError message or log text. Max 5000 characters.
levelstringYes"error", "warning", or "info"
metadataobjectNoAdditional context. Max 50 keys, max nesting depth 5.

Metadata conventions

The following metadata keys are recognized by Ravn for enhanced processing:

KeyPurpose
stack_traceFull stacktrace string. Used for error grouping and AI analysis.
error_typeException 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
}
FieldDescription
statusAlways "accepted" on success.
group_idUUID of the error group this event was assigned to.
new_grouptrue 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

ParameterTypeDefaultDescription
levelstring"all"Filter by level: error, warning, info, or all
limitint100Number of events to return (1–200)
offsetint0Pagination offset

GET /api/v1/projects/{project_id}/stats

Get time-series event counts for charting.

Auth: API key or Bearer token

Query parameters

ParameterDefaultDescription
hours24Time 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"
}
Important The API key is only returned once when the project is created. Store it securely. If you lose it, you must rotate the key.

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)

FieldTypeDescription
auto_analyzeboolEnable automatic AI analysis on new errors
alerts_enabledboolEnable email alerts
alert_emailstringEmail address for alerts
alert_min_levelstringMinimum level for alerts: "error" or "warning"
alert_on_regressionboolAlert on resolved errors that reoccur
webhook_urlstringWebhook 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

RolePermissions
ownerFull access. Can rotate keys, delete project, transfer ownership.
adminManage settings, invite/remove members, trigger analysis.
memberView data, resolve/ignore errors, trigger analysis.
viewerRead-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

  1. When a new error group is created (or manually triggered), Ravn sends the error message and stacktrace to its AI engine.
  2. The AI produces a structured analysis: root cause, explanation, and up to 3 concrete fix steps.
  3. The result is validated for quality — vague or generic responses are automatically rejected and regenerated.
  4. 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:

SettingDescription
alerts_enabledMaster toggle for email alerts
alert_emailRecipient email address
alert_min_levelMinimum severity: "error" or "warning"
alert_on_regressionAlert 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_regression is 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:

PatternReplaced with
IPv4 addresses<ip>
UUIDs<uuid>
Hex hashes (≥8 chars)<hex>
File paths<path>
Floats<num>
Integers<num>

Group statuses

StatusMeaning
activeDefault. New events are accepted.
resolvedMarked as fixed. If new events arrive, status changes to regression.
ignoredNew 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_url points to the correct ingest endpoint.
  • The SDK silently catches its own errors. Run with PYTHONUNBUFFERED=1 and 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-key header. 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:

  • message exceeds 5000 characters
  • metadata has more than 50 top-level keys
  • metadata nesting 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)
Response code A successful ingest returns HTTP 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.
Prefer the SDK The Python SDK handles all of this automatically, including background delivery, automatic traceback capture, and system metadata. Use raw HTTP only if you can't use the SDK. See Quickstart.

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-sdk

Add 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

  1. The exception is sent to Ravn in a background thread (no impact on your app's response time)
  2. Ravn groups it by stacktrace fingerprint — the same bug from the same code path is counted, not duplicated
  3. If it's a new error group, you receive an email alert
  4. 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
Next steps See Quickstart for a full working example, or Exception Capture for the full API reference.