Alert on Sume API errors by category and stage, not 4xx (Python)

Sume error envelopes carry category, stage and public_reason for dashboards. A Python tally pages on quota and auth, tickets bad input, watches 429s.

4 min readSume
All posts

Every Sume error body has the same shape: code, message, request_id, category, stage, retryable, retry_after_seconds, public_reason, next_action and details. The docs split their jobs. Branch your control flow on the HTTP status and then on code. Use category, stage and public_reason for the coarser labels on dashboards and alerts. This post is about the second half, because it is where teams tend to page the wrong people.

If a Recast or video client alerts on every 4xx, a busy hour of queue_full and rate_limited responses wakes someone up for something your retry loop already handled. Alerting on category fixes that without parsing messages.

What each category looks like

Sume error categories with example codes, from the API source (read 2026-10-03)
CategoryStageExample codeWho acts
quotausage_reservationinsufficient_creditsA person: add funds or raise a cap
authauthunauthorizedA person: fix or rotate the key
rate_limitqueuerate_limitedYour retry loop
rate_limitruntimerate_limit_unavailableYour retry loop, longer wait
queuequeuequeue_fullYour retry loop, same key
job_statejob_statejob_not_completedYour poll loop
validationvalidationinvalid_requestA developer: fix the request
mediamediaattachment_fetch_failedA developer: fix the input URL

A severity function and a tally

The code below maps a category to a severity and counts errors by severity, category, stage and public reason. Feed it the error bodies your client already logs. The public_reason strings in the sample are placeholders, so substitute the values you see in your own logs. The sample at the bottom runs as written and prints one row per distinct combination, so a flood of identical queue_full rows collapses into a single counted line.

import json
from collections import Counter

PAGE = {"quota", "auth", "internal"}   # a human has to act
TICKET = {"validation", "media"}       # a code or data bug
# everything else (rate_limit, queue, runtime_unavailable, job_state): code retries


def severity(error: dict) -> str:
    if error["category"] in PAGE:
        return "page"
    return "ticket" if error["category"] in TICKET else "watch"


def tally(bodies: list[str]) -> Counter:
    seen = Counter()
    for body in bodies:
        e = json.loads(body)["error"]
        seen[(severity(e), e["category"], e["stage"], e["public_reason"])] += 1
    return seen


sample = [
    '{"error":{"code":"queue_full","category":"queue","stage":"queue","public_reason":"queue_full"}}',
    '{"error":{"code":"insufficient_credits","category":"quota","stage":"usage_reservation","public_reason":"insufficient_balance"}}',
    '{"error":{"code":"queue_full","category":"queue","stage":"queue","public_reason":"queue_full"}}',
]
for key, n in tally(sample).most_common():
    print(n, *key)

Rules that keep the alerts quiet

  • Page on quota and auth. Retrying insufficient_credits never works, and a revoked key fails every call after it.
  • Ticket validation and media. A 4xx at create means nothing ran and nothing was charged, so the fix is in your code or data.
  • Watch the rest as a rate. A rising queue count means you are submitting faster than your plan drains, which is a pacing problem rather than an outage.
  • Always log request_id, which is also the x-sume-request-id header, next to the tally so support can find the exact request.
  • Keep retryable in your retry code. It is the field that says whether resending the same request can succeed.

The envelope table and the per-code advice live in the errors reference. The rate limit budgets that decide how often you see rate_limited are in the authentication guide.

Sources

Related posts

More in Developers

All Developers posts

Written by Sume