How to Log Claude API Requests: A Practical Guide
If you're calling the Claude API in production, you need a record of what was sent, what came back, how long it took, and how many tokens it used. This is what "logging Claude API requests" means in practice: capturing structured metadata around every call so you can debug failures, track costs, audit usage, and reconstruct what happened when something goes wrong.
The direct answer is: intercept every request/response pair at the point where you call the API, extract the fields you care about (timestamp, model, token counts, latency, status, and optionally the prompt/response content), and write them to a durable store — a database, a log aggregator, or a dedicated observability tool. Below is exactly how to build that, what to include, what to leave out, and where a hosted API layer can save you the work.
Why logging matters for the Claude API
Unlike a typical REST API, Claude API calls have three properties that make logging non-optional once you're past prototyping:
- Cost is usage-based. Every request has an input and output token count that maps directly to money. Without logs, you can't tell which endpoint, feature, or customer is driving spend.
- Failures are often silent. Rate limits, context overflows, and malformed tool calls can degrade output quality without throwing a hard error your monitoring will catch.
- Debugging requires the full exchange. A vague "the model gave a weird answer" bug report is unsolvable without the exact prompt, system message, and parameters that produced it.
What to capture in every log entry
A useful log record for a Claude API call typically includes:
- Request ID (yours, generated before the call)
- Timestamp (start and end, so you can derive latency)
- Model name and version
- Input token count and output token count
- HTTP status code and any error type/message
- Whether the call was streamed
- Tool calls made, if any, and their names
- The calling service/feature/user (for attribution)
- Optionally: the full prompt and response body
That last one is the one to think about carefully — see the redaction section below.
A minimal logging wrapper
The cleanest way to log Claude API calls is to wrap your API client in a single function that all call sites go through. Here's a Node.js example:
async function callClaude(payload, meta = {}) {
const requestId = crypto.randomUUID();
const start = Date.now();
let response, error;
try {
response = await fetch("https://api.anthropic.com/v1/messages", {
method: "POST",
headers: {
"x-api-key": process.env.ANTHROPIC_API_KEY,
"anthropic-version": "2023-06-01",
"content-type": "application/json",
},
body: JSON.stringify(payload),
});
} catch (err) {
error = err;
}
const durationMs = Date.now() - start;
const body = response ? await response.json() : null;
await writeLog({
requestId,
timestamp: new Date().toISOString(),
durationMs,
model: payload.model,
status: response?.status ?? "network_error",
inputTokens: body?.usage?.input_tokens ?? null,
outputTokens: body?.usage?.output_tokens ?? null,
error: error?.message ?? body?.error?.message ?? null,
feature: meta.feature,
userId: meta.userId,
});
if (error) throw error;
return body;
}
The key idea: logging happens at one chokepoint, not scattered across every call site. This guarantees consistency and means adding a new field (say, cache hit/miss) only requires one edit.
Where to send the logs
Once you're extracting structured data, you have three realistic destinations:
- A database table (Postgres, ClickHouse) if you want to run your own cost and usage queries.
- A log aggregator (Datadog, Better Stack, an ELK stack) if you already centralize logs there and want alerting on top.
- A hosted layer that already tracks this for you. If you're using SubToAPI to expose your Claude access as an API, every request made through your
sub_live_...key is already recorded with model, token counts, latency, and status in the dashboard — so you get request-level usage metadata without building the wrapper above yourself. This is particularly useful for teams who want per-seat visibility without standing up their own logging pipeline. See /docs for what's tracked automatically.
For anything you build yourself, batch writes rather than logging synchronously on the request path — an async queue or a fire-and-forget write keeps logging from adding latency to user-facing calls.
Redacting sensitive content
Logging full prompts and responses is extremely useful for debugging, but it also means your logs may contain PII, customer data, or anything else users type into your product. Before storing raw content:
- Strip or hash user-identifiable fields you don't need for debugging.
- Truncate long documents in the log entry (store a reference/ID instead of the full text if it's already in your database).
- Set a retention policy — 30 to 90 days is common — and enforce it with a scheduled deletion job, not manual cleanup.
- If you operate in a regulated industry, log token counts and metadata by default, and only log full content behind a feature flag reserved for active debugging sessions.
Logging streamed responses
If you're using streaming (see /docs/streaming for the concepts, which apply the same way to direct Anthropic streaming), log the request when it starts and the aggregated result when the stream closes. Concatenate the text deltas server-side to reconstruct the full response for your logs — don't try to log every individual chunk, that produces noise without value.
let fullText = "";
for await (const chunk of stream) {
if (chunk.type === "content_block_delta") {
fullText += chunk.delta.text;
}
}
// log fullText + usage once the stream finishes
Logging tool use
When Claude makes tool calls, log the tool name and input parameters, plus whether your code successfully executed the tool and what it returned. Tool-use bugs are almost always the hardest to reproduce after the fact because they involve multiple round trips — a log that only captures the final answer will miss the intermediate tool call that actually failed. See /docs/tools for the request/response shape you'll be logging.
Getting logging without building a pipeline
If you don't want to maintain a logging pipeline, database schema, and retention job just to answer "how many tokens did we use last week," a layer like SubToAPI gives you request logs, usage metadata, and per-key/per-seat breakdowns out of the box, on top of your existing Claude access. Start with the free trial at /signup, check /pricing for plan details, and use /docs/quickstart to get your first logged request in minutes.
questions
Do I need to log every single Claude API request, even in development? Not necessarily with full content, but logging status, latency, and token counts even in dev is cheap and helps you catch regressions before they hit production.
How long should I retain Claude API logs? 30–90 days is a common default for debugging and cost analysis; extend it only if you have a specific compliance or audit requirement, and always document the policy.
Can I log Claude API requests without modifying my application code? Yes, if you route calls through a proxy or a hosted API layer like SubToAPI that records request metadata automatically — otherwise you'll need a wrapper function as shown above.