What to log from a Sume API error: request_id, code, no secrets
Log the status, error.code, error.request_id, retry-after and the path without its query. Keep keys, signed URLs and media URLs out. A 25-line Python logger.

Log the HTTP status, error.code, error.request_id, the retry-after header and the path without its query string. Do not log API keys, signed URLs, raw media URLs, or private workspace and user ids; the errors page says to leave them out of anything you share with Sume support.
The request id in the error body is safe to share with Sume, which makes it the join key between your logs and theirs. If you only log one field, log that one.
What each field is for
The table is a log schema. Each row says what the field lets you do later.
| Field | Where it comes from | Why keep it |
|---|---|---|
| status, code | HTTP status and error.code | Group failures by cause; feed the retry decision. |
| request_id | error.request_id (also a response header) | Quote it to Sume support. |
| retry_after | retry-after header on 429 | Check that your backoff obeyed it. |
| ratelimit_remaining | ratelimit-remaining header | See budget pressure before the 429. |
| path without query | Your request | Query strings can carry URLs you should not store. |
| body_head | First 60 characters, only if not JSON | Catch proxies and edge blocks that do not send the Sume error shape. |
A logger you can paste
It builds one JSON line per failure. The body_head field is filled only when the body was not a Sume error, which is exactly the case where request_id is missing. The sample runs as is and prints one line:
import json, logging, time
log = logging.getLogger("sume")
def log_sume_error(method, path, status, headers, body_text):
"""Structured line with what support needs and nothing secret."""
try:
err = json.loads(body_text).get("error", {})
except ValueError:
err = {}
log.error(json.dumps({
"ts": int(time.time()), "method": method, "path": path.split("?")[0],
"status": status, "code": err.get("code"),
"request_id": err.get("request_id"),
"retry_after": headers.get("retry-after"),
"ratelimit_remaining": headers.get("ratelimit-remaining"),
"body_head": None if err else body_text[:60],
}))
logging.basicConfig(level=logging.ERROR, format="%(message)s")
log_sume_error("POST", "/v1/image-1.0/generate?x=1", 429,
{"retry-after": "12"},
'{"error":{"code":"rate_limited","request_id":"req_demo"}}')Two distinct ids
Do not confuse the error request_id with the job id. On a submit that succeeds, request_id in the response data is the job id you poll. On an error, request_id identifies the failed request. Store them in different columns, and never put a job id in a field named for requests.
Also keep the Idempotency-Key you sent in your own logs. Sume omits idempotency keys from usage rows, but job rows return the key they were created with, so you can still join your log line to a job after a crash.
- Redact
Authorizationandx-api-keyin any request dump. - Treat signed upload and download URLs as temporary secrets, per the authentication page.
- Sample success logs; keep every failure.
A log line you can grep
Log one structured line per failure, with the same keys every time. That makes a support request a copy-paste job and keeps secrets out by construction, since the schema has no slot for them.
| Log | Never log |
|---|---|
HTTP status and error.code | The API key or the Authorization header |
request_id from the body or headers | Signed or raw media URLs |
| Your item id and idempotency key | Private workspace or user ids |
| Elapsed time and attempt number | The full upstream response body |
Sources
Related posts
More in Developers
- Which Sume timeout is which: sync, jobs_wait, waitForJob, webhooks
Sume's waits differ: 30 s sync cap, 50 s jobs_wait, a 20-minute SDK default in ms, 10 s per webhook attempt. A table of each unit and what expiry does.
- Write a Sume video URL back to a CMS record: what to store
Sume media URLs in a finished run are durable and public. Store them on your CMS record, and proxy or copy them if you need per-customer access control.
- Which MCP server lets Claude Code or Cursor generate video and images?
MCP servers that let Claude Code and Cursor make video and images: Sume, fal, Replicate, Runway, Higgsfield. Endpoints, sign-in, billing, setup.
- Idempotency keys for AI video APIs: retry without paying twice
An idempotency key makes a retried create return the original run or job instead of a second paid one. How Sume's Idempotency-Key works on each API.
Written by Sume