OxiRanker दस्तावेज़ीकरण
अपनी वेबसाइट को OxiRanker सेवाओं से जोड़ने के लिए एक साफ़ documentation center. WordPress integration या API integration से शुरू करें, फिर guide को step by step follow करें.
API service
Custom websites, custom CMS platforms, Next.js, Laravel, Node.js, PHP या Python backends के लिए Export API और Webhook का उपयोग करें.
OxiRanker destination website के लिए permanent hosting नहीं है
OxiRanker destination website के content, images या exported files के लिए permanent hosting guarantee प्रदान नहीं करता. OxiRanker user-content hosting service नहीं है, और generated content, cover images, Open Graph images या output files OxiRanker infrastructure पर 90 दिनों से अधिक नहीं रह सकते.
इसलिए, output receive करने के बाद, destination website को final article, images, SEO data, Open Graph data और Schema data को अपने database, server, storage या CDN में store करना चाहिए, और public display के लिए OxiRanker URLs पर निर्भर नहीं रहना चाहिए.
Connection training video
WordPress connect करने या API integration implement करने से पहले इस video को visual walkthrough की तरह इस्तेमाल करें.
Output flow को step by step implement करें
पहले Export Token और Webhook Secret को अपने backend में store करें, फिर Webhook receiver, database, UPSERT logic, Image Mirroring और daily sync पूरा करें.
OxiRanker में register करें और domain बनाएं
सबसे पहले, OxiRanker में register करें, अपने account में sign in करें और Domains section से Add Domain पर click करें.
सही domain format
Domain name field में केवल root domain enter करें. Address में http, https या www शामिल नहीं होना चाहिए.
example.comगलत formats
Domain field में ये formats enter न करें.
https://example.com
http://example.com
www.example.comDomain ownership verify करें
इस step में, आपको यह साबित करना होगा कि domain आपका है या आपके पास उसे manage करने की access है.
DNS TXT
अनुशंसितDomain DNS में TXT record add करें. Cloudflare के लिए, यह आमतौर पर सबसे सरल method है.
DNS Name:
_yourdomain-verification.example.com
TXT Value:
acb1****29803HTTP File
अपनी website के .well-known path में एक text file रखें ताकि OxiRanker उसे verify कर सके.
File URL:
https://example.com/.well-known/yourdomain-verification.txt
File Content:
220daaa37****42730f27Meta Tag
अपनी website homepage के head section के अंदर एक meta tag रखें.
<meta name="yourdomain-verification" content="190208d****d275b65" />पहले domain को verify करने के बाद free tokens receive करें
पहले domain को verify करने के बाद, OxiRanker initial testing के लिए आपके account में 1000 free tokens add करता है.
Languages और Blog URL configure करें
Blog URLs section में define करें कि आपकी website single-language है या multilingual और generated articles के लिए कौन सी languages इस्तेमाल होनी चाहिए.
Languages चुनें
OxiRanker 24 languages support करता है. आप अपनी website structure के आधार पर एक या कई languages चुन सकते हैं.
Blog URL enter करें
हर language के लिए उस language का blog URL enter करें. अगर आपकी website multilingual है, तो हर language path अलग-अलग enter करें.
https://example.com/fa/blog
https://example.com/en/blogहर language के लिए keywords add करें
Keywords tab में, हर language के लिए कम से कम 3 और अधिकतम 50 keywords add करें.
मुख्य नियम
हर language के keywords उसी language में enter होने चाहिए. अगर article language English है, तो keywords भी English होने चाहिए.
Keywords कैसे add करें
हर keyword type करने के बाद, उसे add करने के लिए Enter दबाएं. सभी keywords complete करने के बाद, Save click करें.
Domain Article Profile complete करें
Article Profile OxiRanker को बताता है कि आपका brand क्या है, वह किसके लिए लिखता है, उसे कौन सा tone इस्तेमाल करना चाहिए और किन topics से बचना चाहिए.
Industry
Market या business category define करता है. Example: Dubai में real estate consulting और property buying/selling.
Business description
बताता है कि business क्या बेचता है, किसे serve करता है, कैसे काम करता है और उसे अलग क्या बनाता है.
Audience
Define करता है कि content किससे बात करे. हर audience अलग से save की जा सकती है.
Tone
Generated content की style और voice control करता है, जैसे formal, educational, simple, technical या sales-oriented.
Content goal
Define करता है कि articles क्या achieve करें, जैसे customers attract करना, traffic बढ़ाना या users को educate करना.
Forbidden topics
Brand को जिन चीज़ों के बारे में बात नहीं करनी चाहिए या जिनसे associate नहीं होना चाहिए, वे यहाँ enter की जाती हैं.
Optional CTA
CTA का मतलब वह action है जो आप चाहते हैं कि reader article पढ़ने के बाद करे. अगर आप sure नहीं हैं कि कौन सा text suitable है, तो यह field empty छोड़ना बेहतर है.
Internal और external linking
OxiRanker articles के अंदर internal और external links को intelligently apply कर सकता है.
Internal links
यह option default रूप से enabled है और इसे enabled रखना बेहतर है. Internal linking search engines को आपकी website के content structure को बेहतर समझने में मदद करती है.
पहले article में, internal links आमतौर पर create नहीं होते क्योंकि अभी कोई previous article नहीं होता. दूसरे article से आगे, system internal linking perform कर सकता है.
External links
External links official और trusted sources में add किए जाते हैं और nofollow के रूप में marked होते हैं ताकि SEO authority unintentionally transfer न हो.
AI से profile quality check करें
Article Profile complete करने के बाद, OxiRanker profile quality review करता है.
Article summaries के लिए Telegram connect करें
Profile successfully save करने के बाद, आप Telegram channel पर article summaries भेजना enable कर सकते हैं.
Bot add करें
@Oxiranker_Bot को अपने Telegram channel में add करें और उसे Admin बनाएं.
Channel username enter करें
Channel name को @ के साथ enter करें. Example: @my_channel
Summary delivery enable करें
Valid channel register करने के बाद, article summary delivery enable करें.
Automatic article engine configure करें
Automatic article generation tab में, आप automatic article generation engine manage कर सकते हैं.
Smart topic selection
System keywords और Article Profile का उपयोग करके suitable, unique और publishable topics select करता है.
Brand-aware generation
Article generation के दौरान tone, audience, content goal और brand restrictions का ध्यान रखा जाता है.
Scheduled publishing
Monthly capacity पूरे month में distribute होती है ताकि content publishing अधिक natural और consistent लगे.
Monthly capacity
7 articles / month
10 articles / month
15 articles / month
30 articles / month30 articles का मतलब लगभग हर दिन एक article. 15 articles का मतलब लगभग हर दूसरे दिन. 10 articles का मतलब लगभग हर दो से तीन दिन. 7 articles का मतलब लगभग हर तीन से चार दिन.
Article length और image
Article length Short, Standard या Long हो सकती है. Standard ज्यादातर websites के लिए balanced choice है.
Article images enabled या disabled की जा सकती हैं, और image theme Light या Dark चुनी जा सकती है.
Generate से पहला article बनाएं
Domain prepare करने के बाद, Generate menu खोलें, domain चुनें और New Content या New Article click करें.
Language
हर content request के लिए केवल एक language select होती है.
Topic
एक clear topic लिखें. Source URL optional है.
Image
Image enable या disable करें और Light या Dark theme चुनें.
Payment और generation
Cost wallet से deduct होता है और article generation start होती है.
Preview, SEO Report और JSON Output review करें
Article generate होने के बाद, quality review और publishing preparation के लिए कई important outputs available होते हैं.
Preview
Article text और generated image दिखाता है. यह section publishing से पहले final quality review करने के लिए useful है.
SEO Report
SEO Score, title, meta description, canonical, keywords, schema, headings, links, images और OpenGraph सहित understandable SEO report दिखाता है.
JSON Output
Technical users के लिए full article output दिखाता है, जिसमें slug, html_body, schema, image URLs, tags, category, reading time और created/updated timestamps शामिल हैं.
Export API और Webhook वास्तव में क्या करते हैं?
OxiRanker में article generate होने के बाद, destination website उसे Export API से read कर सकती है या article ready होने पर Webhook के माध्यम से notify हो सकती है.
Method 1: Export API
Destination website जरूरत पड़ने पर Export Token के साथ OxiRanker से articles read कर सकती है. यह method manual sync, daily sync, article list pages और full article JSON fetch करने के लिए use होती है.
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
जब नया article create होता है या article update होता है, OxiRanker destination website endpoint को notify करता है. Webhook full article नहीं भेजता. यह केवल article identifier भेजता है ताकि destination website Export JSON API से full article fetch कर सके.
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 और Image Mirroring
Destination website को final article और images अपनी own infrastructure पर store करने चाहिए.
Destination website को क्या store करना चाहिए
Correct image path example
OxiRanker image URLs को directly और permanently use करना सही नहीं है. Image download होनी चाहिए और article URLs को destination website के internal URLs से replace करना चाहिए.
https://oxiranker.com/uploads/articles/3_5/cover.webphttps://example.com/uploads/site-articles/oxiranker/1/5/cover.webpExport Token और ready endpoints प्राप्त करें
Export Token और ready-to-use endpoints article page, Outputs tab और API Output section से available हैं.
Export Token कहाँ से प्राप्त करें
https://oxiranker.com/en/pannel/generate/{domainId}/articles/{articleId}Ready-to-use endpoints
उसी article page पर, API Output section के अंदर, Ready-to-use endpoints prepared URLs display करता है.
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 और destination endpoint create करें
Webhook Secret भी article page, Outputs tab और API Output section से create होता है.
Webhook Secret कहाँ create होता है?
Fixed destination endpoint
OxiRanker destination Webhook के लिए केवल निम्न fixed path accept करता है. Destination website को यह endpoint अपने backend में create करना चाहिए.
POST /api/oxiranker/webhook
https://example.com/api/oxiranker/webhookयह endpoint केवल POST accept करे, raw body preserve करे, timestamp और signature verify करे, और webhook.test के लिए successful response return करे.
Destination website के लिए required .env values
OxiRanker को destination website files या .env तक access नहीं है. Destination website developer को इन values को अपने backend में store करना होगा.
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 और ZIP
Destination website Export API का उपयोग article list या हर article का full output receive करने के लिए कर सकती है.
Domain के लिए articles की list प्राप्त करें
GET https://oxiranker.com/api/v1/users/domains/:domainId/export/articles?limit=20&offset=0
Authorization: Bearer YOUR_EXPORT_TOKEN
Accept: application/jsonlimit articles की संख्या define करता है और वर्तमान में 1 से 50 के बीच limited है. offset pagination के लिए use होता है. lang optional है और fa, en या tr जैसी specific language के articles fetch करने के लिए use होता है.
Full article को JSON के रूप में प्राप्त करें
GET https://oxiranker.com/api/v1/users/domains/:domainId/articles/:articleId/export/json
Authorization: Bearer YOUR_EXPORT_TOKEN
Accept: application/jsonयह destination website के लिए सबसे important output है. Destination website को full article JSON को अपने database में store करना चाहिए और public display के लिए अपने database से read करना चाहिए.
Ready 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 उन websites के लिए suitable है जो ready output चाहती हैं, लेकिन अधिक professional approach JSON store करना और destination website के own template से render करना है.
ZIP output प्राप्त करें
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 और README.txt शामिल हैं.
Webhook Signature और secure Webhook receiving
Destination website को original raw body का उपयोग करके Webhook signature verify करना चाहिए.
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)अगर destination website पहले JSON parse करती है और फिर उसे दोबारा stringify करती है, तो order या spacing बदल सकती है और signature invalid हो सकती है. Signature verification के लिए original raw body का उपयोग होना चाहिए.
Webhook receive करने के लिए simple Node.js code sample
यह Express sample raw body, timestamp, signature, webhook.test और article events handle करता है.
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 अपने own database में store करने चाहिए और unique constraint से duplicates रोकने चाहिए.
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 से duplicates रोकें
Article को हर Webhook के लिए दोबारा insert नहीं करना चाहिए. अगर यह पहले से exists करता है, तो इसे update करना चाहिए.
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 के बाद full article fetch और sync करें
जब Webhook आता है, destination website को Export API से full article fetch करना चाहिए, images mirror करनी चाहिए और article 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 और image storage rules
Destination website को images download करनी चाहिए, उन्हें अपनी own infrastructure पर store करना चाहिए और article URLs replace करने चाहिए.
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 के लिए daily sync
Webhook alone enough नहीं है. Destination website के पास missed articles recover करने के लिए daily sync भी होना चाहिए.
Daily sync क्यों जरूरी है?
Daily sync algorithm
Destination website पर articles display करें
site_articles में articles store करने के बाद, destination website को हर public display के लिए OxiRanker से connect नहीं करना चाहिए. उसे अपने own database से read करना चाहिए.
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 और HTML rendering
Destination website को अपने own database से SEO data read करके article page पर render करना चाहिए.
SEO fields
HTML rendering
<div
className="article-content"
dangerouslySetInnerHTML={{ __html: article.html_body }}
/>अगर destination website की strict security policy है, तो वह HTML को save करने से पहले या display करने से पहले sanitize कर सकती है.
RTL और LTR
हर article में lang, dir और is_rtl fields होते हैं. अगर dir rtl है, तो article page को right-to-left render करना चाहिए.
<article dir="rtl">
...
</article>Common errors और उनके अर्थ
Destination website errors English, clear और understandable होने चाहिए ताकि troubleshooting जल्दी हो सके.
Export API errors
Webhook errors
Final API और Webhook testing checklist
Implementation के बाद, ये tests beginning से end तक connection verify करते हैं.
Export Token और 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
अगर आप destination Webhook को Postman से बिना signature call करते हैं, तो आपको security error मिलना चाहिए. इसका मतलब है कि endpoint active है और security check करता है.
{
"error": "missing_webhook_signature",
"message": "Webhook signature header is required."
}फिर OxiRanker के अंदर Test Webhook click करें. अगर receiver webhook.test support करता है, तो response_status 200 होना चाहिए.
Correct implementation के लिए important rules
API integration में ये rules follow होने चाहिए ताकि system secure, stable और maintainable रहे.
Complete API flow summary
इन steps को complete करने के बाद, आपकी custom website OxiRanker में generated articles को securely receive, store, mirror और publish कर सकती है.