Posts API

/api/v1/posts altındaki her şey geçerli bir API anahtarı ister ve her yazma işlemi anahtarın rolüne göre denetlenir. Bunun nasıl çalıştığını API Kimlik Doğrulama anlatıyor.

Yönetim panelindeki yazı listesi
API ile oluşturulan yazı da buraya düşer - aynı liste, aynı durumlar.

Yazıları listeleme

GET /api/v1/posts bir sayfa dolusu yazı döner. Süzgeçler sorgu dizesinde gider ve doğrudan yazı sorgusuna aktarılır; yani sessizce düşen bir parametre de, sessizce eklenen bir varsayılan da yoktur.

curl -H "X-API-Key: ANAHTARINIZ" \
  "https://siteniz.com/api/v1/posts?status=published&category=beslenme&per_page=50"

status şunlardan birini alır: published, draft, scheduled, pending, trash. type yazıyı sayfadan ayırır (post / page). category ve tag kısa ad (slug) ister; kategori süzgeci o kategorinin doğrudan alt kategorilerini de kapsar. author_id sayısal kullanıcı kimliği alır. search başlık ve içerikte arar - tam metin dizini varsa onun üzerinden, kısa sorgularda LIKE taramasına düşerek. date_from, date_to ve updated_before tarih-saat dizesi kabul eder; featured=1 öne çıkan olarak işaretlenmiş yazılara daraltır.

Sıralama order_by ve order_dir ile yapılır. Yalnız id, created_at, updated_at, published_at, title, view_count, status ve author_name kabul edilir; başka bir değer sessizce id'ye düşer. Varsayılan sıralama created_at azalan. Alt çizgiye dikkat: parametre order değil, order_dir.

Sayfalama page (1'den başlar) ve per_page ile; per_page 100'e kırpılır. with_relations=1 eklerseniz her yazının kategori ve etiketleri tek toplu sorguyla yüklenir.

Yanıt zarfı her yerdeki gibidir - data nesnesini saran bir success bayrağı:

{
    "success": true,
    "data": {
        "items": [
            {
                "id": 123,
                "title": "Proteini yüksek 10 kahvaltı",
                "slug": "proteini-yuksek-kahvaltilar",
                "status": "published",
                "type": "post",
                "excerpt": "Güne başlarken…",
                "content": "<p>…</p>",
                "featured_image": "images/2026/04/kapak-8f3a12c4.avif",
                "author_id": 1,
                "author_name": "Ada Lovelace",
                "reading_time": 6,
                "view_count": 412,
                "published_at": "2026-04-10 09:00:00",
                "created_at": "2026-04-08 11:20:00",
                "updated_at": "2026-05-02 14:22:00",
                "content_modified_at": "2026-05-02T14:22:00+03:00",
                "reviewed_at": null,
                "review_valid": true,
                "editorial": {
                    "ai_disclosure": null,
                    "original_notes": null,
                    "sources": [],
                    "ymyl": false
                }
            }
        ],
        "total": 237,
        "pages": 5,
        "current_page": 1,
        "per_page": 50
    }
}

Hangi tarih ne anlama geliyor

Her yazıyla birlikte dört zaman damgası geliyor ve bunlar birbirinin yerine geçmiyor. published_at ilk yayında bir kez basılır ve yerinde kalır. content_modified_at yalnızca okurun gördüğü içerik değiştiğinde ilerler. Site haritasındaki lastmod ile yapısal veri içindeki dateModified bu alanı okur. Dış önbellek ya da dizin eşitliyorsanız izlemeniz gereken alan da budur. reviewed_at son editöryel incelemeyi tutar ve biri yazıyı incelenmiş olarak işaretleyene kadar null kalır. updated_at ise sistem dokunuşudur: görüntülenme sayacı, önbellek yeniden üretimi ve toplu SEO işleri de onu ilerletir; "yazı değişti" göstergesi olarak zayıftır.

Bunların yanında review_valid, güncel içeriği onay anında alınan özetle karşılaştırır: true o günden beri değişiklik olmadığını, false olduğunu, null ise yazının hiç yayın onayından geçmediğini söyler. editorial bloğu yazı üzerinde saklanan yapay zekâ beyanını, özgün katkı notunu, kaynak listesini ve YMYL işaretini taşır.

Tek yazıyı okuma

GET /api/v1/posts/{id} sayısal kimlik ister. Bu uçta kısa ad çözülmez - yoldaki kısa ad 0'a dönüşür ve 404 alırsınız. Elinizde yalnız kısa ad varsa ?search= ile listeleyin ya da MCP sunucusunun get_post aracını kullanın.

GET /api/v1/posts/{id}/revisions o yazının saklanan sürüm geçmişini döner.

Yazı oluşturma

curl -X POST https://siteniz.com/api/v1/posts \
  -H "X-API-Key: ANAHTARINIZ" \
  -H "Content-Type: application/json" \
  -d '{
        "title": "Yeni yazım",
        "content": "<h2>Merhaba</h2><p>Gövde burada…</p>",
        "status": "draft",
        "categories": [3],
        "tags": ["protein", "kahvaltı"],
        "seo": { "meta_title": "…", "meta_description": "…", "focus_keyword": "proteinli kahvaltı" }
      }'

title ve content zorunludur ve ikisi de boş olmayan metin olmalıdır; eksik olan 400 ile ve hangi alanın eksik olduğu söylenerek döner. Alanın adı content_html ya da content_markdown değil, content; ama Markdown gövde de kabul edilir, çünkü gelen içerik saklanmadan önce HTML'e normalleştirilir. Ayrıca temizlenir: API güvenilmez bir kanal sayılır, betikler, olay öznitelikleri ve çalıştırılabilir adresler ayıklanır; başlık, liste, tablo, görsel, bağlantı ve video gömme olduğu gibi kalır.

Gerisi isteğe bağlı. slug vermezseniz başlıktan üretilir ve her hâlükârda benzersizleştirilir. excerpt boşsa gövdeden türetilir. status varsayılanı draft; scheduled_at yazıyı zamanlanmış duruma taşır. featured_image kayıtlı bir medya yolu alır. type post ya da page olur. visibility, password, comment_status ve is_featured kendi alanlarına yazılır.

Bu uçta kategoriler kimlik ister. "categories": [3, 7] çalışır; "categories": ["beslenme"] atılır, çünkü ilişki yazılmadan önce sayısal olmayan değerler süzülür. Etiketler daha hoşgörülüdür: adlar mevcut etiketlerle eşleştirilir, yoksa oluşturulur. Kategorileri adıyla göndermek istiyorsanız POST /api/v1/webhook/draft aksiyonunu kullanın; o, kategoriyi bulur ya da oluşturur.

seo nesnesi SEO tablosuna yazar ve meta_title, meta_description, focus_keyword, canonical_url, robots, og_title, og_description, og_image, schema_type, schema_data alanlarını kabul eder. Nesneyi hiç göndermezseniz jekcms SEO satırını içerikten kendisi doldurur.

Başarılı oluşturma yeni kimliği ve yazının tamamını döner:

{ "success": true, "data": { "success": true, "id": 812, "post": { "id": 812, "…": "…" } } }

author_id yalnızca yönetici ve editör anahtarlarından kabul edilir. Gönderen bir yazar anahtarıysa alan sökülür; imza sahteciliği böyle engellenir.

Yazı güncelleme

/api/v1/posts/{id} üzerinde PUT ve PATCH aynı işi yapar ve ikisi de kısmidir: yalnız değiştirmek istediğiniz alanları gönderin. Yanıt tazelenmiş yazıyı taşır.

curl -X PATCH https://siteniz.com/api/v1/posts/812 \
  -H "X-API-Key: ANAHTARINIZ" -H "Content-Type: application/json" \
  -d '{"status": "published"}'

Bilinmesi gereken bir davranış var: yazı zaten yayındaysa, status göndermeseniz bile her güncelleme kalite kapısından geçer. Belirleyici olan isteğe koyduğunuz alan değil, işlemin sonundaki fiilî durumdur.

Yayınlama ve zamanlama

Satırı zaten var olan yazılar için iki alt aksiyon bulunuyor:

POST /api/v1/posts/{id}/publish mevcut yazıyı yayına alır ve kapıyı çalıştırır. POST /api/v1/posts/{id}/schedule ise {"scheduled_at": "2026-06-01 09:00:00"} ile gelecekteki zamanı yazar; kapı o anda değil, zamanlayıcı yazıyı gerçekten yayına aldığında çalışır.

Kalite kapısı

Yayınlamayı amaçlayan her istek önce yayın politikasından geçer. Reddedilirse hiçbir şey yazılmaz ve yanıt 422 olur:

{
    "success": false,
    "error": "quality_gate",
    "blocks": [
        { "code": "short_content", "message_tr": "İçerik çok kısa (612/1500 karakter).", "message_en": "…" }
    ]
}

Engelleyen sebepler arasında short_content (ayarlanan asgarinin altı, varsayılan 1500 karakter), short_title, duplicate_title, similar_title, similar_content, broken_local_image ve YMYL yazılarda ymyl_no_sources ile ymyl_no_reviewer var. Kapak görselinin, iç bağlantının ve H2 alt başlığın eksikliği engel değil uyarıdır; yayını durdurmazlar.

Taslak ve zamanlanmış yazılar kapıya hiç uğramaz. "Önce taslak oluştur, düzelt, sonra yayınla" deseni otomasyonda bu yüzden güvenilir.

Yazı silme

DELETE /api/v1/posts/{id} yazıyı çöpe taşır: satır yerinde kalır, durumu trash olur ve sayfa artık sunulmasın diye önbellekler temizlenir. Gövdeye {"force": true} eklerseniz kalıcı olarak silinir.

{ "success": true, "data": { "success": true } }

Durum kodları

| Kod | Ne zaman | |---|---| | 200 | Okuma, güncelleme ya da silme başarılı | | 400 | title/content eksik, JSON gövdesi bozuk, yazı kimliği yok | | 401 | Anahtar yok, tanınmıyor, pasif ya da süresi dolmuş | | 403 | Rolün yetkisi yetmiyor ya da yazar başkasının yazısına uzanmış | | 404 | O kimlikte yazı yok | | 405 | Bu yolda bu yöntem desteklenmiyor | | 422 | Yayın kalite kapısı isteği reddetti | | 429 | IP başına saatlik hız sınırı aşıldı | | 500 | Sunucu hatası - mesajdaki referans kimliği sunucu günlüğündeki satırla eşleşir |

Hata gövdeleri standart zarfı kullanır: {"success": false, "error": {"code": …, "message": …}}. Tek istisna, yukarıdaki blocks dizisini dönen kapı reddidir.

Yeniliklerden ilk sen haberdar ol

Yeni özellikler, sürüm notları ve CMS rehberleri. Ayda birkaç e-posta gönderiyoruz.