OxiRanker-dokumentaatio
Selkeä dokumentaatiokeskus verkkosivustosi yhdistämiseen OxiRanker-palveluihin. Aloita WordPress-integraatiosta tai API-integraatiosta ja seuraa sitten opasta vaihe vaiheelta.
API-palvelu
Käytä Export API:a ja Webhookia mukautetuille verkkosivustoille, mukautetuille CMS-alustoille sekä Next.js-, Laravel-, Node.js-, PHP- tai Python-backendeille.
OxiRanker ei ole pysyvä hosting kohdesivustolle
OxiRanker ei anna pysyvän hostingin takuuta kohdesivuston sisällölle, kuville tai viedyille tiedostoille. OxiRanker ei ole käyttäjäsisällön hosting-palvelu, ja luotu sisältö, kansikuvat, Open Graph -kuvat tai tulostetiedostot eivät välttämättä säily OxiRankerin infrastruktuurissa yli 90 päivää.
Siksi tulosteen vastaanottamisen jälkeen kohdesivuston on tallennettava lopullinen artikkeli, kuvat, SEO-data, Open Graph -data ja Schema-data omaan tietokantaansa, palvelimeensa, tallennustilaansa tai CDN:äänsä, eikä sen pidä luottaa OxiRanker-URL-osoitteisiin julkisessa näytössä.
Yhteyden koulutusvideo
Käytä tätä videota visuaalisena läpikäyntinä ennen WordPressin yhdistämistä tai API-integraation toteuttamista.
Toteuta tulostevirta vaihe vaiheelta
Tallenna ensin Export Token ja Webhook Secret backendissäsi ja viimeistele sitten Webhook receiver, tietokanta, UPSERT-logiikka, Image Mirroring ja päivittäinen sync.
Rekisteröidy ja luo domain OxiRankerissa
Rekisteröidy ensin OxiRankeriin, kirjaudu tilillesi ja klikkaa Domains-osiosta Add Domain.
Oikea domain-muoto
Syötä Domain name -kenttään vain juuridomain. Osoitteessa ei saa olla http, https tai www.
example.comVäärät muodot
Älä syötä näitä muotoja domain-kenttään.
https://example.com
http://example.com
www.example.comVahvista domainin omistus
Tässä vaiheessa sinun on osoitettava, että domain kuuluu sinulle tai että sinulla on pääsy sen hallintaan.
DNS TXT
SuositeltuLisää TXT record domainin DNS:ään. Cloudflarella tämä on yleensä yksinkertaisin menetelmä.
DNS Name:
_yourdomain-verification.example.com
TXT Value:
acb1****29803HTTP File
Aseta tekstitiedosto verkkosivustosi .well-known-polkuun, jotta OxiRanker voi vahvistaa sen.
File URL:
https://example.com/.well-known/yourdomain-verification.txt
File Content:
220daaa37****42730f27Meta Tag
Aseta meta tag verkkosivustosi etusivun head-osioon.
<meta name="yourdomain-verification" content="190208d****d275b65" />Vastaanota ilmaisia tokeneita ensimmäisen domainin vahvistamisen jälkeen
Ensimmäisen domainin vahvistamisen jälkeen OxiRanker lisää tilillesi 1000 ilmaista tokenia alkutestausta varten.
Määritä kielet ja Blog URL
Blog URLs -osiossa määritä, onko verkkosivustosi yksikielinen vai monikielinen ja mitä kieliä luoduissa artikkeleissa käytetään.
Valitse kielet
OxiRanker tukee 24 kieltä. Voit valita yhden tai useamman kielen verkkosivustosi rakenteen mukaan.
Syötä Blog URL
Syötä jokaiselle kielelle kyseisen kielen blogin URL. Jos verkkosivustosi on monikielinen, syötä jokaisen kielen polku erikseen.
https://example.com/fa/blog
https://example.com/en/blogLisää avainsanat jokaiselle kielelle
Lisää Keywords-välilehdellä jokaiselle kielelle vähintään 3 ja enintään 50 avainsanaa.
Pääsääntö
Jokaisen kielen avainsanat on syötettävä samalla kielellä. Jos artikkelin kieli on englanti, myös avainsanojen on oltava englanniksi.
Kuinka avainsanoja lisätään
Kirjoitettuasi jokaisen avainsanan paina Enter lisätäksesi sen. Kun kaikki avainsanat ovat valmiit, klikkaa Save.
Täydennä domainin Article Profile
Article Profile kertoo OxiRankerille, mikä brändisi on, kenelle se kirjoittaa, mitä sävyä sen tulee käyttää ja mitä aiheita sen tulee välttää.
Toimiala
Määrittää markkinan tai liiketoimintakategorian. Esimerkki: kiinteistökonsultointi sekä kiinteistöjen osto ja myynti Dubaissa.
Liiketoiminnan kuvaus
Selittää, mitä yritys myy, ketä se palvelee, miten se toimii ja mikä tekee siitä erilaisen.
Yleisö
Määrittää, kenelle sisällön tulee puhua. Jokainen yleisö voidaan tallentaa erikseen.
Sävy
Ohjaa luodun sisällön tyyliä ja ääntä, kuten muodollinen, opettavainen, yksinkertainen, tekninen tai myyntiorientoitunut.
Sisällön tavoite
Määrittää, mitä artikkeleiden tulee saavuttaa, kuten asiakkaiden houkutteleminen, liikenteen kasvattaminen tai käyttäjien kouluttaminen.
Kielletyt aiheet
Kaikki, mistä brändin ei pitäisi puhua tai mihin sitä ei pitäisi yhdistää, syötetään tähän.
Valinnainen CTA
CTA tarkoittaa toimintoa, jonka haluat lukijan tekevän artikkelin lukemisen jälkeen. Jos et ole varma sopivasta tekstistä, tämä kenttä on parempi jättää tyhjäksi.
Sisäinen ja ulkoinen linkitys
OxiRanker voi älykkäästi lisätä sisäisiä ja ulkoisia linkkejä artikkeleihin.
Sisäiset linkit
Tämä vaihtoehto on oletuksena käytössä, ja se kannattaa pitää käytössä. Sisäinen linkitys auttaa hakukoneita ymmärtämään verkkosivustosi sisältörakenteen paremmin.
Ensimmäisessä artikkelissa sisäisiä linkkejä ei yleensä luoda, koska aiempaa artikkelia ei vielä ole. Toisesta artikkelista eteenpäin järjestelmä voi tehdä sisäistä linkitystä.
Ulkoiset linkit
Ulkoisia linkkejä lisätään virallisiin ja luotettaviin lähteisiin, ja ne merkitään nofollow-muotoon, jotta SEO-auktoriteetti ei siirry tahattomasti.
Tarkista profiilin laatu AI:n avulla
Article Profilen täydentämisen jälkeen OxiRanker tarkistaa profiilin laadun.
Yhdistä Telegram artikkeliyhteenvetoja varten
Kun profiili on tallennettu onnistuneesti, voit ottaa käyttöön artikkeliyhteenvetojen lähettämisen Telegram-kanavalle.
Lisää botti
Lisää @Oxiranker_Bot Telegram-kanavallesi ja tee siitä Admin.
Syötä kanavan käyttäjänimi
Syötä kanavan nimi @-merkin kanssa. Esimerkki: @my_channel
Ota yhteenvedon toimitus käyttöön
Kun kelvollinen kanava on rekisteröity, ota artikkeliyhteenvedon toimitus käyttöön.
Määritä automaattinen artikkelimoottori
Automatic article generation -välilehdellä voit hallita automaattista artikkelien luontimoottoria.
Älykäs aiheen valinta
Järjestelmä käyttää avainsanoja ja Article Profilea valitakseen sopivia, uniikkeja ja julkaisukelpoisia aiheita.
Bränditietoinen luonti
Sävy, yleisö, sisällön tavoite ja brändirajoitukset otetaan huomioon artikkelin luonnin aikana.
Ajastettu julkaisu
Kuukausikapasiteetti jaetaan kuukauden ajalle, jotta sisällön julkaisu näyttää luonnollisemmalta ja johdonmukaisemmalta.
Kuukausikapasiteetti
7 articles / month
10 articles / month
15 articles / month
30 articles / month30 artikkelia tarkoittaa lähes yhtä artikkelia päivässä. 15 artikkelia tarkoittaa lähes joka toinen päivä. 10 artikkelia tarkoittaa noin kahden tai kolmen päivän välein. 7 artikkelia tarkoittaa noin kolmen tai neljän päivän välein.
Artikkelin pituus ja kuva
Artikkelin pituus voi olla Short, Standard tai Long. Standard on tasapainoinen valinta useimmille verkkosivustoille.
Artikkelikuvat voidaan ottaa käyttöön tai poistaa käytöstä, ja kuvan teema voi olla Light tai Dark.
Luo ensimmäinen artikkeli Generate-osiosta
Kun domain on valmisteltu, avaa Generate-valikko, valitse domain ja klikkaa New Content tai New Article.
Kieli
Jokaiselle sisältöpyynnölle valitaan vain yksi kieli.
Aihe
Kirjoita selkeä aihe. Source URL on valinnainen.
Kuva
Ota kuva käyttöön tai poista se käytöstä ja valitse Light- tai Dark-teema.
Maksu ja luonti
Kustannus vähennetään walletista ja artikkelin luonti alkaa.
Tarkista Preview, SEO Report ja JSON Output
Kun artikkeli on luotu, useita tärkeitä tulosteita on saatavilla laadun tarkistukseen ja julkaisun valmisteluun.
Preview
Näyttää artikkelin tekstin ja luodun kuvan. Tämä osio on hyödyllinen lopullisen laadun tarkistamiseen ennen julkaisua.
SEO Report
Näyttää ymmärrettävän SEO-raportin, joka sisältää SEO Score, title, meta description, canonical, keywords, schema, headings, links, images ja OpenGraph.
JSON Output
Näyttää teknisille käyttäjille artikkelin täydellisen tulosteen, mukaan lukien slug, html_body, schema, kuva-URL:t, tags, category, lukuaika sekä luonti- ja päivitysaikaleimat.
Mitä Export API ja Webhook tarkalleen tekevät?
Kun artikkeli on luotu OxiRankerissa, kohdesivusto voi lukea sen Export API:lla tai saada ilmoituksen Webhookin kautta, kun artikkeli on valmis.
Menetelmä 1: Export API
Kohdesivusto voi lukea artikkeleita OxiRankerista Export Tokenin avulla aina tarvittaessa. Tätä menetelmää käytetään manuaaliseen synciin, päivittäiseen synciin, artikkelilistaussivuihin ja täydellisen artikkeli-JSONin hakemiseen.
GET https://oxiranker.com/api/v1/users/domains/1/export/articles?limit=20&offset=0
Authorization: Bearer YOUR_EXPORT_TOKEN
Accept: application/jsonGET https://oxiranker.com/api/v1/users/domains/1/articles/5/export/json
Authorization: Bearer YOUR_EXPORT_TOKEN
Accept: application/jsonMenetelmä 2: Webhook
Kun uusi artikkeli luodaan tai artikkeli päivitetään, OxiRanker ilmoittaa kohdesivuston endpointille. Webhook ei lähetä koko artikkelia. Se lähettää vain artikkelin tunnisteen, jotta kohdesivusto voi hakea koko artikkelin Export JSON API:sta.
https://example.com/api/oxiranker/webhook{
"event": "article.created",
"domain_id": "1",
"article_id": "5",
"request_id": "5",
"lang": "fa",
"slug": "smart-internal-links",
"occurred_at": "2026-05-20T19:35:33.000Z"
}Sisällön tallennusvastuu ja Image Mirroring
Kohdesivuston on tallennettava lopullinen artikkeli ja kuvat omalle infrastruktuurilleen.
Mitä kohdesivuston on tallennettava
Oikea kuvan polun esimerkki
OxiRanker-kuva-URLien suora ja pysyvä käyttö ei ole oikein. Kuva on ladattava ja artikkelin URLit on korvattava kohdesivuston sisäisillä URLeilla.
https://oxiranker.com/uploads/articles/3_5/cover.webphttps://example.com/uploads/site-articles/oxiranker/1/5/cover.webpHanki Export Token ja valmiit endpointit
Export Token ja käyttövalmiit endpointit ovat saatavilla artikkelisivulta, Outputs-välilehdeltä ja API Output -osiosta.
Mistä Export Token saadaan
https://oxiranker.com/en/pannel/generate/{domainId}/articles/{articleId}Käyttövalmiit endpointit
Samalla artikkelisivulla API Output -osion sisällä Ready-to-use endpoints näyttää valmiit URLit.
https://oxiranker.com/api/v1/users/domains/1/export/articles?limit=20&offset=0https://oxiranker.com/api/v1/users/domains/1/articles/1/export/jsonhttps://oxiranker.com/api/v1/users/domains/1/articles/1/export/html?canonical_mode=absolute&include_css=0Luo Webhook Secret ja kohde-endpoint
Webhook Secret luodaan myös artikkelisivulta, Outputs-välilehdeltä ja API Output -osiosta.
Missä Webhook Secret luodaan?
Kiinteä kohde-endpoint
OxiRanker hyväksyy kohde-Webhookille vain seuraavan kiinteän polun. Kohdesivuston on luotava tämä endpoint omaan backendiinsä.
POST /api/oxiranker/webhook
https://example.com/api/oxiranker/webhookTämän endpointin on hyväksyttävä vain POST, säilytettävä raw body, vahvistettava timestamp ja signature sekä palautettava onnistunut vastaus webhook.testille.
Kohdesivuston vaaditut .env-arvot
OxiRankerilla ei ole pääsyä kohdesivuston tiedostoihin tai .env-tiedostoon. Kohdesivuston kehittäjän on tallennettava nämä arvot omaan backendiinsä.
OXIRANKER_EXPORT_API_BASE_URL=https://oxiranker.com
OXIRANKER_EXPORT_API_TOKEN=YOUR_EXPORT_TOKEN
OXIRANKER_WEBHOOK_SECRET=YOUR_WEBHOOK_SECRET
OXIRANKER_WEBHOOK_MAX_SKEW_MS=300000
OXIRANKER_SOURCE_PROVIDER=oxiranker
OXIRANKER_SOURCE_DOMAIN_IDS=1
SITE_ARTICLE_IMAGE_UPLOAD_DIR=/var/www/example.com/uploads/site-articles
SITE_ARTICLE_IMAGE_MAX_BYTES=10485760
PUBLIC_BASE_URL=https://example.com
OXIRANKER_SYNC_ENABLED=true
OXIRANKER_SYNC_HOUR=3
OXIRANKER_SYNC_MINUTE=10
OXIRANKER_SYNC_PAGE_LIMIT=20
OXIRANKER_SYNC_MAX_PAGES=50Turva-arvot
Tallennusarvot
Export API: List, JSON, HTML ja ZIP
Kohdesivusto voi käyttää Export API:a artikkelilistan tai jokaisen artikkelin täydellisen tulosteen vastaanottamiseen.
Hae domainin artikkelilista
GET https://oxiranker.com/api/v1/users/domains/:domainId/export/articles?limit=20&offset=0
Authorization: Bearer YOUR_EXPORT_TOKEN
Accept: application/jsonlimit määrittää artikkelien määrän ja on tällä hetkellä rajattu välille 1–50. offsetia käytetään sivutukseen. lang on valinnainen ja sitä käytetään tietyn kielen, kuten fa, en tai tr, artikkelien hakemiseen.
Hae koko artikkeli JSON-muodossa
GET https://oxiranker.com/api/v1/users/domains/:domainId/articles/:articleId/export/json
Authorization: Bearer YOUR_EXPORT_TOKEN
Accept: application/jsonTämä on kohdesivuston tärkein tuloste. Kohdesivuston on tallennettava koko artikkeli-JSON omaan tietokantaansa ja luettava julkista näyttöä varten omasta tietokannastaan.
Hae valmis HTML
GET https://oxiranker.com/api/v1/users/domains/1/articles/5/export/html?canonical_mode=absolute&include_css=0
Authorization: Bearer YOUR_EXPORT_TOKENHTML sopii sivustoille, jotka haluavat valmiin tulosteen, mutta ammattimaisempi tapa on tallentaa JSON ja renderöidä se kohdesivuston omalla mallipohjalla.
Hae ZIP-tuloste
GET https://oxiranker.com/api/v1/users/domains/1/articles/5/export/zip
Authorization: Bearer YOUR_EXPORT_TOKENZIP-tuloste sisältää tiedostot article.json, meta.json, assets.json, schema.jsonld, index.html, version.txt ja README.txt.
Webhook-allekirjoitus ja turvallinen Webhookin vastaanotto
Kohdesivuston on vahvistettava Webhook-allekirjoitus alkuperäisen raw bodyn avulla.
Webhook-headerit
content-type: application/json
user-agent: Oxiranker-Webhooks/1.0
x-oxiranker-event: article.created
x-oxiranker-domain-id: 1
x-oxiranker-timestamp: 2026-05-20T19:35:33.000Z
x-oxiranker-signature: sha256=...Allekirjoituskaava
payload_to_sign = timestamp + "." + rawBody
signature = "sha256=" + HMAC_SHA256(webhook_secret, payload_to_sign)Jos kohdesivusto parsii JSONin ensin ja stringifyaa sen sitten uudelleen, järjestys tai välilyönnit voivat muuttua ja allekirjoitus voi muuttua virheelliseksi. Alkuperäistä raw bodya on käytettävä allekirjoituksen vahvistamiseen.
Yksinkertainen Node.js-koodiesimerkki Webhookin vastaanottamiseen
Tämä Express-esimerkki käsittelee raw bodyn, timestampin, signaturen, webhook.testin ja artikkelitapahtumat.
import express from "express";
import crypto from "crypto";
const app = express();
app.use(
express.json({
verify: (req, _res, buf) => {
(req as any).rawBody = Buffer.from(buf);
},
})
);
function getHeader(req: express.Request, name: string): string {
const value = req.headers[name.toLowerCase()];
if (Array.isArray(value)) return String(value[0] || "").trim();
return String(value || "").trim();
}
function getRawBody(req: express.Request): string {
const raw = (req as any).rawBody;
if (Buffer.isBuffer(raw)) return raw.toString("utf8");
return JSON.stringify(req.body || {});
}
function createSignature(secret: string, timestamp: string, rawBody: string): string {
const payload = `${timestamp}.${rawBody}`;
const digest = crypto
.createHmac("sha256", secret)
.update(payload, "utf8")
.digest("hex");
return `sha256=${digest}`;
}
function timingSafeEqualText(a: string, b: string): boolean {
const ab = Buffer.from(a, "utf8");
const bb = Buffer.from(b, "utf8");
if (ab.length !== bb.length) return false;
return crypto.timingSafeEqual(ab, bb);
}
function isTimestampValid(timestamp: string): boolean {
const maxSkewMs = Number(process.env.OXIRANKER_WEBHOOK_MAX_SKEW_MS || 300000);
const date = new Date(timestamp);
if (Number.isNaN(date.getTime())) return false;
const diff = Math.abs(Date.now() - date.getTime());
return diff <= maxSkewMs;
}
app.post("/api/oxiranker/webhook", async (req, res) => {
const secret = String(process.env.OXIRANKER_WEBHOOK_SECRET || "").trim();
if (!secret) {
return res.status(500).json({
error: "webhook_secret_missing",
message: "Webhook secret is not configured.",
});
}
const eventHeader = getHeader(req, "x-oxiranker-event");
const domainIdHeader = getHeader(req, "x-oxiranker-domain-id");
const timestamp = getHeader(req, "x-oxiranker-timestamp");
const receivedSignature = getHeader(req, "x-oxiranker-signature");
const rawBody = getRawBody(req);
if (!eventHeader) {
return res.status(400).json({
error: "missing_webhook_event",
message: "Webhook event header is required.",
});
}
if (!domainIdHeader) {
return res.status(400).json({
error: "missing_webhook_domain_id",
message: "Webhook domain id header is required.",
});
}
if (!timestamp) {
return res.status(400).json({
error: "missing_webhook_timestamp",
message: "Webhook timestamp header is required.",
});
}
if (!receivedSignature) {
return res.status(400).json({
error: "missing_webhook_signature",
message: "Webhook signature header is required.",
});
}
if (!isTimestampValid(timestamp)) {
return res.status(401).json({
error: "invalid_webhook_timestamp",
message: "Webhook timestamp is expired or invalid.",
});
}
const expectedSignature = createSignature(secret, timestamp, rawBody);
if (!timingSafeEqualText(expectedSignature, receivedSignature)) {
return res.status(401).json({
error: "invalid_webhook_signature",
message: "Invalid webhook signature.",
});
}
const payload = req.body || {};
const event = String(payload.event || eventHeader || "").trim();
if (event === "webhook.test") {
const sourceDomainId = Number(payload.domain_id || domainIdHeader);
return res.status(200).json({
ok: true,
message: "Webhook test received successfully.",
event,
source_domain_id:
Number.isFinite(sourceDomainId) && sourceDomainId > 0 ? sourceDomainId : null,
test: true,
});
}
if (event !== "article.created" && event !== "article.updated") {
return res.status(400).json({
error: "unsupported_webhook_event",
message: "Webhook event is not supported.",
});
}
const sourceDomainId = Number(payload.domain_id || domainIdHeader);
const sourceArticleId = Number(payload.article_id);
if (!Number.isFinite(sourceDomainId) || sourceDomainId <= 0) {
return res.status(400).json({
error: "invalid_domain_id",
message: "Webhook domain id is invalid.",
});
}
if (!Number.isFinite(sourceArticleId) || sourceArticleId <= 0) {
return res.status(400).json({
error: "invalid_article_id",
message: "Webhook article id is invalid.",
});
}
// Fetch the full article from Export API and store it in the database here.
// await syncArticleFromOxiranker(sourceDomainId, sourceArticleId);
return res.status(200).json({
ok: true,
message: "Webhook processed successfully.",
event,
source_domain_id: sourceDomainId,
source_article_id: sourceArticleId,
});
});Suositeltu kohdesivuston tietokanta
Kohdesivuston on tallennettava artikkelit omaan tietokantaansa ja estettävä duplikaatit unique constraintilla.
CREATE TABLE public.site_articles (
id BIGSERIAL PRIMARY KEY,
source_provider VARCHAR NOT NULL DEFAULT 'oxiranker',
source_domain_id BIGINT NOT NULL,
source_article_id BIGINT NOT NULL,
source_request_id BIGINT NULL,
lang VARCHAR NOT NULL,
lang_label VARCHAR NULL,
dir VARCHAR NOT NULL,
is_rtl BOOLEAN NULL,
length VARCHAR NULL,
topic_text TEXT NULL,
slug VARCHAR NOT NULL,
canonical_path VARCHAR NULL,
effective_canonical_url TEXT NULL,
title TEXT NOT NULL,
meta_description TEXT NULL,
html_body TEXT NULL,
focus_keywords JSONB NOT NULL DEFAULT '[]'::jsonb,
category VARCHAR NULL,
tags JSONB NOT NULL DEFAULT '[]'::jsonb,
reading_time_minutes INTEGER NULL,
og_title TEXT NULL,
og_description TEXT NULL,
cover_image_url TEXT NULL,
og_image_url TEXT NULL,
article_schema JSONB NOT NULL DEFAULT '{}'::jsonb,
raw_list_item JSONB NOT NULL DEFAULT '{}'::jsonb,
raw_detail_payload JSONB NOT NULL DEFAULT '{}'::jsonb,
source_created_at TIMESTAMPTZ NULL,
source_updated_at TIMESTAMPTZ NULL,
sync_status VARCHAR NOT NULL DEFAULT 'synced',
last_synced_at TIMESTAMPTZ NOT NULL DEFAULT now(),
last_sync_error TEXT NULL,
publish_status VARCHAR NOT NULL DEFAULT 'draft',
published_at TIMESTAMPTZ NULL,
deleted_at TIMESTAMPTZ NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
source_cover_image_url TEXT NULL,
source_og_image_url TEXT NULL,
image_mirror_status VARCHAR NOT NULL DEFAULT 'pending',
image_mirror_error TEXT NULL,
image_mirrored_at TIMESTAMPTZ NULL,
CONSTRAINT site_articles_dir_check
CHECK (dir IN ('ltr', 'rtl')),
CONSTRAINT site_articles_reading_time_check
CHECK (reading_time_minutes IS NULL OR reading_time_minutes >= 0),
CONSTRAINT site_articles_sync_status_check
CHECK (sync_status IN ('pending', 'syncing', 'synced', 'failed')),
CONSTRAINT site_articles_publish_status_check
CHECK (publish_status IN ('draft', 'published', 'archived')),
CONSTRAINT site_articles_image_mirror_status_check
CHECK (image_mirror_status IN ('pending', 'not_needed', 'mirrored', 'failed')),
CONSTRAINT site_articles_source_unique
UNIQUE (source_provider, source_domain_id, source_article_id)
);CREATE INDEX site_articles_source_lookup_idx
ON public.site_articles (source_provider, source_domain_id, source_article_id);
CREATE INDEX site_articles_source_updated_at_idx
ON public.site_articles (source_updated_at DESC);
CREATE INDEX site_articles_slug_idx
ON public.site_articles (slug)
WHERE deleted_at IS NULL;
CREATE UNIQUE INDEX site_articles_lang_slug_active_unique
ON public.site_articles (lang, slug)
WHERE deleted_at IS NULL;
CREATE INDEX site_articles_lang_publish_idx
ON public.site_articles (lang, publish_status)
WHERE deleted_at IS NULL;
CREATE INDEX site_articles_published_at_idx
ON public.site_articles (published_at DESC)
WHERE deleted_at IS NULL;
CREATE INDEX site_articles_focus_keywords_gin_idx
ON public.site_articles USING gin (focus_keywords);
CREATE INDEX site_articles_tags_gin_idx
ON public.site_articles USING gin (tags);
CREATE INDEX site_articles_article_schema_gin_idx
ON public.site_articles USING gin (article_schema);Estä duplikaatit UPSERTilla
Artikkelia ei saa lisätä uudelleen jokaisesta Webhookista. Jos se on jo olemassa, se on päivitettävä.
INSERT INTO public.site_articles (
source_provider,
source_domain_id,
source_article_id,
source_request_id,
lang,
dir,
slug,
title,
meta_description,
html_body,
focus_keywords,
category,
tags,
reading_time_minutes,
cover_image_url,
og_image_url,
article_schema,
source_created_at,
source_updated_at,
sync_status,
last_synced_at,
publish_status,
published_at,
created_at,
updated_at
)
VALUES (
'oxiranker',
1,
5,
5,
'fa',
'rtl',
'smart-internal-links',
'Article title',
'Meta description',
'<p>HTML body</p>',
'[]'::jsonb,
'SEO',
'[]'::jsonb,
7,
'https://example.com/uploads/site-articles/oxiranker/1/5/cover.webp',
'https://example.com/uploads/site-articles/oxiranker/1/5/cover.webp',
'{}'::jsonb,
'2026-05-20T19:33:23.618Z',
'2026-05-20T19:34:09.629Z',
'synced',
now(),
'published',
now(),
now(),
now()
)
ON CONFLICT (source_provider, source_domain_id, source_article_id)
DO UPDATE SET
source_request_id = EXCLUDED.source_request_id,
lang = EXCLUDED.lang,
dir = EXCLUDED.dir,
slug = EXCLUDED.slug,
title = EXCLUDED.title,
meta_description = EXCLUDED.meta_description,
html_body = EXCLUDED.html_body,
focus_keywords = EXCLUDED.focus_keywords,
category = EXCLUDED.category,
tags = EXCLUDED.tags,
reading_time_minutes = EXCLUDED.reading_time_minutes,
cover_image_url = EXCLUDED.cover_image_url,
og_image_url = EXCLUDED.og_image_url,
article_schema = EXCLUDED.article_schema,
source_created_at = EXCLUDED.source_created_at,
source_updated_at = EXCLUDED.source_updated_at,
sync_status = 'synced',
last_synced_at = now(),
last_sync_error = NULL,
publish_status = CASE
WHEN public.site_articles.publish_status = 'archived'
THEN public.site_articles.publish_status
ELSE 'published'
END,
published_at = COALESCE(public.site_articles.published_at, EXCLUDED.published_at, now()),
deleted_at = NULL,
updated_at = now();Hae koko artikkeli ja syncaa Webhookin jälkeen
Kun Webhook saapuu, kohdesivuston on haettava koko artikkeli Export API:sta, mirrorattava kuvat ja tehtävä artikkelille UPSERT.
async function fetchOxirankerArticleDetail(args: {
baseUrl: string;
token: string;
domainId: number;
articleId: number;
}) {
const url = `${args.baseUrl.replace(/\/+$/, "")}/api/v1/users/domains/${args.domainId}/articles/${args.articleId}/export/json`;
const response = await fetch(url, {
method: "GET",
headers: {
Authorization: `Bearer ${args.token}`,
Accept: "application/json",
"User-Agent": "My-Site-Oxiranker-Sync/1.0",
},
});
const bodyText = await response.text();
if (response.status < 200 || response.status > 299) {
throw new Error(
`Export API request failed with HTTP status ${response.status}. Response: ${bodyText.slice(0, 1000)}`
);
}
try {
return JSON.parse(bodyText);
} catch {
throw new Error("Export API returned invalid JSON.");
}
}async function syncArticleFromWebhook(payload: any) {
const sourceDomainId = Number(payload.domain_id);
const sourceArticleId = Number(payload.article_id);
if (!Number.isFinite(sourceDomainId) || sourceDomainId <= 0) {
throw new Error("Webhook domain id is invalid.");
}
if (!Number.isFinite(sourceArticleId) || sourceArticleId <= 0) {
throw new Error("Webhook article id is invalid.");
}
const article = await fetchOxirankerArticleDetail({
baseUrl: process.env.OXIRANKER_EXPORT_API_BASE_URL!,
token: process.env.OXIRANKER_EXPORT_API_TOKEN!,
domainId: sourceDomainId,
articleId: sourceArticleId,
});
const mirroredImages = await mirrorArticleImages(article);
const finalHtmlBody = replaceImageUrls(article.html_body, mirroredImages);
const finalArticleSchema = replaceImageUrlsInJson(article.article_schema, mirroredImages);
await upsertSiteArticle({
source_provider: process.env.OXIRANKER_SOURCE_PROVIDER || "oxiranker",
source_domain_id: sourceDomainId,
source_article_id: sourceArticleId,
source_request_id: article.request_id,
lang: article.lang,
dir: article.dir,
slug: article.slug,
title: article.title,
meta_description: article.meta_description,
html_body: finalHtmlBody,
focus_keywords: article.focus_keywords,
category: article.category,
tags: article.tags,
reading_time_minutes: article.reading_time_minutes,
cover_image_url: mirroredImages.cover_image_url,
og_image_url: mirroredImages.og_image_url,
article_schema: finalArticleSchema,
raw_detail_payload: article,
source_created_at: article.created_at,
source_updated_at: article.updated_at,
});
}Image Mirroring ja kuvien tallennussäännöt
Kohdesivuston on ladattava kuvat, tallennettava ne omaan infrastruktuuriinsa ja korvattava artikkelin URLit.
Image Mirroring -säännöt
Suositeltu tallennuspolku
/uploads/site-articles/oxiranker/1/5/cover.webp
/uploads/site-articles/oxiranker/1/5/og.webphttps://example.com/uploads/site-articles/oxiranker/1/5/cover.webpPäivittäinen sync luotettavuutta varten
Webhook yksin ei riitä. Kohdesivustolla pitäisi olla myös päivittäinen sync, jotta puuttuneet artikkelit voidaan palauttaa.
Miksi päivittäinen sync tarvitaan?
Päivittäisen syncin algoritmi
Näytä artikkelit kohdesivustolla
Kun artikkelit on tallennettu site_articles-tauluun, kohdesivuston ei pidä yhdistää OxiRankeriin jokaista julkista näyttöä varten. Sen on luettava omasta tietokannastaan.
Blogin listasivu
SELECT
id,
lang,
lang_label,
dir,
is_rtl,
slug,
title,
meta_description,
category,
tags,
reading_time_minutes,
cover_image_url,
og_image_url,
source_created_at,
published_at,
created_at
FROM public.site_articles
WHERE source_provider = 'oxiranker'
AND deleted_at IS NULL
AND sync_status = 'synced'
AND publish_status = 'published'
ORDER BY COALESCE(source_created_at, published_at, created_at) DESC, id DESC
LIMIT 20 OFFSET 0;Artikkelin yksityiskohtasivu
SELECT *
FROM public.site_articles
WHERE source_provider = 'oxiranker'
AND lang = 'fa'
AND slug = 'smart-internal-links'
AND deleted_at IS NULL
AND sync_status = 'synced'
AND publish_status = 'published'
LIMIT 1;SEO, Schema, RTL ja HTML-renderöinti
Kohdesivuston on luettava SEO-data omasta tietokannastaan ja renderöitävä se artikkelisivulla.
SEO-kentät
HTML-renderöinti
<div
className="article-content"
dangerouslySetInnerHTML={{ __html: article.html_body }}
/>Jos kohdesivustolla on tiukka turvallisuuskäytäntö, se voi sanitizeoida HTML:n ennen tallennusta tai ennen näyttämistä.
RTL ja LTR
Jokaisella artikkelilla on kentät lang, dir ja is_rtl. Jos dir on rtl, artikkelisivu on renderöitävä oikealta vasemmalle.
<article dir="rtl">
...
</article>Yleiset virheet ja niiden merkitykset
Kohdesivuston virheiden on oltava englanniksi, selkeitä ja ymmärrettäviä, jotta vianmääritys voidaan tehdä nopeasti.
Export API -virheet
Webhook-virheet
Lopullinen API- ja Webhook-testauksen tarkistuslista
Toteutuksen jälkeen nämä testit varmistavat yhteyden alusta loppuun.
Export Token ja JSON -testi
GET https://oxiranker.com/api/v1/users/domains/1/export/articles?limit=2&offset=0
Authorization: Bearer YOUR_EXPORT_TOKEN
Accept: application/jsonGET https://oxiranker.com/api/v1/users/domains/1/articles/5/export/json
Authorization: Bearer YOUR_EXPORT_TOKEN
Accept: application/jsonWebhook-testi
Jos kutsut kohde-Webhookia Postmanilla ilman allekirjoitusta, sinun on saatava turvavirhe. Tämä tarkoittaa, että endpoint on aktiivinen ja tarkistaa turvallisuuden.
{
"error": "missing_webhook_signature",
"message": "Webhook signature header is required."
}Klikkaa sitten Test Webhook OxiRankerin sisällä. Jos vastaanotin tukee webhook.testiä, response_status-arvon on oltava 200.
Tärkeät säännöt oikeaan toteutukseen
Näitä sääntöjä on noudatettava API-integraatiossa, jotta järjestelmä pysyy turvallisena, vakaana ja ylläpidettävänä.
Täydellinen API-virran yhteenveto
Kun nämä vaiheet on suoritettu, mukautettu verkkosivustosi voi turvallisesti vastaanottaa, tallentaa, mirrorata ja julkaista OxiRankerissa luotuja artikkeleita.