Claude API Fundamentals Best Practices
Twenty-five practices for reliable authentication, requests, and error handling when building on the Claude API with the official Python SDK.
Organized from setup and authentication through request design, error handling, and model selection.
How to Use This List
- Treat section A as non-negotiable setup hygiene before writing any application code.
- Revisit section C whenever a new failure mode shows up in production logs.
- Use section D when you're first choosing a model, and again whenever cost or latency becomes a concern.
A - Authentication and Setup
- Never hardcode your API key in source. Use the
ANTHROPIC_API_KEYenvironment variable or a secrets manager, and add.envto.gitignorebefore your first commit. - Fail fast when the API key is missing. Check for the key explicitly at startup and raise a clear error, rather than letting a cryptic
401surface deep in your call stack later. - Use separate API keys per environment. Dev, staging, and production keys let you revoke or rotate one without affecting the others.
- Pin the anthropic SDK version in your dependency file. An unpinned dependency can pull in breaking changes on a fresh install; upgrade deliberately instead.
- Construct the client once and reuse it. Build
anthropic.Anthropic()at application startup or module load, not per request or per function call.
B - Request Design
- Always set max_tokens deliberately, not to an arbitrary default. Too low truncates useful output; too high wastes budget on tasks that don't need it.
- Use system prompts for persistent instructions, not the first user message. Keeping role/constraint instructions in
systemkeeps themessageshistory focused on the actual conversation. - Lower temperature for factual, structured, or deterministic tasks. Reserve higher values for genuinely creative or exploratory generation.
- Resend full conversation history on every call. The API is stateless; there is no server-side session to reference, so the client owns the conversation state.
- Prefer temperature over top_p for most tuning. Adjust one knob deliberately rather than changing both
temperatureandtop_pat once, which makes behavior harder to predict. - Keep prompts and system instructions in version control. Treat prompt text as code, review changes to it the same way you'd review logic changes.
C - Error Handling and Resilience
- Set a sensible max_retries on the client. The SDK's built-in exponential backoff handles
429/5xxautomatically; don't disable it without a specific reason. - Catch specific exception types, not a bare Exception.
anthropic.AuthenticationError,anthropic.RateLimitError, andanthropic.BadRequestErroreach point to a different fix. - Never retry 4xx errors other than 429. A malformed request fails identically on every retry; fix the request instead of retrying it.
- Add jitter to any custom retry logic. Without random variance, many clients backing off on the same schedule can resynchronize and re-trigger the same rate limit.
- Set a request timeout appropriate to your use case. Long streaming generations need a longer timeout than short, simple completions.
- Log status codes and error messages, but redact request headers. Full request dumps can leak your API key into logs if not scrubbed first.
- Monitor usage.input_tokens and usage.output_tokens on real traffic. Token counts drive both cost and context-window usage; don't fly blind on either.
D - Model Selection and Cost
- Default to Claude Sonnet 5 unless you have a specific reason to change it. It's the current balanced default across the lineup.
- Route simple, high-volume tasks to Claude Haiku 4.5. Classification, tagging, and short extraction rarely need a flagship model.
- Reserve Claude Opus 4.8 or Claude Fable 5 for genuinely hard, multi-step tasks. Using a flagship model everywhere inflates cost without a matching quality benefit for simple work.
- Centralize model ID strings as named constants. A future model swap should be a one-line change, not a search-and-replace across the codebase.
- Verify current model IDs and pricing before hardcoding them. Model names, context limits, and prices change as new versions ship; don't rely on stale documentation or training data.
- Budget for post-introductory pricing, not just launch rates. Some models ship with a limited-time intro price that increases on a announced date; plan around the steady-state rate.
E - Architecture and Maintainability
- Wrap the SDK client once your codebase has multiple call sites. A thin wrapper centralizes default model, retries, timeout, and error handling instead of repeating them everywhere.
- Keep sync and async client usage separate. Use
anthropic.AsyncAnthropicinsideasynciocode andanthropic.Anthropicin sync code; don't mix them in one call path.
FAQs
Which practice should I implement first if I'm just getting started?
Setting up the API key correctly via an environment variable (section A) and constructing the client once, reused across your app, matters before anything else.
Do I really need to catch specific exception types instead of a generic Exception?
Yes, anthropic.AuthenticationError, anthropic.RateLimitError, and anthropic.BadRequestError each require a different fix, a bare except Exception hides which one occurred and how to respond.
Is it ever okay to hardcode an API key for a quick local test?
Only for a throwaway script you'll never commit; an environment variable is just as fast to set up and avoids the risk of an accidental commit exposing the key.
Why does resending full conversation history matter for cost, not just correctness?
Because the API is stateless, every turn in a growing conversation resends (and is billed on) the entire prior history, so conversation length has a direct, compounding cost impact.
Should I always use the cheapest model to save cost?
No, match model tier to task difficulty; using a model too weak for a hard reasoning task produces worse results that may cost more in retries and rework than a stronger model would have.
What's the risk of not pinning the SDK version?
An unpinned anthropic dependency can silently pull in a newer version with breaking changes on a fresh install, causing failures unrelated to your own code changes.
Why add jitter to retry logic if the SDK already backs off?
The SDK's built-in retries already include jitter; this practice matters specifically when you write custom retry logic on top of or instead of the built-in behavior.
How often should I revisit model selection?
Whenever cost or latency becomes a concern, and periodically regardless, since new model versions and pricing can shift the best default over time.
What's the single most common mistake this list addresses?
Treating all errors the same, retrying 400-class errors that will never succeed on retry, or not retrying 429/5xx errors that would have succeeded with backoff.
Do I need a client wrapper from day one?
No, a wrapper is worth building once you have multiple call sites needing shared configuration; for a single script, a direct client call is simpler and sufficient.
Related
- Understanding the Claude API Request Lifecycle - the request flow these practices protect.
- Handling Rate Limits with Exponential Backoff - the retry practices in section C, in depth.
- Claude API Error Codes and Troubleshooting Reference - the full error catalog behind section C.
- Selecting a Model: Claude Fable 5, Opus 4.8, Sonnet 5, and Haiku 4.5 - the comparison behind section D.
- Building a Reusable Claude API Client Wrapper - the pattern behind section E.
Stack versions: Written against the Claude model lineup current as of ~June 2026 - Claude Fable 5, Claude Opus 4.8, Claude Sonnet 5 (the default), and Claude Haiku 4.5 - and the official
anthropicPython SDK (latest 0.x release). Model names, SDK versions, and pricing move quickly - verify current specifics at platform.claude.com/docs before relying on them.