Create a Client

Su Suriya Ravichandran Updated 10 Nov 2026
10 min read ·Lesson 10 of 200

What a Client Is

A client is the object your program uses to talk to the Claude API. You create it once, and it holds everything needed to send requests:

  • credentials: your API key,
  • connection settings: the API address, timeouts, and retry rules, and
  • open network connections that can be reused between requests.
from anthropic import Anthropic

client = Anthropic()

Every API call then goes through this object: client.messages.create(...), client.models.list(), and so on.

Why the SDK uses a client object instead of plain functions such as anthropic.create_message(...): configuration is set once and applied automatically to every request. You can also have several clients with different configurations in the same program, for example one per API key or workspace, without them interfering with each other.


Creating a Client with Defaults

from anthropic import Anthropic

client = Anthropic()

With no arguments, the client uses these defaults:

SettingDefaultWhere it comes from
CredentialANTHROPIC_API_KEY (then the other sources in Lesson 7)Environment
API addresshttps://api.anthropic.comANTHROPIC_BASE_URL if set
Timeout10 minutes per requestBuilt-in default
Retries2 automatic retries for retryable errorsBuilt-in default
API versionThe one tied to your SDK major version (2026-10-21 for 2.x)Built-in

For learning and many scripts, these defaults are exactly right.

Note: Creating a client does not contact the API. Network activity starts only with the first request. A client can therefore be created successfully even when the key is wrong or the network is down. Those problems appear as exceptions on the first call.


Constructor Options

You can override the defaults with keyword arguments:

from anthropic import Anthropic

client = Anthropic(
    api_key="...",            # Credential; normally omitted (use ANTHROPIC_API_KEY)
    base_url="https://...",   # API address
    timeout=60.0,             # Seconds per request
    max_retries=3,            # Automatic retries for retryable errors
    default_headers={"anthropic-workspace-id": "wrkspc_..."},  # Sent on every request
)
OptionTypePurposeWhen to change it
api_keystrThe API key to useThe key comes from a secrets manager or a differently named variable
auth_tokenstrA bearer token instead of an API keyGateways and token-based authentication
base_urlstrThe API addressCorporate gateways, proxies, or test servers
timeoutfloat or httpx2.TimeoutMaximum wait per requestInteractive apps that must respond quickly
max_retriesintNumber of automatic retriesIncrease for batch jobs; set to 0 for custom retry logic
default_headersdictHeaders added to every requestWorkspace selection, gateway headers
http_clientDefaultHttpxClientCustom HTTP configurationProxies and custom network settings

Remember from Lesson 7: pass at most one credential (api_key or auth_token). Passing two raises ValueError.

Timeouts in Detail

A single number sets the overall timeout in seconds:

client = Anthropic(timeout=30.0)

For finer control, use an httpx2.Timeout object. httpx2 is installed with the SDK.

import httpx2
from anthropic import Anthropic

client = Anthropic(
    timeout=httpx2.Timeout(60.0, connect=5.0),
)

Here, establishing the connection may take at most 5 seconds, and the other phases of the request use the 60-second default.

How to choose a timeout:

WorkloadSuggested approach
Short interactive answers30–60 seconds
Long documents or large outputsUse streaming (Lessons 51–60) rather than a very long timeout
Background batch jobsKeep the default or use the Message Batches API

When a request times out, the SDK raises anthropic.APITimeoutError. Timed-out requests are also retried according to max_retries, so the worst-case wait is roughly the timeout multiplied by the number of attempts, plus the backoff delays between them.

Retries in Detail

client = Anthropic(max_retries=4)

Automatic retries apply only to errors that may succeed on a second try: connection errors, 408, 409, 429, and 5xx responses. Errors such as 400 or 401 are never retried, because the same request would fail the same way. Between attempts, the SDK waits with exponential backoff: each wait is longer than the previous one, which gives an overloaded service time to recover.

max_retriesUse when
0You implement your own retry logic, or you are testing and want failures immediately
2 (default)Most applications
3–5Background jobs where finishing matters more than speed

Per-Request Settings with with_options()

Sometimes one request needs different settings, such as a shorter timeout for a quick classification. with_options() returns a copy of the client with the changes applied:

from anthropic import Anthropic

client = Anthropic()  # Defaults: 10-minute timeout, 2 retries

message = client.with_options(timeout=15.0, max_retries=0).messages.create(
    model="claude-opus-5-5",
    max_tokens=1024,
    messages=[{"role": "user", "content": "Is this spam? 'You won a prize!' Answer yes or no."}],
)
  • The original client is unchanged.
  • The copy shares the original's connection pool, so creating it is cheap.
  • A credential passed to with_options() replaces the copy's credential (Lesson 7).

You can also keep the configured copy for repeated use:

fast_client = client.with_options(timeout=15.0)

client.copy(...) accepts the same options. with_options is the more readable name for per-request use.


The Client's Lifecycle

Create Once, Reuse Everywhere

A client keeps a connection pool: open HTTPS connections that are reused for later requests. Opening a new HTTPS connection requires several network round trips, including the TLS handshake, so reusing connections makes requests faster.

# Good: one client, reused
client = Anthropic()

for question in questions:
    client.messages.create(...)
# Bad: a new client, and new connections, for every request
for question in questions:
    Anthropic().messages.create(...)

The second version throws away the connection pool on every iteration. It works, but it is slower and wastes resources, and the effect grows with traffic.

Closing a Client

By default, a client's connections are closed automatically when the client object is garbage-collected. You can also close it explicitly:

client = Anthropic()
try:
    ...
finally:
    client.close()

Or, more simply, with a context manager:

with Anthropic() as client:
    message = client.messages.create(...)
# Connections are closed here, even if an exception occurred
SituationApproach
Long-running application (web server, worker)Create one client at startup and keep it for the life of the process
Short scriptEither approach; a context manager is the tidiest
Code that creates temporary clients (for example, per tenant)Use with or call .close() so connections are released promptly

Sync and Async Clients

Anthropic is synchronous: each call blocks until the response arrives. AsyncAnthropic has the same options and methods, but its calls must be awaited:

from anthropic import AsyncAnthropic

client = AsyncAnthropic(timeout=60.0, max_retries=3)

Use the async client in async frameworks such as FastAPI, and when sending many requests concurrently. Lessons 71–80 cover it in depth.


Multiple Clients in One Program

Each client is independent. That is useful when different parts of an application need different configurations:

import os

from anthropic import Anthropic

# Interactive features: fail fast
chat_client = Anthropic(timeout=30.0, max_retries=1)

# Background reports: be patient
report_client = Anthropic(timeout=300.0, max_retries=5)

# A second workspace, with its own key
analytics_client = Anthropic(api_key=os.environ["ANALYTICS_ANTHROPIC_KEY"])

If two clients differ only in timeout or retries, prefer one client plus with_options(). It shares connections and keeps configuration in one place. Create genuinely separate clients when credentials differ.


Practical Example: A Client Factory

In a real application, client creation belongs in one place, configured from settings and easy to replace in tests. A small factory function achieves that.

"""claude_client.py: create and share a configured Anthropic client."""

import logging
import os
from functools import lru_cache

import anthropic
from anthropic import Anthropic

logger = logging.getLogger(__name__)


@lru_cache(maxsize=1)
def get_client() -> Anthropic:
    """Return the application's shared client, creating it on first use."""
    timeout = float(os.environ.get("CLAUDE_TIMEOUT", "60"))
    max_retries = int(os.environ.get("CLAUDE_MAX_RETRIES", "2"))

    client = Anthropic(timeout=timeout, max_retries=max_retries)

    logger.info(
        "Anthropic client ready (sdk=%s, timeout=%ss, max_retries=%s)",
        anthropic.__version__,
        timeout,
        max_retries,
    )
    return client


def ask(prompt: str, client: Anthropic | None = None) -> str:
    """Send a single prompt and return the text of the reply."""
    client = client or get_client()
    message = client.messages.create(
        model=os.environ.get("CLAUDE_MODEL", "claude-opus-5-5"),
        max_tokens=1024,
        messages=[{"role": "user", "content": prompt}],
    )
    return "".join(block.text for block in message.content if block.type == "text")


if __name__ == "__main__":
    logging.basicConfig(level=logging.INFO)
    print(ask("Name two benefits of reusing HTTP connections."))

Example output:

INFO:__main__:Anthropic client ready (sdk=2.0.0, timeout=60.0s, max_retries=2)
Reusing HTTP connections avoids repeating the connection and TLS setup for every request, which lowers latency. It also reduces CPU and network overhead on both the client and the server.

How It Works

  • @lru_cache(maxsize=1) remembers the return value of get_client(). The first call creates the client, and every later call returns the same object. This is a simple, thread-safe-to-call way to get a shared, lazily created client: it is created only when first needed.
  • Settings come from environment variables with defaults (Lesson 8). They are converted with float() and int(), because environment values are strings.
  • The startup log line records the SDK version and configuration. This is invaluable when diagnosing behavior in production. It never logs the key.
  • ask() accepts an optional client parameter. Normal code calls ask("...") and uses the shared client. Tests can pass a fake or specially configured client instead. This technique is called dependency injection, and Lessons 181–184 use it for testing without real API calls.
  • The reply text is assembled from text blocks, so the function works even when a response contains other block types first.

Common Mistakes

Creating a Client per Request

What happens: latency increases and resources are wasted, especially under load.

Fix: create one client at startup, or use a cached factory as above, and reuse it.

Adding /v1 to base_url

client = Anthropic(base_url="https://api.anthropic.com/v1")   # Wrong

What happens: the SDK adds its own /v1/... paths, so requests go to /v1/v1/messages and fail with 404 Not Found.

Fix: use only the host part, https://api.anthropic.com, or the base address your gateway documents.

Setting Very Long Timeouts Instead of Streaming

What happens: some networks drop idle connections. Requests then fail after a long wait, and are retried, multiplying the delay.

Fix: use streaming for long responses. The SDK even raises a ValueError for non-streaming requests that it expects to take longer than about 10 minutes, unless you enable streaming or set a timeout yourself.

Disabling Retries in Production Without a Replacement

What happens: brief network glitches and rate limits become user-facing errors.

Fix: keep the default retries, or implement your own retry logic deliberately (Lessons 143 and 146).

Using the Sync Client in Async Code

What happens: in FastAPI or other async applications, a blocking Anthropic call freezes the event loop, and every other request waits.

Fix: use AsyncAnthropic in async code.

Expecting Client Creation to Validate the Key

What happens: Anthropic() succeeds, and the code assumes everything is configured correctly.

Fix: creating a client does not contact the API. To verify configuration at startup, make a zero-token request such as client.models.list(limit=1) (Lesson 6).


Best Practices

  • One long-lived client per process (per credential), created at startup or lazily through a cached factory.
  • Configure timeouts deliberately: short for interactive features, with streaming for long outputs.
  • Keep automatic retries enabled unless you replace them with your own logic.
  • Use with_options() for per-request differences instead of creating extra clients.
  • Use context managers or .close() for short-lived clients.
  • Log the SDK version and client settings at startup, never the credential.
  • Design functions to accept a client parameter so they can be tested without real API calls.

Exercise

  1. Create a client with a 20-second timeout and 3 retries. Print client.timeout and client.max_retries to confirm the settings.

  2. Use with_options() to send one request with max_retries=0, and confirm that the original client still reports max_retries == 3.

  3. Rewrite this code so it reuses one client:

    for topic in ["lists", "dicts", "sets"]:
        reply = Anthropic().messages.create(
            model="claude-opus-5-5",
            max_tokens=1024,
            messages=[{"role": "user", "content": f"Explain Python {topic} in one sentence."}],
        )
  4. Explain why Anthropic(base_url="https://api.anthropic.com/v1") fails.

Show answers

Steps 1–2:

from anthropic import Anthropic

client = Anthropic(timeout=20.0, max_retries=3)
print(client.timeout, client.max_retries)        # 20.0 3

quick = client.with_options(max_retries=0)
print(quick.max_retries, client.max_retries)     # 0 3

Step 3:

from anthropic import Anthropic

client = Anthropic()

for topic in ["lists", "dicts", "sets"]:
    reply = client.messages.create(
        model="claude-opus-5-5",
        max_tokens=1024,
        messages=[{"role": "user", "content": f"Explain Python {topic} in one sentence."}],
    )
    print("".join(block.text for block in reply.content if block.type == "text"))

Step 4: the SDK appends its own endpoint paths, which already start with /v1, to base_url. The request goes to https://api.anthropic.com/v1/v1/messages, which does not exist, so the API returns 404 Not Found.

0 Comments

Reviewed before they appear

No comments yet.

Anthropic SDK Using Python
Anthropic SDK Intro Claude API Intro SDK Requirements Install Anthropic SDK Upgrade the SDK Create an API Key Set API Keys Use Environment Variables Import Anthropic Create a Client Send Your First Message Choose a Claude Model Set Max Tokens Read a Response Print Response Text Understand Message Blocks Use System Prompts Send User Messages Set Temperature Handle Stop Reasons Messages API Intro Create Messages Use Message Roles Send Conversation History Continue a Conversation Build a Chat Loop Reset Chat History Use Multiple Turns Inspect Message IDs Read Usage Data Write Clear Prompts Set a System Prompt Add Context to Prompts Use Prompt Templates Add Examples to Prompts Request JSON Output Use XML Tags Control Response Length Improve Prompt Quality Avoid Prompt Injection Read Text Blocks Read Content Blocks Check Stop Reasons Count Input Tokens Count Output Tokens Count Tokens Before Sending Limit Output Tokens Use Stop Sequences Read Request IDs Parse API Responses Streaming Intro Stream Text Output Use Stream Events Use Stream Helpers Get Final Messages Stream Async Responses Handle Stream Errors Build a Live Chat Stream Long Responses Close Streams Safely Build a Chatbot Keep Chat History Manage Chat Sessions Add Chat Memory Build a CLI Chatbot Build a Web Chatbot Use Flask with Claude Use FastAPI with Claude Return JSON from an API Add Chat Error Messages Async SDK Intro Create an Async Client Send Async Messages Use asyncio Run Parallel Requests Stream Async Text Handle Async Errors Set Async Timeouts Close Async Clients Choose Sync or Async Tool Use Intro Define a Tool Create Tool Schemas Send Tools to Claude Read Tool Calls Run Python Tools Return Tool Results Continue After Tool Use Control Tool Choice Handle Tool Errors Use the beta_tool Decorator Create a Tool Runner Add Tool Docstrings Validate Tool Inputs Use Multiple Tools Limit Tool Calls Log Tool Activity Build a Weather Tool Build a Search Tool Build a Calculator Tool Structured Output Intro Request JSON Data Validate JSON Output Use Pydantic Models Handle Invalid JSON Extract Fields from Text Generate Typed Data Use Image URLs Send Base64 Images Read PDFs with Claude Use Output Schemas Build Data Extraction Test Structured Responses Send Image Inputs Upload Files Use File IDs Process Text Files Summarize Documents Extract Table Data Handle File Errors Read Cache Usage Reduce Repeated Input Costs Debug Cache Misses Prompt Caching Intro Enable Prompt Caching Cache System Prompts Cache Long Context Test Cache Performance Use Cache Breakpoints Cache Best Practices Message Batches Intro Create a Message Batch Set Batch IDs Check Batch Status List Message Batches Get Batch Results Process Batch Outputs Handle Failed Batch Items Use Batches for Bulk Tasks Compare Batches and Requests Handle API Errors Handle Rate Limits Retry Failed Requests Set Request Timeouts Handle Network Errors Use Exponential Backoff Build Reliable API Calls Use Extended Thinking Log API Errors Track Request IDs Paginate API Results Set Thinking Budgets Read Thinking Blocks Use Tool Search Use Web Search Tools Use Computer Tools Use Citations Use Token Counting Read Raw Responses Set Custom Headers Use Claude on AWS Bedrock Use Claude on Vertex AI Use Claude on Microsoft Foundry Configure Cloud Credentials Choose a Cloud Provider Compare API Integrations Configure Provider Clients Handle Cloud Errors Use Cloud SDK Extras Deploy Cloud Apps Protect API Keys Use a .env File Ignore Secrets in Git Validate User Input Reduce Prompt Injection Limit Tool Permissions Avoid Unsafe Tool Calls Sanitize Tool Results Set Usage Limits Review Security Logs Test SDK Functions Mock API Responses Test Error Handling Test Streaming Code Measure Token Usage Track API Costs Configure Logging Deploy with Docker Deploy a FastAPI App Monitor Production Apps Build a Claude Chatbot Build a Text Summarizer Build a Code Explainer Build a JSON Extractor Build a PDF Assistant Build a Streaming Chat App Build a Tool-Calling Agent Build a Batch Processor Build a Research Assistant Build a Claude API Dashboard
Ask about this post
AI Ask about this post

Ask questions about Create a Client and get answers drawn from it.

Signed-in readers only.