# Claude API — PHP > **Note:** The PHP SDK is the official Anthropic SDK for PHP. Tool runner and Agent SDK are not available. Bedrock, Vertex AI, and Foundry clients are supported. ## Installation \`\`\`bash composer require "anthropic-ai/sdk" \`\`\` ## Client Initialization \`\`\`php use Anthropic\\Client; // Using API key from environment variable $client = new Client(apiKey: getenv("ANTHROPIC_API_KEY")); \`\`\` ### Amazon Bedrock \`\`\`php use Anthropic\\Bedrock; // Constructor is private — use the static factory. Reads AWS credentials from env. $client = Bedrock\\Client::fromEnvironment(region: 'us-east-1'); \`\`\` ### Google Vertex AI \`\`\`php use Anthropic\\Vertex; // Constructor is private. Parameter is \`location\`, not \`region\`. $client = Vertex\\Client::fromEnvironment( location: 'us-east5', projectId: 'my-project-id', ); \`\`\` ### Anthropic Foundry \`\`\`php use Anthropic\\Foundry; // Constructor is private. baseUrl or resource is required. $client = Foundry\\Client::withCredentials( authToken: getenv('ANTHROPIC_FOUNDRY_AUTH_TOKEN'), baseUrl: 'https://.services.ai.azure.com/anthropic', ); \`\`\` --- ## Basic Message Request \`\`\`php $message = $client->messages->create( model: '{{OPUS_ID}}', maxTokens: 1024, messages: [ ['role' => 'user', 'content' => 'What is the capital of France?'], ], ); // content is an array of polymorphic blocks (TextBlock, ToolUseBlock, // ThinkingBlock). Accessing ->text on content[0] without checking the block // type will throw if the first block is not a TextBlock (e.g., when extended // thinking is enabled and a ThinkingBlock comes first). Always guard: foreach ($message->content as $block) { if ($block->type === 'text') { echo $block->text; } } \`\`\` If you only want the first text block: \`\`\`php foreach ($message->content as $block) { if ($block->type === 'text') { echo $block->text; break; } } \`\`\` --- ## Streaming > **Requires SDK v0.5.0+.** v0.4.0 and earlier used a single \`$params\` array; calling with named parameters throws \`Unknown named parameter $model\`. Upgrade: \`composer require "anthropic-ai/sdk:^0.6"\` \`\`\`php use Anthropic\\Messages\\RawContentBlockDeltaEvent; use Anthropic\\Messages\\TextDelta; $stream = $client->messages->createStream( model: '{{OPUS_ID}}', maxTokens: 1024, messages: [ ['role' => 'user', 'content' => 'Write a haiku'], ], ); foreach ($stream as $event) { if ($event instanceof RawContentBlockDeltaEvent && $event->delta instanceof TextDelta) { echo $event->delta->text; } } \`\`\` --- ## Tool Use (Manual Loop) Tools are passed as arrays. **The SDK uses camelCase keys** (\`inputSchema\`, \`toolUseID\`, \`stopReason\`) and auto-maps to the API's snake_case on the wire — since v0.5.0. See [shared tool use concepts](../shared/tool-use-concepts.md) for the loop pattern. \`\`\`php use Anthropic\\Messages\\ToolUseBlock; $tools = [ [ 'name' => 'get_weather', 'description' => 'Get the current weather in a given location', 'inputSchema' => [ // camelCase, not input_schema 'type' => 'object', 'properties' => [ 'location' => ['type' => 'string', 'description' => 'City and state'], ], 'required' => ['location'], ], ], ]; $messages = [['role' => 'user', 'content' => 'What is the weather in SF?']]; $response = $client->messages->create( model: '{{OPUS_ID}}', maxTokens: 1024, tools: $tools, messages: $messages, ); while ($response->stopReason === 'tool_use') { // camelCase property $toolResults = []; foreach ($response->content as $block) { if ($block instanceof ToolUseBlock) { // $block->name : string — tool name to dispatch on // $block->input : array — parsed JSON input // $block->id : string — pass back as toolUseID $result = executeYourTool($block->name, $block->input); $toolResults[] = [ 'type' => 'tool_result', 'toolUseID' => $block->id, // camelCase, not tool_use_id 'content' => $result, ]; } } // Append assistant turn + user turn with tool results $messages[] = ['role' => 'assistant', 'content' => $response->content]; $messages[] = ['role' => 'user', 'content' => $toolResults]; $response = $client->messages->create( model: '{{OPUS_ID}}', maxTokens: 1024, tools: $tools, messages: $messages, ); } // Final text response foreach ($response->content as $block) { if ($block->type === 'text') { echo $block->text; } } \`\`\` \`$block->type === 'tool_use'\` also works; \`instanceof ToolUseBlock\` narrows for PHPStan. --- ## Extended Thinking **Adaptive thinking is the recommended mode for Claude 4.6+ models.** Claude decides dynamically when and how much to think. \`\`\`php use Anthropic\\Messages\\ThinkingBlock; $message = $client->messages->create( model: '{{OPUS_ID}}', maxTokens: 16000, thinking: ['type' => 'adaptive'], messages: [ ['role' => 'user', 'content' => 'Solve: 27 * 453'], ], ); // ThinkingBlock(s) precede TextBlock in content foreach ($message->content as $block) { if ($block instanceof ThinkingBlock) { echo "Thinking:\\n{$block->thinking}\\n\\n"; // $block->signature is an opaque string — preserve verbatim if // passing thinking blocks back in multi-turn conversations } elseif ($block->type === 'text') { echo "Answer: {$block->text}\\n"; } } \`\`\` > **Deprecated:** \`['type' => 'enabled', 'budgetTokens' => N]\` (fixed-budget extended thinking) still works on Claude 4.6 but is deprecated. Use adaptive thinking above. \`$block->type === 'thinking'\` also works for the check; \`instanceof\` narrows for PHPStan. --- ## Beta Features & Server-Side Tools **\`betas:\` is NOT a param on \`$client->messages->create()\`** — it only exists on the beta namespace. Use it for features that need an explicit opt-in header: \`\`\`php use Anthropic\\Beta\\Messages\\BetaRequestMCPServerURLDefinition; $response = $client->beta->messages->create( model: '{{OPUS_ID}}', maxTokens: 1024, mcpServers: [ BetaRequestMCPServerURLDefinition::with( name: 'my-server', url: 'https://example.com/mcp', ), ], betas: ['mcp-client-2025-11-20'], // only valid on ->beta->messages messages: [['role' => 'user', 'content' => 'Use the MCP tools']], ); \`\`\` **Server-side tools** (bash, web_search, text_editor, code_execution) are GA and work on both paths — \`Anthropic\\Messages\\ToolBash20250124\` / \`WebSearchTool20260209\` / \`ToolTextEditor20250728\` / \`CodeExecutionTool20260120\` for non-beta, \`Anthropic\\Beta\\Messages\\BetaToolBash20250124\` / \`BetaWebSearchTool20260209\` / \`BetaToolTextEditor20250728\` / \`BetaCodeExecutionTool20260120\` for beta. No \`betas:\` header needed for these.