Uygulama · Claude API
Sitene yapay zekâ.
Bu rehber Anthropic’in Claude modellerini kendi sitene güvenle bağlamayı anlatır: anahtar sunucuda kalır, tarayıcı yalnızca kendi API’ni çağırır.
01Mimari: anahtar asla tarayıcıda değil#
Altın kuralAPI anahtarını HTML’e, JavaScript dosyasına ya da GitHub’a koyma. Tarayıcıya giden her şey herkese açıktır; anahtarını bulan biri senin hesabından harcama yapar.
- Anthropic Console’dan bir API anahtarı oluştur.
- Anahtarı sunucuda ortam değişkeni olarak sakla:
ANTHROPIC_API_KEY. - Tarayıcı senin
/api/chatadresini çağırır; sunucun Claude’a gider, cevabı geri döndürür.
02Modeller#
| Model | Model kimliği | Ne zaman? |
|---|---|---|
| Claude Opus 5.5 | claude-opus-5-5 | Varsayılan: en iyi kalite; karmaşık soru, kod, uzun metin |
| Claude Sonnet 5.5 | claude-sonnet-5-5 | Hızlı ve dengeli; günlük sohbet, destek |
| Claude Haiku 5.5 | claude-haiku-5-5 | En hızlı ve ekonomik; sınıflandırma, kısa yanıt |
Model kimliğini olduğu gibi kullan; sonuna tarih ekleme. Güncel liste ve fiyatlar için resmî dokümantasyona bak.
03API anahtarını saklamak#
ANTHROPIC_API_KEY=sk-ant-....env
node_modules/
vendor/Vercel’de: Project → Settings → Environment Variables. Paylaşımlı hostingde: cPanel’de ortam değişkeni ya da public_html dışında duran bir .env dosyası.
04Node.js (resmî SDK)#
npm install @anthropic-ai/sdk expressimport Anthropic from '@anthropic-ai/sdk';
const client = new Anthropic();
const response = await client.messages.create({
model: 'claude-opus-5-5',
max_tokens: 16000,
system: 'Sen fkdev.sbs sitesinin yardımcı asistanısın. Kısa ve Türkçe cevap ver.',
messages: [{ role: 'user', content: 'fkdev UI nedir?' }],
});
if (response.stop_reason === 'refusal') {
console.log('Model bu isteği yanıtlamadı.');
} else {
for (const block of response.content) {
if (block.type === 'text') console.log(block.text);
}
}new Anthropic() anahtarı ANTHROPIC_API_KEY ortam değişkeninden okur. Cevap bir blok dizisidir; metni almak için type === 'text' olan blokları oku.
05Node.js: canlı akış (streaming) uç noktası#
Uzun cevaplarda kullanıcı beklemesin diye cevabı kelime kelime akıt. Sunucu, Claude’dan gelen metni Server-Sent Events olarak tarayıcıya iletir.
import express from 'express';
import Anthropic from '@anthropic-ai/sdk';
const app = express();
const client = new Anthropic();
app.use(express.json({ limit: '100kb' }));
app.post('/api/chat', async (req, res) => {
const history = Array.isArray(req.body?.messages) ? req.body.messages.slice(-20) : [];
const messages = history
.filter((m) => (m.role === 'user' || m.role === 'assistant') && typeof m.content === 'string')
.map((m) => ({ role: m.role, content: m.content.slice(0, 4000) }));
if (!messages.length || messages[messages.length - 1].role !== 'user') {
return res.status(400).json({ error: 'Mesaj yok' });
}
res.setHeader('Content-Type', 'text/event-stream; charset=utf-8');
res.setHeader('Cache-Control', 'no-cache');
res.setHeader('Connection', 'keep-alive');
try {
const stream = client.messages.stream({
model: 'claude-opus-5-5',
max_tokens: 64000,
system: 'Sen fkdev.sbs sitesinin yardımcı asistanısın. Kısa ve Türkçe cevap ver.',
messages,
});
for await (const event of stream) {
if (event.type === 'content_block_delta' && event.delta.type === 'text_delta') {
res.write('data: ' + JSON.stringify({ text: event.delta.text }) + '\n\n');
}
}
const final = await stream.finalMessage();
res.write('data: ' + JSON.stringify({ done: true, stop: final.stop_reason }) + '\n\n');
} catch (err) {
console.error(err);
res.write('data: ' + JSON.stringify({ error: 'Şu an cevap veremiyorum, biraz sonra tekrar dene.' }) + '\n\n');
}
res.end();
});
app.listen(process.env.PORT || 3000);06Tarayıcı: sohbet arayüzü#
Arayüz sohbet kalıbını kullanır. Akış okunurken metin asistan balonuna parça parça eklenir.
<div class="fk-card" style="max-width:640px;margin:auto">
<div class="fk-chat" id="chat" aria-live="polite"></div>
<form class="fk-input-group" id="chat-form" style="margin-top:18px">
<input class="fk-input" name="q" placeholder="Bir şey sor…" aria-label="Mesaj" required style="padding-right:56px">
<button class="fk-btn fk-btn-icon" type="submit" aria-label="Gönder">→</button>
</form>
</div>const log = document.getElementById('chat');
const form = document.getElementById('chat-form');
const history = [];
function bubble(role, text = '') {
const el = document.createElement('div');
el.className = 'fk-msg ' + (role === 'user' ? 'fk-msg-user' : 'fk-msg-ai');
el.textContent = text;
log.appendChild(el);
return el;
}
form.addEventListener('submit', async (e) => {
e.preventDefault();
const q = form.q.value.trim();
if (!q) return;
form.q.value = '';
bubble('user', q);
history.push({ role: 'user', content: q });
const out = bubble('assistant');
out.innerHTML = '<span class="fk-typing"><i></i><i></i><i></i></span>';
let answer = '';
try {
const res = await fetch('/api/chat', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ messages: history }),
});
const reader = res.body.getReader();
const decoder = new TextDecoder();
let buffer = '';
while (true) {
const { value, done } = await reader.read();
if (done) break;
buffer += decoder.decode(value, { stream: true });
const parts = buffer.split('\n\n');
buffer = parts.pop();
for (const part of parts) {
if (!part.startsWith('data: ')) continue;
const msg = JSON.parse(part.slice(6));
if (msg.text) { answer += msg.text; out.textContent = answer; }
if (msg.error) FK.toast(msg.error, 'danger');
}
}
history.push({ role: 'assistant', content: answer });
} catch {
out.textContent = 'Bağlantı koptu.';
}
});textContent kullanModelden gelen metni innerHTML ile basma; textContent kullan. Markdown göstermek istersen önce güvenli bir temizleyiciden (ör. DOMPurify) geçir.
07PHP (resmî SDK)#
composer require "anthropic-ai/sdk"<?php
require __DIR__ . '/vendor/autoload.php';
use Anthropic\Client;
$client = new Client(apiKey: getenv('ANTHROPIC_API_KEY'));
$message = $client->messages->create(
model: 'claude-opus-5-5',
maxTokens: 16000,
system: 'Sen fkdev.sbs sitesinin yardımcı asistanısın. Kısa ve Türkçe cevap ver.',
messages: [
['role' => 'user', 'content' => 'fkdev UI nedir?'],
],
);
foreach ($message->content as $block) {
if ($block->type === 'text') {
echo $block->text;
}
}PHP SDK’da parametre adları camelCase’dir: maxTokens.
<?php
use Anthropic\Messages\RawContentBlockDeltaEvent;
use Anthropic\Messages\TextDelta;
header('Content-Type: text/event-stream; charset=utf-8');
header('Cache-Control: no-cache');
$stream = $client->messages->createStream(
model: 'claude-opus-5-5',
maxTokens: 64000,
messages: [['role' => 'user', 'content' => $_POST['q'] ?? '']],
);
foreach ($stream as $event) {
if ($event instanceof RawContentBlockDeltaEvent && $event->delta instanceof TextDelta) {
echo 'data: ' . json_encode(['text' => $event->delta->text]) . "\n\n";
@ob_flush();
flush();
}
}
echo 'data: ' . json_encode(['done' => true]) . "\n\n";08PHP: composer’sız (saf cURL)#
Paylaşımlı hostingde composer yoksa doğrudan HTTP isteği atabilirsin.
<?php
function claude(array $messages, string $system = ''): array {
$payload = [
'model' => 'claude-opus-5-5',
'max_tokens' => 16000,
'messages' => $messages,
];
if ($system !== '') $payload['system'] = $system;
$ch = curl_init('https://api.anthropic.com/v1/messages');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 120,
CURLOPT_HTTPHEADER => [
'content-type: application/json',
'x-api-key: ' . getenv('ANTHROPIC_API_KEY'),
'anthropic-version: 2026-10-21',
],
CURLOPT_POSTFIELDS => json_encode($payload, JSON_UNESCAPED_UNICODE),
]);
$body = curl_exec($ch);
$code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
$data = json_decode((string) $body, true) ?: [];
if ($code !== 200) throw new RuntimeException($data['error']['message'] ?? 'Claude API hatası: ' . $code);
return $data;
}
$res = claude([['role' => 'user', 'content' => 'Merhaba!']], 'Kısa ve Türkçe cevap ver.');
$text = '';
foreach ($res['content'] as $block) {
if ($block['type'] === 'text') $text .= $block['text'];
}
echo htmlspecialchars($text);09cURL ile hızlı test#
curl https://api.anthropic.com/v1/messages \
-H "content-type: application/json" \
-H "x-api-key: $ANTHROPIC_API_KEY" \
-H "anthropic-version: 2026-10-21" \
-d '{
"model": "claude-opus-5-5",
"max_tokens": 16000,
"messages": [{"role": "user", "content": "Merhaba!"}]
}'| Başlık / alan | Açıklama |
|---|---|
x-api-key | API anahtarın |
anthropic-version | API sürümü |
model | Model kimliği |
max_tokens | Cevabın en fazla uzunluğu (token) |
system | Asistanın rolü ve kuralları |
messages | Sohbet geçmişi: user ve assistant sırayla |
stream | true ise cevap olay akışı olarak gelir |
10Güvenlik, kötüye kullanım ve hatalar#
- Hız sınırı koy: IP ya da oturum başına dakikada belirli sayıda istek; yoksa biri senin faturanı şişirir.
- Girdiyi sınırla: mesaj uzunluğunu ve geçmiş sayısını kes (ör. son 20 mesaj, mesaj başına 4.000 karakter).
- Sistem talimatı: asistanın ne yapıp ne yapmayacağını
systemiçinde yaz. Kullanıcı mesajları sistem talimatını “ezmeye” çalışabilir; gizli bilgiyi zaten prompt’a koyma. - Reddetme:
stop_reasondeğeri"refusal"ise model isteği yanıtlamamıştır; kullanıcıya nazik bir mesaj göster. - Hatalar: 429 (çok istek) ve 5xx hatalarında bir süre bekleyip yeniden dene; 400 hatalarında isteği düzelt, tekrar deneme.
- Kayıt: token kullanımını (
usage.input_tokens,usage.output_tokens)ai_messagestablosuna yaz; maliyeti buradan izlersin. - Gizlilik: kullanıcıya sohbetin yapay zekâ tarafından işlendiğini söyle; kişisel veri istemeyen bir arayüz tasarla.
11Arayüz demosu#
<div class="fk-card" data-chat-demo style="width:100%;max-width:560px">
<div class="fk-chat" aria-live="polite"><div class="fk-msg fk-msg-ai">Merhaba! Ben bir demo asistanım; gerçek API’ye bağlı değilim.</div></div>
<form class="fk-input-group" style="margin-top:18px"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 3c.6 4.8 4.2 8.4 9 9-4.8.6-8.4 4.2-9 9-.6-4.8-4.2-8.4-9-9 4.8-.6 8.4-4.2 9-9Z"/></svg><input type="text" class="fk-input" placeholder="Bir şey sor…" aria-label="Mesaj" style="padding-right:56px"><button class="fk-btn fk-btn-icon" type="submit" aria-label="Gönder"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M21 3 3 10.5l7 2.5 2.5 7L21 3Z"/><path d="m10 13 4-4"/></svg></button></form>
</div>