---
title: "EVREN API'sine PHP ile İlk İstek: Şart Onayı ve 403 Tuzağı"
url: "https://cilginyazilim.com/blog/evren-api-php-ile-ilk-istek"
description: "EVREN API'sine PHP ile istek: cURL istemcisi, kullanım şartı onayı ve 403 terms_not_accepted, auto modelin kimliği, günlük kota ve 429'da bekleme."
published: "2026-10-12T10:00:00+03:00"
modified: "2026-10-12T10:00:06+03:00"
author: "Çılgın Yazılım"
category: "Web Geliştirme ve Frameworkler"
tags: ["php", "api", "entegrasyon", "llm", "sır yönetimi", "hız sınırlama"]
site: "Çılgın Yazılım"
language: "tr"
---

# EVREN API'sine PHP ile İlk İstek: Şart Onayı ve 403 Tuzağı

EVREN'in auto kipi cevap verecek modeli isteğe ve filo yüküne göre seçer; seçileni yanıttaki tek bir alan söyler. 403 ve 429 dahil, cURL ile tek dosyalık PHP istemcisi.

EVREN'in büyük dil modeli uç noktası OpenAI'nin REST sözleşmesini uyguladığı için EVREN API'sine PHP ile istek atmak yeni bir kütüphane istemez: curl eklentisiyle yazılmış tek dosyalık bir istemci yeter. Anahtarı alıp doğrudan sohbet ucunu çağıran kod ise `403 terms_not_accepted` alır, çünkü önce kullanım şartlarının onaylanması gerekir. Adımların sonunda bu onayı yapan bir betik, `auto` isteğine hangi modelin cevap verdiğini gösteren ilk çağrı ve 429'da ne zaman bekleneceğini bilen bir fonksiyon elinizde olacak. Gerekenler: PHP 8.1 ya da üstü, curl ve mbstring eklentileri ve EVREN hesabınızdan alınmış bir anahtar.

![EVREN API'sine PHP ile ilk istekte anahtar, şart onayı, auto model ve kota adımlarını özetleyen kart](https://cilginyazilim.com/uploads/blog/2026/10/evren-api-php-ile-ilk-istek-ozet.png)
*Şart onayı atlanırsa doğru anahtarla atılan istek de 403 alır.*

## Adım 1: Anahtarı ortam değişkenine koyun

Anahtar EVREN panelinin LLM Çıkarım menüsündeki API Anahtarları sayfasında (`/api-keys`) üretilir, `evren_llm_` ile başlar ve oluşturulduğu anda bir kez gösterilir. Platformun API belgesi aynı menüdeki Modeller ve API sayfasıdır (`/llm/models`, giriş ister); yazıda belge denince bu sayfa kastedilir. Kodun içine yazılan anahtar Git geçmişine ve yedeklere de girer, bu yüzden istemci anahtarı `EVREN_API_KEY` ortam değişkeninden okuyacak. Anahtar kopyalarının nasıl izleneceğini [.env dosyasından sır kasasına geçiş yazımız](https://cilginyazilim.com/blog/sirlarin-yonetimi-env-dosyasindan-sir-kasasina) anlatıyor. PowerShell'de anahtarı oturuma tanımlayın ve PHP'nin hem curl eklentisini hem değişkeni gördüğünü tek komutla doğrulayın:

```powershell
$env:EVREN_API_KEY = 'evren_llm_...'
C:\xampp\php\php.exe -r "var_dump(extension_loaded('curl'), getenv('EVREN_API_KEY') !== false);"
```

Çıktıda iki kez `bool(true)` görünmelidir. `php.exe` PATH'te olmayabileceği için komut XAMPP'teki tam yolu kullanır. Tek tırnak, anahtarda `$` varsa PowerShell'in onu değişken sanmasını önler. Değişken o PowerShell penceresi kapanınca silinir. Git Bash'te karşılığı `export EVREN_API_KEY='evren_llm_...'` satırıdır. CodeIgniter 4 uygulamasında aynı değeri `.env` dosyasına `EVREN_API_KEY = evren_llm_...` diye yazın: CI4 `.env` değerlerini `putenv()` ile ortama koyar ve istemcideki `getenv()` onları okur.

> Anahtar sızdıysa API Anahtarları sayfasındaki yenileme yeni bir gizli anahtar üretir ama eskisini hemen kapatmaz: liste eski anahtarın kaç dakika sonra devre dışı kalacağını gösterir ve sızan anahtar bu sürede istek atabilir. Aynı satırda anahtarı iptal etmek için ayrı bir düğme vardır. Yenileme o günkü token kullanımını sıfırlamaz.

## Adım 2: Tek dosyalık cURL istemcisini yazın

İstemcinin üç işi vardır: her isteğe anahtarı eklemek, yanıtı JSON olarak çözmek ve HTTP hatasını ayrıntılı bir istisnaya çevirmek. Üç parça aynı `evren.php` dosyasına sırayla yazılır. Python kullanan biri aynı bağlantıyı resmî `openai` paketine `base_url` ve `api_key` vererek kurar (belgedeki örnek); PHP tarafında curl yeter.

### Taban adres ve hata sınıfı

Bütün uçlar `https://evren-llmapi.ssyz.org.tr/v1` altındadır. İstisna sınıfı HTTP kodunu, hata kodunu ve yanıt başlıklarını taşır. 6. adımdaki kota kararı bu başlıklara bakar.

```php
<?php
// evren.php: EVREN API istemcisi (PHP 8.1+, curl ve mbstring eklentileri)
const EVREN_TABAN = 'https://evren-llmapi.ssyz.org.tr/v1';

final class ApiHatasi extends RuntimeException
{
    public function __construct(
        string $mesaj,
        public readonly int $http = 0,          // 0: HTTP yanıtı hiç gelmedi
        public readonly string $hataKodu = '',  // error.code, yoksa error.type
        public readonly array $basliklar = [],  // kota kararı için yanıt başlıkları
    ) {
        parent::__construct($mesaj);
    }
}
```

### Kimlik başlığı ve zaman aşımı

Belge kimlik için iki başlığı da kabul eder: ilk çağrıya kadarki dört adım ve uçtan uca örnek `X-API-Key`, Desteklenen uçlar kartlarındaki curl örnekleri `Authorization: Bearer` gönderir. İstemci Bearer kullanır: resmî SDK'ların kendiliğinden gönderdiği başlık budur, ileride SDK'ya geçildiğinde sunucuya giden kimlik bilgisi aynı kalır.

```php
// Yanıt başlıkları da toplanır: 429'da beklemek mi durmak mı, kalan token başlığı söyler.
function istek_secenekleri(string $yontem, ?array $govde, int $sure, array &$basliklar): array
{
    $anahtar = getenv('EVREN_API_KEY') ?: throw new ApiHatasi('EVREN_API_KEY ortam değişkeni boş');
    $secenek = [
        CURLOPT_CUSTOMREQUEST  => $yontem,
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_CONNECTTIMEOUT => 10,
        CURLOPT_TIMEOUT        => $sure,
        CURLOPT_HTTPHEADER     => ['Authorization: Bearer ' . $anahtar, 'Content-Type: application/json'],
        CURLOPT_HEADERFUNCTION => function ($ch, string $satir) use (&$basliklar): int {
            if (str_contains($satir, ':')) {
                [$ad, $deger] = explode(':', $satir, 2);
                $basliklar[strtolower(trim($ad))] = trim($deger);
            }
            return strlen($satir);
        },
    ];
    if ($govde !== null) {
        $secenek[CURLOPT_POSTFIELDS] = json_encode($govde, JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR);
    }
    return $secenek;
}
```

[PHP kılavuzuna göre](https://www.php.net/manual/en/curl.constants.php#constant.curlopt-timeout) `CURLOPT_TIMEOUT` varsayılan olarak 0'dır, yani yanıt gelmezse aktarım hiç zaman aşımına uğramaz. Bağlanma beklemesinin varsayılanı da 300 saniyedir. Sohbet yanıtının süresi modele ve `max_tokens` değerine göre değiştiği için toplam süre parametre oldu, bağlanma 10 saniyeyle sınırlandı.

### Yanıtı çözmek ve hatayı ayırmak

Son parça isteği gönderir ve üç arıza yolunu ayırır: hiç yanıt gelmemesi, JSON olmayan gövde ve 4xx ya da 5xx durum kodu.

```php
function evren(string $yontem, string $yol, ?array $govde = null, int $sure = 120): array
{
    $basliklar = [];
    $ch = curl_init(EVREN_TABAN . $yol);
    curl_setopt_array($ch, istek_secenekleri($yontem, $govde, $sure, $basliklar));
    $ham = curl_exec($ch);
    if ($ham === false) {
        throw new ApiHatasi('Bağlantı hatası: ' . curl_error($ch));
    }
    $http = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
    $veri = json_decode($ham, true);
    if (! is_array($veri)) {   // boş gövde, yarım JSON ya da aradaki bir vekil sunucunun HTML sayfası
        throw new ApiHatasi("HTTP $http, JSON olmayan yanıt: " . mb_strcut($ham, 0, 120, 'UTF-8'), $http, '', $basliklar);
    }
    if ($http >= 400) {
        $hata = $veri['error'] ?? [];
        $kod = (string) ($hata['code'] ?? $hata['type'] ?? '');
        $mesaj = is_string($hata['message'] ?? null) ? $hata['message'] : mb_strcut($ham, 0, 200, 'UTF-8');
        throw new ApiHatasi(trim("HTTP $http $kod") . ': ' . $mesaj, $http, $kod, $basliklar);
    }
    return $veri;
}
```

Hata kodu önce `error.code`, yoksa `error.type` alanından okunur. Gövdede `error.message` yoksa ya da metin değilse mesajın yerine ham gövdenin ilk 200 baytı yazılır; `mb_strcut()` Türkçe bir harfi ortadan bölmeden keser. Belge hata gövdesinin OpenAI sözleşmesine uyduğunu söyler ama 403 gövdesinin alanlarını tek tek göstermez.

## Adım 3: Kullanım şartlarını API'den onaylayın

Şart onayı üç çağrıdır: `GET /v1/terms/status` yanıtındaki `current_version` okunur, `GET /v1/terms/text` ile metin alınır ve aynı sayı `POST /v1/terms/accept` gövdesinde `{"version": N}` olarak geri gönderilir. Belgeye göre bu adım **hesap başına bir kez** gerekir, atlanırsa sohbet çağrısı `403 terms_not_accepted` döner. `sartlar.php` metni dosyaya yazar ve onayınızı bekler:

```php
<?php
require __DIR__ . '/evren.php';

$durum = evren('GET', '/terms/status');
$surum = $durum['current_version'];
if ($durum['accepted']) {
    exit("Şartlar zaten onaylı (sürüm $surum).\n");
}
$metin = evren('GET', '/terms/text');
file_put_contents("sartlar-v$surum.md", $metin['content']);
echo "Şartlar sartlar-v$surum.md dosyasına yazıldı. Okuyup onaylıyorsanız evet yazın: ";
if (trim((string) fgets(STDIN)) !== 'evet') {
    exit("Onay verilmedi, sohbet istekleri 403 ile dönmeye devam eder.\n");
}
$kabul = evren('POST', '/terms/accept', ['version' => $surum]);
echo "Onaylandı: sürüm {$kabul['accepted_version']}, {$kabul['accepted_at']}\n";
```

**Sürüm numarası koda yazılmaz**, `status` yanıtından gelir. Belgeye göre onay, yeni esaslı bir sürüm yayınlanana kadar geçerlidir. O gün betik yeni sayıyı kendisi gönderir, sohbet kodunuz ise 403'ü yeniden görebilir; 5. adımdaki kod bu yüzden hatayı yakalar. Panelin Test Alanı'ndaki anahtar kutusu da belgeye göre bu üç adımı tek tıkla yapar.

## Adım 4: Hesabınıza tanımlı modelleri listeleyin

`GET /v1/models` hesabınıza tanımlı modellerin kimliklerini döndürür. `model` alanına yazılacak değer bu listeden gelir.

```php
<?php
require __DIR__ . '/evren.php';

foreach (evren('GET', '/models')['data'] as $model) {
    echo str_pad($model['id'], 24), implode(', ', $model['modalities'] ?? []), "\n";
}
```

Belgedeki örnek yanıtta `glm-5.3`, `deepseek-v4.1-flash` ve `auto` görünür. Her kaydın `pricing` alanı token fiyatını ve `free_until` tarihini taşır. Bu kimliklerin arkasındaki açık ağırlıklı modelleri ve platformun yapısını [EVREN yapay zekâ platformunu anlatan yazımızda](https://cilginyazilim.com/blog/evren-yapay-zeka-platformu-nedir) ele aldık.

## Adım 5: auto ile ilk sohbeti gönderin ve modeli okuyun

`auto` belirli bir model sayılmaz, geçidin yönlendirme kipidir. EVREN'in [herkese açık LLM çıkarım sayfasına](https://evren.ssyz.org.tr/llm-inference/) göre isteğin yeteneğini, bağlamını ve anlık filo yükünü değerlendirip uygun modeli seçer. Belgedeki örnekte `auto` isteğinin yanıtında `model` alanı `zai/glm-5.3-fp8` yazar; cevabı veren model bu yüzden bu alandan okunur.

```php
<?php
require __DIR__ . '/evren.php';

try {
    $yanit = evren('POST', '/chat/completions', [
        'model'      => 'auto',
        'messages'   => [['role' => 'user', 'content' => "PHP'de bir dizinin son elemanını nasıl alırım?"]],
        'max_tokens' => 4096,
    ]);
} catch (ApiHatasi $e) {
    if ($e->http === 403) {   // belge bu durumu "403 terms_not_accepted" diye anar, gövde alanlarını vermez
        exit($e->getMessage() . "\nŞartlar onaylanmamış olabilir: php sartlar.php\n");
    }
    throw $e;
}
$secim = $yanit['choices'][0];
echo "Yanıtlayan model: {$yanit['model']} · bitiş: {$secim['finish_reason']}\n";
echo $secim['message']['content'] ?? '', "\n";
$u = $yanit['usage'];
echo "Token: {$u['prompt_tokens']} girdi + {$u['completion_tokens']} çıktı = {$u['total_tokens']}\n";
```

Şart dalı hata koduna değil HTTP 403'e bakar, çünkü 403 gövdesinin hangi alanda `terms_not_accepted` taşıdığı belgede yazmaz; sunucunun metni ipucundan önceki satır ya da satırlarda görünür (uzun gövdenin yalnız başı). `max_tokens` 4096 verildi, çünkü `auto` düşünen bir modele de gidebilir. Belge DeepSeek ve Qwen gibi akıl yürüten modellerde bu sınırın düşünme ile cevap arasında paylaşıldığını yazar ve en az 2048, tercihen 4096 önerir. Bitiş nedeni `length` ise yanıt bu sınırda kesilmiştir. Aynı istem farklı saatte başka bir modele gidebileceği için `model` ve `usage` alanlarını istekle birlikte günlüğe yazmak işinize yarar.

Belirli bir modelle çalışmak için `auto` yerine kısa kimliği (`glm-5.3`) ya da tam repo adını (`zai/glm-5.3-fp8`) yazın; belge ikisini de destekler. Bu istemci yanıtı tek parça bekler. Gövdeye `"stream": true` eklenirse sunucu SSE akışı döner ve `evren()` onu JSON olmayan yanıt diye reddeder; akış için yanıtı parça parça okuyan ayrı bir kod (ör. `CURLOPT_WRITEFUNCTION`) ya da bir SDK'nın akış desteği gerekir.

## Adım 6: 429 ve 503'te ne zaman beklenir, ne zaman durulur

**429 geldiğinde kalan token başlığı 0 ise durun**, değilse en fazla 60 saniye bekleyip yeniden deneyin. 503'te kısa bir bekleme yeter. Belgeye göre 429 hesap kotasını, 503 sunucunun anlık doluluğunu bildirir ve dakikalık sınırda bekleme en fazla 60 saniye sürer. Günlük kota bütün modellerin ve bütün anahtarlarınızın toplamıdır, yeni anahtar onu artırmaz. Ekim 2026 itibarıyla herkese açık LLM çıkarım sayfası bu kotayı 1 Kasım 2026'ya kadar 15 milyon token olarak gösteriyor, her gün 03:00'te (TSİ) yenilendiğini yazıyor ve hız sınırını da HTTP 429 diye anıyor. Kalan token her yanıtın `x-evren-daily-remaining-tokens` başlığında da gelir, bu yüzden başlık 0 iken beklemek sonucu değiştirmez. Başlığın biçimi belgede yazmadığı için aşağıdaki fonksiyon orada sayı göremezse dakikalık sınır varsayar ve en fazla üç kez dener. Fonksiyonu `evren.php` dosyasının sonuna ekleyin:

```php
// 429: günlük kota ya da dakikalık sınır; 503: sunucu anlık dolu. Öteki hatalar yeniden denenmez.
function evren_dayanikli(string $yontem, string $yol, ?array $govde = null, int $enFazla = 3): array
{
    for ($deneme = 1; ; $deneme++) {
        try {
            return evren($yontem, $yol, $govde);
        } catch (ApiHatasi $e) {
            $kalan = $e->basliklar['x-evren-daily-remaining-tokens'] ?? null;
            if ($e->http === 429 && is_numeric($kalan) && (int) $kalan <= 0) {
                throw new ApiHatasi('Günlük kota bitti, 03:00 (TSİ) sonrası yenilenir', 429, 'kota', $e->basliklar);
            }
            if (! in_array($e->http, [429, 503], true) || $deneme >= $enFazla) {
                throw $e;
            }
            sleep($e->http === 429 ? 60 : 5 * $deneme);   // belge: dakikalık sınırda bekleme en fazla 60 sn
        }
    }
}
```

`sleep()` çağrıldığı süreci bekletir. Ziyaretçiye dönen bir sayfada 60 saniye beklemek yerine işi kuyruğa alın. Herkese açık LLM çıkarım sayfasına göre aynı anahtarla en fazla 32 istek aynı anda etkin olabilir, kuyruk işçilerinin sayısı bunu aşmamalı. Kuyruk ve devre kesici gibi genel önlemleri [üçüncü parti API dayanıklılığı yazımızda](https://cilginyazilim.com/blog/ucuncu-parti-api-entegrasyonunda-dayaniklilik) anlattık. Kalan kotayı ve sıfırlanma zamanını `GET /v1/quota` da verir; belge bu ucun yanıt örneğini yayımlamadığı için fonksiyon başlığa bakar.

## Çıktıdan kurulumun hangi adımda kaldığını bulun

Üç betik sorunsuz çalıştığında `ilk.php` çıktısının son satırı `Token: … girdi + … çıktı` olur. Bir yerde durursa istemcinin yazdığı satır dönülecek adımı gösterir.

### Hata satırı ve dönülecek adım

Tablodaki her satır istemcinin kendi mesajıyla başlar:

| Çıktı | Anlamı | Dönülecek yer |
| --- | --- | --- |
| `EVREN_API_KEY ortam değişkeni boş` | PHP anahtarı görmüyor | Adım 1, aynı terminal penceresinde |
| `HTTP 403` ve `Şartlar onaylanmamış olabilir` | Şartlar onaylanmamış ya da yeni esaslı sürüm çıkmış; başka bir neden varsa sunucunun metni ipucundan önceki satırlardadır | Adım 3 |
| `HTTP 429` / `HTTP 503` | Kota, dakikalık sınır ya da anlık doluluk | `evren_dayanikli()` (Adım 6); `Günlük kota bitti` derse 03:00 (TSİ) sonrası |
| `JSON olmayan yanıt` | Gövde boş, yarım, araya giren bir vekil sunucunun HTML sayfası ya da istekte `"stream": true` | Taban adres, ağ çıkışı, istek gövdesi |
| `Bağlantı hatası: … timed out` | Yanıt süre içinde gelmedi | `$sure` ya da `max_tokens` |
| `Bağlantı hatası: SSL certificate problem` | php.ini'de CA dosyası tanımlı değil ya da tanımlı dosyada sunucunun kök sertifikası yok | php.ini'deki `openssl.cafile` ya da `curl.cainfo` satırı |

### Sertifika hatası ve öteki 4xx kodları

PHP'nin curl eklentisi CA dosyasını önce `openssl.cafile`, o boşsa `curl.cainfo` satırından alır ([eklentinin kaynak kodu](https://github.com/php/php-src/blob/PHP-8.2/ext/curl/interface.c)). XAMPP 8.2.12 kurulumunda ikisi de `apache\bin\curl-ca-bundle.crt` dosyasını gösterir; ikisi de boşsa aynı HTTPS isteği sertifika hatasıyla durur. Başka bir 4xx kodunda istemci sunucunun metnini yazar. HTTP 401 kimlik bilgisinin kabul edilmediğini söyler, bakılacak yer anahtardır: yenilenen bir anahtarın eskisi kısa süre sonra devre dışı kalır. 400 ya da 404'te model kimliğini 4. adımdaki listeyle karşılaştırın.

**Test ortamı:** Kod PHP 8.2.12 (libcurl 8.4.0), CodeIgniter 4.7.4, Windows 10, PowerShell 5.1 ve Git Bash ile, belgedeki istek–yanıt örneklerini taklit eden yerel bir sunucuya karşı denendi; gerçek EVREN uç noktasına anahtarla istek atılmadı. HTTPS ve CA dosyası davranışı herkese açık bir adrese (`repo.packagist.org`) atılan GET isteğiyle denendi. Uç nokta adları, kota, eşzamanlılık ve hata kodları Modeller ve API sayfasına ve herkese açık LLM çıkarım sayfasına (Ekim 2026) göre yazıldı.

---

Anahtarı aldığınız gün terminalde önce `sartlar.php` betiğini çalıştırın; çıktıda `Onaylandı: sürüm …` ya da `Şartlar zaten onaylı` satırı görünmelidir. Ardından gelen `ilk.php` çıktısında sonraki kararı `Yanıtlayan model` satırı verir. Orada yazan kimlik birkaç denemede aynı kalıyor ve cevaplar işinize uyuyorsa `auto` yerinde kalabilir. Kimlik denemeden denemeye değişiyorsa ya da cevabın biçimi her seferinde aynı olmalıysa o kimliği `model` alanına sabitleyin. Bundan sonra cevabı her istekte sizin seçtiğiniz model üretir.

## Sıkça Sorulan Sorular

### EVREN API'si ücretli mi?

Ekim 2026 itibarıyla hayır: platform 1 Kasım 2026'ya kadar bütün çıkarım isteklerini kredi bakiyesinden bağımsız ve ücretsiz sunuyor. Günlük 15 milyon token kotası da bu tarihe kadar tanımlı. /v1/models yanıtındaki pricing alanı her modelin token fiyatını ve free_until tarihini gösterir.

### PHP için OpenAI SDK'sı EVREN ile çalışır mı?

Çalışması gerekir: EVREN Bearer başlığını ve OpenAI sözleşmesini kabul eder, topluluğun openai-php/client paketi (0.21.0) de taban adresi withBaseUri() ile değiştirip anahtarı Bearer başlığıyla gönderir. Belge PHP örneği vermez; bu yol taklit sunucumuza karşı denendi ve sohbet isteği aynı yanıtı verdi.

### EVREN API anahtarımı kaybettim, yeniden görebilir miyim?

Hayır. Anahtar oluşturulduğu anda bir kez gösterilir, API Anahtarları sayfası onu bir daha göstermez. Kayıp anahtarı yenilerseniz eskisi kısa süre sonra devre dışı kalır ve onunla çalışan uygulamalara yeni değeri girmeniz gerekir. Yeni anahtar oluşturmak günlük kotayı artırmaz.

### OpenAI SDK'sında base_url ortam değişkeniyle verilebilir mi?

Python SDK'sında evet: base_url verilmezse OPENAI_BASE_URL, api_key verilmezse OPENAI_API_KEY ortam değişkeni okunur; ikisine EVREN'in adresini ve anahtarını yazmak yeter. openai-php/client ortamı kendisi okumaz, adres withBaseUri() ile kodda verilir; değeri getenv() ile ortamdan almak size kalır.

---

Kaynak: [EVREN API'sine PHP ile İlk İstek: Şart Onayı ve 403 Tuzağı](https://cilginyazilim.com/blog/evren-api-php-ile-ilk-istek)
