Az első hónapból 50% kedvezmény a(z) LAUNCH50 kóddal Csak havi csomagokhoz. Kezdés most

Ingyenes próbaidőszak indítása

Kérje le a cikkeit.
Tegye közzé őket a saját módján.

Minden Önnek írt cikkünk elérhető HTTP-n keresztül: a cím, a metaadatok, a Markdown, a HTML, a kiemelt kép és a GYIK. Vegye át őket a Next.js, Astro vagy egyedi oldalába, amikor a build fut, tegye őket élesbe, és mondja meg nekünk, melyik végül milyen címen jelent meg.

Egy bearer token. JSON be, JSON ki. Nincs telepítendő SDK, és egy kulcson kívül semmit nem kell beállítani.

A webhook küld. Az API-val lekérheti.

Ugyanazt a cikket ugyanabban a formában viszik, ezért az egyikhez írt parser a másikat is változtatás nélkül beolvassa. A különbség az, hogy ki kezdi a kommunikációt.

A webhook, amikor a webhelye bármikor tud fogadni bejegyzést

Meghívjuk a végpontját, amint egy cikk elkészül, majd újra, amikor változik. Nincs mit lekérdezni, nincs mit ütemezni. WordPress, Shopify, Ghost, egy Zapier trigger vagy egy saját útvonal.

Az API, amikor a webhelye egy egységként épül fel és kerül telepítésre

Egy statikus oldal nem tud reggel fél hétkor fogadni egy bejegyzést, előbb valaminek újra kell buildelnie. Ezért a buildje megkérdezi, mi várakozik, átveszi, majd utána megadja nekünk a címet. Ön dönti el, mikor.

Mindkettő, ha szeretné

Ezek külön kapcsolatok, és egyik sem zárja ki a másikat. Teljesen megszokott felállás, hogy egy webhook a hírlevelet, egy API pedig a webhelyet látja el.

Egy kulcs, bearer tokenként elküldve.

Hozzon létre egy kulcsot a vezérlőpulton a Beállítások, majd a Kapcsolatok alatt. Csak egyszer jelenik meg, létrehozáskor, mert csak a hashét tároljuk: ha elveszíti, vonja vissza, és hozzon létre egy másikat. Egy kulcs a fiók összes oldalát olvassa, és ugyanerről a képernyőről vonható vissza.

Minden kérés

Authorization: Bearer sns_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Accept: application/json

Ellenőrizze, hogy működik

curl https://seonis.ai/api/v1/me \
  -H "Authorization: Bearer $SEONIS_API_KEY"
{
  "organisation": { "id": 12, "name": "Peppervale", "country": "GB" },
  "key": {
    "name": "Production build",
    "prefix": "sns_live_ab",
    "created_at": "2026-09-06T09:12:44+00:00",
    "last_used_at": "2026-09-06T09:31:02+00:00"
  },
  "sites": 2
}

Sebességkorlát

Percenként 120 kérés, a kulcshoz számolva, nem ahhoz a címhez, ahonnan érkezik, mert a build futtatók megosztják a címeket. E fölött 429 választ kap a szabványos Retry-After és X-RateLimit fejlécekkel. A cikkek lekérése egy buildhez csak néhány kérés, ezért ezt a korlátot nem kellene véletlenül elérnie.

Tartsa a kulcsot távol a repositoryjától

Elolvassa mindazt, amit a fiókjához írtunk, beleértve a még nem közzétett cikkeket is. Tegye a build környezetébe, ne a forráskódba. Itt azonnal vonja vissza, amint olyan helyre kerül, ahol nem szabadna lennie, és minden, ami használja, rögtön 401-et kap.

Öt van belőlük, és négy csak olvas.

Minden a /api/v1/ alatt található. A verzió az első naptól az útvonal része, így egy v2 később létezhet anélkül, hogy megtörné azt, amit ma ír.

GET /api/v1/me

Az a fiók, amelyhez a kulcs tartozik, valamint a kulcs saját neve és előtagja. Erre mindenekelőtt azért hasznos, hogy jelezze Önnek: ebben a környezetben valóban az a kulcs van, amelyre gondol.

Példa

curl https://seonis.ai/api/v1/me \
  -H "Authorization: Bearer $SEONIS_API_KEY"
GET /api/v1/sites

A fiók összes oldala. Az id alapján szűr a cikklista, a domain pedig azért van ott, hogy egy build script olyasmire illeszthessen, amit már ismer, ne kelljen id-t vinnie magával.

Példa

curl https://seonis.ai/api/v1/sites \
  -H "Authorization: Bearer $SEONIS_API_KEY"
{
  "sites": [
    {
      "id": 5,
      "name": "Peppervale",
      "domain": "peppervale.co.uk",
      "url": "https://peppervale.co.uk",
      "language": "en",
      "country": "GB",
      "market": "en-GB",
      "status": "active",
      "articles_published": 34,
      "created_at": "2026-06-02T11:04:19+00:00"
    }
  ]
}
GET /api/v1/articles

Egy oldalnyi cikk, a legfrissebb módosítással kezdve, a szöveg nélkül. A sorrend az utolsó módosítás ideje szerint van, nem az írás ideje szerint, ezért hasznos az updated_since: egy már élő cikk később is szerkeszthető, ha egy link bekerül vagy kikerül belőle.

Lekérdezési paraméterek

Név Mit csinál
site_id Egy oldal, a /sites alól. Egy olyan id, amely nincs a fiókján, 404-et ad.
status A következők egyike: draft, qa, needs_repair, review, ready, published, failed. Bármi más 422-t ad, nem üres oldalt. A "ready" az, amit egy build szeretne: megírva, ellenőrizve, és még sehol sincs kint.
updated_since ISO 8601, például 2026-09-06T00:00:00Z. Jegyezze meg az utolsó futás időbélyegét, és adja vissza a következőnél.
per_page Legfeljebb 100. Az alapértelmezett érték 25.
page 1-től. A meta.has_more jelzi, hogy kell-e újabbat kérni.

Példa

curl -G https://seonis.ai/api/v1/articles \
  -H "Authorization: Bearer $SEONIS_API_KEY" \
  -d site_id=5 \
  -d status=ready \
  -d updated_since=2026-09-01T00:00:00Z \
  -d per_page=50
{
  "articles": [
    {
      "id": 918,
      "site_id": 5,
      "language": "en",
      "status": "ready",
      "title": "How to choose a pepper grinder",
      "slug": "how-to-choose-a-pepper-grinder",
      "meta_title": "How to choose a pepper grinder | Peppervale",
      "meta_description": "Burr or blade, ceramic or steel: what matters in a grinder.",
      "meta_keywords": ["pepper grinder", "burr grinder"],
      "tags": ["Kitchen"],
      "excerpt": "A burr grinder gives an even grind; a blade one does not.",
      "key_takeaways": ["A burr grinder gives an even grind; a blade one does not."],
      "faq": [{ "question": "Ceramic or steel?", "answer": "Steel for pepper, ceramic for salt." }],
      "hero_image": { "url": "https://seonis.ai/storage/images/5/918.jpg", "alt": "A pepper grinder" },
      "infographic_url": null,
      "word_count": 1812,
      "published_url": null,
      "published_at": null,
      "updated_at": "2026-09-06T06:31:12+00:00",
      "created_at": "2026-09-05T02:14:55+00:00",
      "quality": { "seo_score": 88, "language_score": 96, "language_findings": [] }
    }
  ],
  "meta": { "page": 1, "per_page": 50, "total": 1, "last_page": 1, "has_more": false }
}
GET /api/v1/articles/{id}

Egy cikk, mindazzal, amit a lista kihagy: content_markdown, content_html, a GYIK schema.org JSON-LD formában, készen az oldalba illesztésre, valamint a cikkben lévő összes cserehivatkozás.

A mezőnevek szándékosan ugyanazok, mint amelyeket a webhookunk küld. Ha már van webhookfogadója, ugyanaz a parser ezt is beolvassa.

Példa

curl https://seonis.ai/api/v1/articles/918 \
  -H "Authorization: Bearer $SEONIS_API_KEY"
{
  "article": {
    "id": 918,
    "site_id": 5,
    "language": "en",
    "status": "ready",
    "title": "How to choose a pepper grinder",
    "slug": "how-to-choose-a-pepper-grinder",
    "meta_description": "Burr or blade, ceramic or steel: what matters in a grinder.",
    "content_markdown": "Grinders differ.\n\n## Burr or blade\n\nA **burr** grinder ...",
    "content_html": "<p>Grinders differ.</p>\n<h2>Burr or blade</h2> ...",
    "faq_schema": { "@context": "https://schema.org", "@type": "FAQPage", "mainEntity": [] },
    "hero_image": { "url": "https://seonis.ai/storage/images/5/918.jpg", "alt": "A pepper grinder" },
    "word_count": 1812,
    "published_url": null,
    "updated_at": "2026-09-06T06:31:12+00:00",
    "quality": { "seo_score": 88, "language_score": 96, "language_findings": [] },
    "exchange_links": []
  }
}
POST /api/v1/articles/{id}/published

Ön teszi közzé a cikket, itt adja meg nekünk a címet. Ez az egyetlen végpont, amely bármit megváltoztat.

Pontosan azt teszi, mint egy általunk végzett kézbesítés: a cikk published jelölést kap, beleszámít a havi keretébe, a csomagtétel lezárul, a benne lévő cserehivatkozások megkapják azt a címet, amire az ellenőrző várt, és a Facebook Page is értesítést kap, ha csatlakoztatta. E hívás nélkül a cikk örökre ready állapotban marad, és ebből semmi nem történik meg.

Példa

curl -X POST https://seonis.ai/api/v1/articles/918/published \
  -H "Authorization: Bearer $SEONIS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://peppervale.co.uk/blog/how-to-choose-a-pepper-grinder"}'

Válaszok

200 Rögzítve. A cikk az új állapotával, a published_at és a published_url értékével tér vissza.
422 Az URL hiányzik, vagy nem teljes http:// vagy https:// cím.
409 A cikk még nem áll készen a közzétételre, vagy már közzétettként van nyilvántartva. A cikk az elutasítással együtt visszakerül, így láthatja, melyikről van szó.

Kapcsolja össze a "REST API"-t közzétételi motorként.

A Beállítások, majd a Közzététel alatt van egy "REST API: Ön lekéri és közzéteszi" nevű motor. Nem kér semmit, mert nincs mit elküldenünk. Azt változtatja meg, ami minden reggel történik: a kész cikk cím nélkül marad készen, és a közzétételi képernyő azt jelzi, hogy arra vár, hogy Ön lekérje, ahelyett hogy sikertelen kézbesítést mutatna.

Az API-t csatlakoztatás nélkül is használhatja: a végpontok minden olyan fióknál működnek, amelyhez tartozik kulcs. A csatlakoztatásból tudja a termék többi része, hogy ne várjon saját címet, és ez akadályozza meg azt is, hogy az Ön által közzétett cikk hibának legyen számítva.

Minden hibaüzenet egy üzenetet tartalmazó JSON.

Mindenhol ugyanaz a forma, akár a kulcsellenőrzés, akár a validátor utasította el, így semminek nem kell kettőt feldolgoznia.

{ "message": "That API key is not valid, or it has been revoked." }
Állapot Mit jelent
401 Nincs kulcs, nem a mi kulcsunk, vagy a kulcs vissza lett vonva.
404 Nincs ilyen cikk vagy oldal ezen a fiókon. Egy másik fiók cikke 404-et ad, nem 403-at: egy API, amely azt mondja, "az nem az Öné", megerősítette, hogy a dolog létezik.
409 A kérés rendben volt, de a cikk nincs ehhez megfelelő állapotban.
422 Egy paraméter hiányzik vagy hibás. A válasz tartalmaz egy errors objektumot a mező nevével és az üzenettel.
429 Túllépte a sebességkorlátot. A Retry-After megmondja, mennyit kell várni.

Mit csinál valójában egy build lépés.

#!/usr/bin/env bash
set -euo pipefail

# 1. What is finished and not yet on the site?
ready=$(curl -sG https://seonis.ai/api/v1/articles \
  -H "Authorization: Bearer $SEONIS_API_KEY" \
  -d site_id=5 -d status=ready -d per_page=100)

for id in $(echo "$ready" | jq -r '.articles[].id'); do
  # 2. Take the article, body and all.
  curl -s https://seonis.ai/api/v1/articles/$id \
    -H "Authorization: Bearer $SEONIS_API_KEY" \
    | jq -r '.article.content_markdown' > "content/posts/$id.md"

  # 3. Your build puts it live, and you know where it landed.
  slug=$(echo "$ready" | jq -r ".articles[] | select(.id==$id) | .slug")

  curl -s -X POST https://seonis.ai/api/v1/articles/$id/published \
    -H "Authorization: Bearer $SEONIS_API_KEY" \
    -H "Content-Type: application/json" \
    -d "{\"url\": \"https://peppervale.co.uk/blog/$slug\"}"
done

Ellenőrizze, hogy a kézbesítés tőlünk érkezett.

Minden, a saját formátumunkban küldött webhook-kézbesítés tartalmaz három fejlécet a Standard Webhooks specifikációból. Ellenőrizze őket a Közzététel beállításaiban található aláírókulccsal, és utasítson el mindent, ami nem egyezik, vagy öt percnél régebbi.

webhook-id: 7c1e2f0a-3b5d-4e8f-9a61-2d4c8b0e5f13
webhook-timestamp: 1790490600
webhook-signature: v1,s0RzLJ1d7Cxd9nwe8pqER9DWy/stEIB+uT9MlGVLlM0=
Fejléc Mi ez
webhook-id A kézbesítés azonosítója, ugyanaz az érték, mint a törzsben lévő delivery_id. Jegyezze meg a már kezelt azonosítókat, és ismétlés esetén válaszoljon 200-zal anélkül, hogy újra elvégezné a műveletet.
webhook-timestamp Amikor aláírtuk, egész másodpercekben 1970 óta. Utasítson el mindent, ami a saját órájához képest öt percnél régebbi, hogy egy régi kézbesítést ne lehessen újra elküldeni Önnek.
webhook-signature v1, majd az azonosító, az időbélyeg és a nyers törzs pontokkal összefűzött értékének base64 HMAC-SHA256 aláírása, az Ön aláírókulcsával készítve. Több is lehet belőle, szóközökkel elválasztva. Fogadja el a kézbesítést, ha bármelyik egyezik.

Az aláírókulcs

A whsec_ előtaggal kezdődik. Vegye ezt le, és a többit dekódolja base64-ből: ezek a bájtok az HMAC-kulcs, nem a kimásolt szöveg. Egyszer jelenik meg, amikor csatlakoztatja a webhookot, vagy megnyomja a "New signing key" gombot a Közzététel oldalon, és soha nem küldjük el.

Ellenőrizze a nyers törzset

Az aláírást pontosan a beérkezett bájtokból számolja ki, mielőtt bármi feldolgozná őket. Az a JSON, amelyet dekódoltak, majd újra kódoltak, nem ugyanazokból a bájtokból áll, ezért az aláírása nem fog egyezni.

PHP

<?php
$raw = getenv('SEONIS_SIGNING_KEY'); // whsec_...

// No key, no deliveries. A missing or empty variable would otherwise become
// an empty HMAC key, and anybody can sign with that.
$secret = is_string($raw) && str_starts_with($raw, 'whsec_')
    ? base64_decode(substr($raw, strlen('whsec_')), true)
    : false;

if ($secret === false || strlen($secret) < 24) {
    http_response_code(500);
    exit('Signing key not configured.');
}

$id        = $_SERVER['HTTP_WEBHOOK_ID'] ?? '';
$timestamp = $_SERVER['HTTP_WEBHOOK_TIMESTAMP'] ?? '';
$header    = $_SERVER['HTTP_WEBHOOK_SIGNATURE'] ?? '';
$body      = file_get_contents('php://input');

if (! ctype_digit($timestamp) || abs(time() - (int) $timestamp) > 300) {
    http_response_code(401);
    exit('Too old.');
}

$expected = base64_encode(hash_hmac('sha256', "$id.$timestamp.$body", $secret, true));

$valid = false;
foreach (explode(' ', $header) as $signature) {
    [$version, $value] = array_pad(explode(',', $signature, 2), 2, '');
    $valid = $valid || ($version === 'v1' && hash_equals($expected, $value));
}

if (! $valid) {
    http_response_code(401);
    exit('Bad signature.');
}

$delivery = json_decode($body, true);

Node.js

import crypto from 'node:crypto';
import express from 'express';

const app = express();

// No key, no server. An empty variable would otherwise become an empty HMAC
// key, and anybody can sign with that.
const raw = process.env.SEONIS_SIGNING_KEY ?? '';
if (!raw.startsWith('whsec_')) throw new Error('SEONIS_SIGNING_KEY is not set.');
const key = Buffer.from(raw.slice('whsec_'.length), 'base64');
if (key.length < 24) throw new Error('SEONIS_SIGNING_KEY is not a valid key.');

// express.raw keeps the body as the bytes we signed.
app.post('/hooks/seonis', express.raw({ type: 'application/json' }), (req, res) => {
  const id = req.get('webhook-id') ?? '';
  const timestamp = req.get('webhook-timestamp') ?? '';
  const header = req.get('webhook-signature') ?? '';

  if (!/^\d+$/.test(timestamp) || Math.abs(Date.now() / 1000 - Number(timestamp)) > 300) {
    return res.status(401).send('Too old.');
  }

  const expected = crypto.createHmac('sha256', key)
    .update(`${id}.${timestamp}.`)
    .update(req.body)
    .digest();

  const valid = header.split(' ').some((signature) => {
    const [version, value = ''] = signature.split(',');
    const given = Buffer.from(value, 'base64');
    return version === 'v1' && given.length === expected.length && crypto.timingSafeEqual(given, expected);
  });

  if (!valid) return res.status(401).send('Bad signature.');

  const delivery = JSON.parse(req.body);
  res.json({ ok: true });
});

Vagy használjon könyvtárat

A hivatalos Standard Webhooks könyvtárak PHP-hoz, JavaScripthoz, Pythonhoz, Go-hoz, Rubyhoz és más nyelvekhez mindezt egyetlen hívással elvégzik. Adja meg nekik az aláírókulcsot pontosan úgy, ahogy megjelent Önnek, a whsec_ előtaggal együtt. github.com/standard-webhooks

A régebbi fejlécek

A kézbesítések a bearer tokenként használt aláírási titkot is tartalmazzák, valamint az X-AutoSEO-LT-Signature fejlécet, amely csak a törzs HMAC-je ezzel a titokkal készítve. Ezeket továbbra is küldjük, hogy a rájuk épülő végpontok működjenek. Az új végpont inkább a webhook-signature fejlécet ellenőrizze: a régi aláírás nem tartalmaz időt és azonosítót, a kulcsa pedig minden kérésben utazik.

Az AutoSEO, Outrank és BabyLoveGrowth formátum pontosan azt küldi, amit ezek a szolgáltatások küldtek, ezért ezek nem tartalmazzák ezeket a fejléceket.

Hozzon létre egy kulcsot, és próbálja ki.

A próbaidőszak elég az API olvasásához: az ezalatt írt cikkek valódi cikkek, és ezeken a végpontokon ugyanúgy visszajönnek, mint bármely másik.

Ingyenes próbaidőszak indítása