DEV Community

Alex Spinov
Alex Spinov

Posted on

Sentry Has a Free API: Here's How to Use It for Error Tracking Automation

Sentry's API goes far beyond just reporting errors. You can manage projects, query issues, automate triage, track releases, and build custom error monitoring dashboards — all via REST.

Why Use the Sentry API?

  • Automate issue triage and assignment
  • Track error trends across releases
  • Build custom alerting beyond Sentry's defaults
  • Integrate error data into your incident management

Getting Started

Get an auth token from sentry.io > Settings > Auth Tokens:

export SENTRY_TOKEN="your-auth-token"
export SENTRY_ORG="your-org"

# List projects
curl -s -H "Authorization: Bearer $SENTRY_TOKEN" \
  "https://sentry.io/api/0/projects/" | jq '.[] | {slug: .slug, platform: .platform, dateCreated: .dateCreated}'

# Get recent issues
curl -s -H "Authorization: Bearer $SENTRY_TOKEN" \
  "https://sentry.io/api/0/projects/$SENTRY_ORG/my-project/issues/?query=is:unresolved&sort=date" \
  | jq '.[] | {title: .title, count: .count, firstSeen: .firstSeen, level: .level}'
Enter fullscreen mode Exit fullscreen mode

Python Client

import requests

class SentryClient:
    def __init__(self, token, org):
        self.url = "https://sentry.io/api/0"
        self.headers = {'Authorization': f'Bearer {token}'}
        self.org = org

    def get_projects(self):
        resp = requests.get(f"{self.url}/organizations/{self.org}/projects/", headers=self.headers)
        return resp.json()

    def get_issues(self, project, query='is:unresolved', sort='date', limit=25):
        resp = requests.get(
            f"{self.url}/projects/{self.org}/{project}/issues/",
            params={'query': query, 'sort': sort, 'limit': limit},
            headers=self.headers
        )
        return resp.json()

    def get_issue_events(self, issue_id, limit=10):
        resp = requests.get(
            f"{self.url}/issues/{issue_id}/events/",
            params={'limit': limit},
            headers=self.headers
        )
        return resp.json()

    def resolve_issue(self, issue_id):
        resp = requests.put(
            f"{self.url}/issues/{issue_id}/",
            json={'status': 'resolved'},
            headers=self.headers
        )
        return resp.json()

    def assign_issue(self, issue_id, email):
        resp = requests.put(
            f"{self.url}/issues/{issue_id}/",
            json={'assignedTo': email},
            headers=self.headers
        )
        return resp.json()

    def create_release(self, project, version, commits=None):
        payload = {'version': version, 'projects': [project]}
        if commits:
            payload['commits'] = commits
        resp = requests.post(
            f"{self.url}/organizations/{self.org}/releases/",
            json=payload,
            headers=self.headers
        )
        return resp.json()

# Usage
sentry = SentryClient('your-token', 'your-org')

# Check unresolved issues
issues = sentry.get_issues('my-project', query='is:unresolved level:error')
for issue in issues:
    print(f"[{issue['level']:7s}] {issue['title'][:60]:60s} ({issue['count']} events)")
Enter fullscreen mode Exit fullscreen mode

Automated Issue Triage

def auto_triage(sentry, project):
    issues = sentry.get_issues(project, query='is:unresolved !has:assignee', sort='priority')

    routing_rules = {
        'database': 'backend-team@company.com',
        'timeout': 'infra-team@company.com',
        'auth': 'security-team@company.com',
        'payment': 'payments-team@company.com',
        'render': 'frontend-team@company.com',
        'api': 'api-team@company.com'
    }

    for issue in issues:
        title_lower = issue['title'].lower()
        assigned = False

        for keyword, assignee in routing_rules.items():
            if keyword in title_lower:
                sentry.assign_issue(issue['id'], assignee)
                print(f"Assigned '{issue['title'][:50]}' to {assignee}")
                assigned = True
                break

        if not assigned:
            # High-count errors go to on-call
            if issue['count'] > 100:
                sentry.assign_issue(issue['id'], 'oncall@company.com')
                print(f"HIGH PRIORITY: '{issue['title'][:50]}' -> oncall")

auto_triage(sentry, 'my-project')
Enter fullscreen mode Exit fullscreen mode

Error Rate Monitoring

def error_rate_report(sentry, project, hours=24):
    from datetime import datetime, timedelta

    since = (datetime.utcnow() - timedelta(hours=hours)).isoformat() + 'Z'

    # Get issues sorted by frequency
    issues = sentry.get_issues(project, query=f'is:unresolved firstSeen:>{since}', sort='freq')

    total_events = sum(int(i.get('count', 0)) for i in issues)

    print(f"=== Error Report (last {hours}h) ===")
    print(f"New issues: {len(issues)}")
    print(f"Total events: {total_events}")
    print()

    if issues:
        print("Top 10 errors:")
        for i, issue in enumerate(issues[:10], 1):
            print(f"  {i:2d}. [{issue['level']:5s}] {issue['title'][:55]:55s} x{issue['count']}")

error_rate_report(sentry, 'my-project')
Enter fullscreen mode Exit fullscreen mode

Release Tracking

def deploy_with_sentry(sentry, project, version, commits):
    # Create release
    release = sentry.create_release(project, version, commits=[
        {'id': c['sha'], 'message': c['message']} for c in commits
    ])
    print(f"Release {version} created")

    # Mark as deployed
    resp = requests.post(
        f"{sentry.url}/organizations/{sentry.org}/releases/{version}/deploys/",
        json={'environment': 'production', 'name': f'Deploy {version}'},
        headers=sentry.headers
    )
    print(f"Marked as deployed to production")

    return release

# After deployment, monitor for regressions
def check_regression(sentry, project, version, threshold_minutes=30):
    import time
    time.sleep(threshold_minutes * 60)

    issues = sentry.get_issues(project, query=f'is:unresolved release:{version}')

    if len(issues) > 5:
        print(f"REGRESSION DETECTED: {len(issues)} new issues in {version}!")
        for issue in issues[:5]:
            print(f"  - {issue['title']} (x{issue['count']})")
        return True

    print(f"Release {version} looks healthy ({len(issues)} issues)")
    return False
Enter fullscreen mode Exit fullscreen mode

Real-World Use Case

A SaaS company with 50+ microservices built an automated incident pipeline with Sentry API. When a new error appears with 100+ events in 5 minutes, the system: (1) creates a PagerDuty incident, (2) assigns the issue to the right team based on error type, (3) collects recent deployment info, and (4) posts a summary to Slack with stack trace and affected users. Mean time to awareness dropped from 15 minutes to 30 seconds.

What You Can Build

  • Auto-triage system routing errors to the right team
  • Release quality gate blocking deploys with high error rates
  • Error budget tracker for SLO compliance
  • Incident reporter generating post-mortems from error data
  • Custom alerting beyond Sentry's built-in rules

Need custom error monitoring solutions? I build observability tools and DevOps pipelines.

Email me: spinov001@gmail.com
Check out my developer tools: https://apify.com/spinov001

Top comments (0)