OxiRanker dokumentasiýasy
Web saýtyňyzy OxiRanker hyzmatlaryna birikdirmek üçin arassa dokumentasiýa merkezi. WordPress integrasiýasy ýa-da API integrasiýasy bilen başlaň, soňra gollanmany ädimme-ädim ýerine ýetiriň.
API hyzmaty
Custom websites, custom CMS platformalary, Next.js, Laravel, Node.js, PHP ýa-da Python backendleri üçin Export API we Webhook ulanyň.
OxiRanker destination website üçin permanent hosting däldir
OxiRanker destination website content, images ýa-da exported files üçin permanent hosting kepilligini bermeýär. OxiRanker user-content hosting service däldir we generated content, cover images, Open Graph images ýa-da output files OxiRanker infrastructure-da 90 günden köp galman biler.
Şonuň üçin output alnandan soň destination website final article, images, SEO data, Open Graph data we Schema data-ny öz database, server, storage ýa-da CDN içinde store etmeli we public display üçin OxiRanker URLs-e bagly bolmaly däldir.
Birikme okuw wideosy
WordPress-i birikdirmezden ýa-da API integrasiýasyny implement etmezden öň bu wideony wizual walkthrough hökmünde ulanyň.
Output flow-y ädimme-ädim implement ediň
Ilki Export Token we Webhook Secret-i backend-iňizde store ediň, soňra Webhook receiver, database, UPSERT logic, Image Mirroring we daily sync-i tamamlaň.
OxiRanker-de hasaba durmak we domain döretmek
Ilki OxiRanker-de hasaba duruň, account-yňyza sign in ediň we Domains bölüminden Add Domain-e basyň.
Dogry domain formaty
Domain name meýdanyna diňe root domain giriziň. Salgyda http, https ýa-da www bolmaly däl.
example.comNädogry formatlar
Bu formatlary domain meýdanyna girizmäň.
https://example.com
http://example.com
www.example.comDomain eýeçiligini tassyklamak
Bu ädimde domain size degişlidigini ýa-da ony dolandyrmak üçin access bardygyny subut etmeli.
DNS TXT
Maslahat berilýärDomain DNS-e TXT record goşuň. Cloudflare üçin bu adatça iň aňsat method bolýar.
DNS Name:
_yourdomain-verification.example.com
TXT Value:
acb1****29803HTTP File
OxiRanker barlap biler ýaly web saýtyňyzyň .well-known path içinde text file ýerleşdiriň.
File URL:
https://example.com/.well-known/yourdomain-verification.txt
File Content:
220daaa37****42730f27Meta Tag
Web saýtyňyzyň homepage head section içinde meta tag ýerleşdiriň.
<meta name="yourdomain-verification" content="190208d****d275b65" />Birinji domain tassyklanylandan soň mugt tokenleri almak
Birinji domain verify edilenden soň, OxiRanker initial testing üçin account-yňyza 1000 free tokens goşýar.
Dilleri we Blog URL-ni sazlamak
Blog URLs bölüminde web saýtyňyzyň single-language ýa-da multilingual bolandygyny we generated articles üçin haýsy languages ulanylmalydygyny kesgitläň.
Dilleri saýlamak
OxiRanker 24 languages goldaýar. Web saýtyňyzyň gurluşyna görä bir ýa-da birnäçe languages saýlap bilersiňiz.
Blog URL girizmek
Her language üçin şol language blog URL-ni giriziň. Eger web saýtyňyz multilingual bolsa, her language path-y aýratyn giriziň.
https://example.com/fa/blog
https://example.com/en/blogHer language üçin keywords goşmak
Keywords tab içinde her language üçin azyndan 3 we iň köp 50 keywords goşuň.
Esasy düzgün
Her language üçin keywords şol language-da girizilmeli. Eger article language English bolsa, keywords hem English bolmaly.
Keywords nädip goşulýar
Her keyword ýazanyňyzdan soň, ony goşmak üçin Enter basyň. Ähli keywords tamamlanandan soň Save basyň.
Domain Article Profile doldurmak
Article Profile OxiRanker-e brand-iňiziň nämedigini, kim üçin ýazýandygyny, haýsy tone ulanylmalydygyny we haýsy topics-den gaça durmalydygyny aýdýar.
Industry
Market ýa-da business category kesgitleýär. Mysal: Dubai-de real estate consulting we property buying and selling.
Business description
Business näme satýandygyny, kime hyzmat edýändigini, nähili işleýändigini we näme bilen tapawutlanýandygyny düşündirýär.
Audience
Content kime ýüzlenmelidigini kesgitleýär. Her audience aýratyn save edilip bilner.
Tone
Generated content-iň style we voice bölegini dolandyrýar, meselem formal, educational, simple, technical ýa-da sales-oriented.
Content goal
Articles näme gazanmalydygyny kesgitleýär, meselem customers çekmek, traffic artdyrmak ýa-da users öwretmek.
Gadagan topics
Brand-iň gürrüň etmeli däl ýa-da baglanyşdyrylmaly däl ähli zatlary şu ýere girizilýär.
Optional CTA
CTA reader article okandan soň etmeli action-yňyzy aňladýar. Haýsy text suitable bolýandygyna ynamly bolmasaňyz, bu field-i empty goýmak gowudyr.
Içki we daşarky linkler
OxiRanker articles içinde internal links we external links-i intelligent görnüşde ulanyp biler.
Internal links
Bu option default ýagdaýda enabled we enabled saklamak has gowudyr. Internal linking search engines-e web saýtyňyzyň content structure-yny has gowy düşünmäge kömek edýär.
Birinji article içinde internal links adatça döredilmeýär, sebäbi entek previous article ýok. Ikinji article-den başlap system internal linking edip biler.
External links
External links official we trusted sources-e goşulýar we SEO authority unintended geçmez ýaly nofollow hökmünde marked edilýär.
AI bilen profile quality barlamak
Article Profile doldurylandan soň, OxiRanker profile quality-ni review edýär.
Article summaries üçin Telegram birikdirmek
Profile üstünlikli save edilenden soň, article summaries-i Telegram channel-a ibermegi enable edip bilersiňiz.
Bot goşmak
@Oxiranker_Bot-y Telegram channel-yňyza goşuň we ony Admin ediň.
Channel username girizmek
Channel name-i @ bilen giriziň. Mysal: @my_channel
Summary delivery enable etmek
Valid channel bellige alnandan soň article summary delivery enable ediň.
Automatic article engine sazlamak
Automatic article generation tab içinde automatic article generation engine-i dolandyryp bilersiňiz.
Smart topic selection
System suitable, unique we publishable topics saýlamak üçin keywords we Article Profile ulanýar.
Brand-aware generation
Article generation wagtynda tone, audience, content goal we brand restrictions berjaý edilýär.
Scheduled publishing
Monthly capacity aýyň dowamynda paýlanýar, şonuň üçin content publishing has natural we consistent görünýär.
Monthly capacity
7 articles / month
10 articles / month
15 articles / month
30 articles / month30 articles diýenimizde takmynan her gün bir article. 15 articles diýenimizde takmynan iki günden bir. 10 articles diýenimizde takmynan her iki-üç günden bir. 7 articles diýenimizde takmynan her üç-dört günden bir.
Article length we image
Article length Short, Standard ýa-da Long bolup biler. Standard köp web saýtlar üçin balanced choice.
Article images enabled ýa-da disabled edilip bilner, image theme bolsa Light ýa-da Dark saýlanyp bilner.
Generate-den birinji article döretmek
Domain taýýarlanylandan soň Generate menu açyň, domain saýlaň we New Content ýa-da New Article basyň.
Language
Her content request üçin diňe bir language saýlanýar.
Topic
Açyk topic ýazyň. Source URL optional.
Image
Image enable ýa-da disable ediň we Light ýa-da Dark theme saýlaň.
Payment we generation
Cost wallet-den deducted edilýär we article generation başlanýar.
Preview, SEO Report we JSON Output review etmek
Article generated bolandan soň, quality review we publishing preparation üçin birnäçe important outputs elýeterli bolýar.
Preview
Article text we generated image görkezýär. Bu section publishing-den öň final quality review etmek üçin peýdaly.
SEO Report
SEO Score, title, meta description, canonical, keywords, schema, headings, links, images we OpenGraph öz içine alýan understandable SEO report görkezýär.
JSON Output
Technical users üçin full article output görkezýär, şol sanda slug, html_body, schema, image URLs, tags, category, reading time we created/updated timestamps.
Export API we Webhook takyk näme edýär?
OxiRanker içinde article generated bolandan soň, destination website ony Export API bilen read edip biler ýa-da article ready bolanda Webhook arkaly notified bolup biler.
Method 1: Export API
Destination website zerur wagty Export Token bilen OxiRanker-den articles read edip biler. Bu method manual sync, daily sync, article list pages we full article JSON fetching üçin ulanylýar.
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/jsonMethod 2: Webhook
New article created bolanda ýa-da article updated bolanda, OxiRanker destination website endpoint-e notify edýär. Webhook full article ibermeýär. Ol diňe article identifier iberýär, şonuň bilen destination website Export JSON API-den full article fetch edip biler.
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"
}Content storage responsibility we Image Mirroring
Destination website final article we images-i öz infrastructure-da store etmeli.
Destination website nämeleri store etmeli
Dogry image path mysaly
OxiRanker image URLs-i directly we permanently ulanmak dogry däl. Image download edilmeli we article URLs destination website internal URLs bilen replace edilmeli.
https://oxiranker.com/uploads/articles/3_5/cover.webphttps://example.com/uploads/site-articles/oxiranker/1/5/cover.webpExport Token we ready endpoints almak
Export Token we ready-to-use endpoints article page, Outputs tab we API Output section arkaly elýeterli.
Export Token nireden alynýar
https://oxiranker.com/en/pannel/generate/{domainId}/articles/{articleId}Ready-to-use endpoints
Şol article page içinde, API Output section-da, Ready-to-use endpoints prepared URLs görkezýär.
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=0Webhook Secret we destination endpoint döretmek
Webhook Secret hem article page, Outputs tab we API Output section arkaly döredilýär.
Webhook Secret nirede döredilýär?
Fixed destination endpoint
OxiRanker destination Webhook üçin diňe aşakdaky fixed path kabul edýär. Destination website bu endpoint-i backend içinde create etmeli.
POST /api/oxiranker/webhook
https://example.com/api/oxiranker/webhookBu endpoint diňe POST accept etmeli, raw body preserve etmeli, timestamp we signature verify etmeli we webhook.test üçin successful response return etmeli.
Destination website üçin required .env values
OxiRanker destination website files ýa-da .env-e access etmeýär. Destination website developer bu values-i öz backend içinde store etmeli.
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=50Security values
Storage values
Export API: List, JSON, HTML we ZIP
Destination website Export API ulanyp article list ýa-da her article-iň full output-y receive edip biler.
Domain üçin list of articles almak
GET https://oxiranker.com/api/v1/users/domains/:domainId/export/articles?limit=20&offset=0
Authorization: Bearer YOUR_EXPORT_TOKEN
Accept: application/jsonlimit number of articles kesgitleýär we häzirki wagtda 1 bilen 50 arasynda limited. offset pagination üçin ulanylýar. lang optional we fa, en ýa-da tr ýaly specific language articles fetch etmek üçin ulanylýar.
Full article-i JSON hökmünde almak
GET https://oxiranker.com/api/v1/users/domains/:domainId/articles/:articleId/export/json
Authorization: Bearer YOUR_EXPORT_TOKEN
Accept: application/jsonBu destination website üçin iň important output. Destination website full article JSON-y öz database içinde store etmeli we public display üçin öz database-den read etmeli.
Ready HTML almak
GET https://oxiranker.com/api/v1/users/domains/1/articles/5/export/html?canonical_mode=absolute&include_css=0
Authorization: Bearer YOUR_EXPORT_TOKENHTML ready output isleýän websites üçin suitable, ýöne has professional approach JSON store edip, destination website-iň öz template-i bilen render etmekdir.
ZIP output almak
GET https://oxiranker.com/api/v1/users/domains/1/articles/5/export/zip
Authorization: Bearer YOUR_EXPORT_TOKENZIP output article.json, meta.json, assets.json, schema.jsonld, index.html, version.txt we README.txt öz içine alýar.
Webhook Signature we secure Webhook receiving
Destination website original raw body ulanyp Webhook signature verify etmeli.
Webhook headers
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=...Signature formula
payload_to_sign = timestamp + "." + rawBody
signature = "sha256=" + HMAC_SHA256(webhook_secret, payload_to_sign)Eger destination website ilki JSON parse edip, soňra täzeden stringify etse, order ýa-da spacing üýtgäp biler we signature invalid bolup biler. Signature verification üçin original raw body ulanylmaly.
Webhook receiving üçin simple Node.js code sample
Bu Express sample raw body, timestamp, signature, webhook.test we article events handle edýär.
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,
});
});Recommended destination website database
Destination website articles-i öz database içinde store etmeli we unique constraint bilen duplicates prevent etmeli.
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);UPSERT bilen duplicates prevent etmek
Article her Webhook üçin täzeden inserted edilmeli däl. Eger ol eýýäm exists bolsa, updated edilmeli.
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();Webhook-dan soň full article fetch we sync etmek
Webhook gelende, destination website Export API-den full article fetch etmeli, images mirror etmeli we article UPSERT etmeli.
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 we image storage rules
Destination website images download etmeli, öz infrastructure-da store etmeli we article URLs replace etmeli.
Image Mirroring rules
Recommended storage path
/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.webpReliability üçin daily sync
Webhook alone ýeterlik däl. Destination website missed articles recover etmek üçin daily sync hem bolmaly.
Daily sync näme üçin gerek?
Daily sync algorithm
Destination website-de articles görkezmek
Articles site_articles içinde storing edilenden soň, destination website her public display üçin OxiRanker-e connect etmeli däl. Ol öz database-den read etmeli.
Blog list page
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;Article detail page
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 we HTML rendering
Destination website SEO data-ny öz database-den read edip, article page-de render etmeli.
SEO fields
HTML rendering
<div
className="article-content"
dangerouslySetInnerHTML={{ __html: article.html_body }}
/>Eger destination website strict security policy bar bolsa, HTML-i saving-den öň ýa-da displaying-den öň sanitize edip biler.
RTL we LTR
Her article-de lang, dir we is_rtl fields bar. Eger dir rtl bolsa, article page right-to-left rendered edilmeli.
<article dir="rtl">
...
</article>Common errors we olaryň manysy
Destination website errors English, clear we understandable bolmaly, şeýdip troubleshooting tiz ýerine ýetirilip bilner.
Export API errors
Webhook errors
Final API we Webhook testing checklist
Implementation-den soň bu tests connection-i beginning-den end-e çenli verify edýär.
Export Token we JSON test
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 test
Eger destination Webhook-y Postman bilen signature bolmazdan call etseňiz, security error receive etmeli. Bu endpoint active we checks security diýmekdir.
{
"error": "missing_webhook_signature",
"message": "Webhook signature header is required."
}Soňra OxiRanker içinde Test Webhook basyň. Eger receiver webhook.test supports edýän bolsa, response_status 200 bolmaly.
Correct implementation üçin important rules
System secure, stable we maintainable bolmagy üçin API integration içinde bu rules ýerine ýetirilmeli.
Complete API flow summary
Bu steps tamamlanandan soň custom website OxiRanker-de generated articles-i securely receive, store, mirror we publish edip biler.