OxiRanker-dokumentation
Ett tydligt dokumentationscenter för att ansluta din webbplats till OxiRanker-tjänster. Börja med WordPress-integration eller API-integration och följ sedan guiden steg för steg.
API-tjänst
Använd Export API och Webhook för anpassade webbplatser, custom CMS-plattformar, Next.js, Laravel, Node.js, PHP eller Python backend.
OxiRanker är inte permanent hosting för destination website
OxiRanker ger ingen garanti för permanent hosting av innehåll, bilder eller exported files för destination website. OxiRanker är inte en user-content hosting service, och generated content, cover images, Open Graph images eller output files kanske inte finns kvar på OxiRankers infrastruktur i mer än 90 dagar.
Därför måste destination website, efter att ha tagit emot output, lagra final article, images, SEO data, Open Graph data och Schema data i sin egen database, server, storage eller CDN, och får inte vara beroende av OxiRanker URLs för publik visning.
Utbildningsvideo för anslutning
Använd den här videon som en visuell genomgång innan du ansluter WordPress eller implementerar API-integrationen.
Implementera output flow steg för steg
Spara först Export Token och Webhook Secret i din backend, slutför sedan Webhook receiver, database, UPSERT logic, Image Mirroring och daily sync.
Registrera dig och skapa domain i OxiRanker
Registrera dig först i OxiRanker, sign in på ditt account och klicka på Add Domain i sektionen Domains.
Korrekt domain-format
I fältet Domain name ska du endast ange root domain. Adressen får inte innehålla http, https eller www.
example.comFelaktiga format
Ange inte dessa format i domain-fältet.
https://example.com
http://example.com
www.example.comVerifiera domain-ägarskap
I det här steget måste du bevisa att domain tillhör dig eller att du har access för att hantera den.
DNS TXT
RekommenderasLägg till en TXT record i domain DNS. För Cloudflare är detta vanligtvis den enklaste metoden.
DNS Name:
_yourdomain-verification.example.com
TXT Value:
acb1****29803HTTP File
Placera en text file i webbplatsens .well-known path så att OxiRanker kan verifiera den.
File URL:
https://example.com/.well-known/yourdomain-verification.txt
File Content:
220daaa37****42730f27Meta Tag
Placera en meta tag i head section på webbplatsens homepage.
<meta name="yourdomain-verification" content="190208d****d275b65" />Ta emot gratis token efter verifiering av första domain
Efter att första domain har verifierats lägger OxiRanker till 1000 free tokens på ditt account för initial testing.
Konfigurera språk och Blog URL
I sektionen Blog URLs definierar du om din webbplats är single-language eller multilingual och vilka languages som ska användas för generated articles.
Välj språk
OxiRanker stöder 24 languages. Du kan välja ett eller flera languages baserat på webbplatsens struktur.
Ange Blog URL
För varje language anger du blog URL för det språket. Om din webbplats är multilingual anger du varje language path separat.
https://example.com/fa/blog
https://example.com/en/blogLägg till keywords för varje språk
I tabben Keywords lägger du till minst 3 och högst 50 keywords för varje språk.
Huvudregel
Keywords för varje språk måste anges på samma språk. Om article language är English måste keywords också vara English.
Så lägger du till keywords
Efter att du har skrivit varje keyword trycker du på Enter för att lägga till den. När alla keywords är klara klickar du på Save.
Slutför domain Article Profile
Article Profile berättar för OxiRanker vad ditt brand är, vem det skriver för, vilken tone det ska använda och vilka topics det ska undvika.
Industry
Definierar market eller business category. Exempel: real estate consulting och property buying and selling i Dubai.
Business description
Förklarar vad business säljer, vem det betjänar, hur det fungerar och vad som gör det annorlunda.
Audience
Definierar vem content ska tala till. Varje audience kan sparas separat.
Tone
Styr style och voice för generated content, till exempel formal, educational, simple, technical eller sales-oriented.
Content goal
Definierar vad articles ska uppnå, till exempel att locka customers, öka traffic eller utbilda users.
Förbjudna ämnen
Allt som brand inte bör tala om eller inte bör associeras med anges här.
Optional CTA
CTA betyder den action du vill att reader ska göra efter att ha läst article. Om du inte är säker på vilken text som är suitable är det bättre att lämna detta field empty.
Intern och extern länkning
OxiRanker kan intelligent tillämpa internal links och external links i artiklar.
Internal links
Det här option är enabled som default och det är bäst att hålla det enabled. Internal linking hjälper search engines att förstå webbplatsens content structure bättre.
I den första article skapas internal links vanligtvis inte eftersom det ännu inte finns någon previous article. Från den andra article och framåt kan system utföra internal linking.
External links
External links läggs till official och trusted sources och markeras som nofollow så att SEO authority inte överförs unintentionally.
Kontrollera profile quality med AI
Efter att Article Profile är slutförd granskar OxiRanker profile quality.
Anslut Telegram för article summaries
Efter att profile har saveats framgångsrikt kan du enable att article summaries skickas till en Telegram channel.
Lägg till bot
Lägg till @Oxiranker_Bot i din Telegram channel och gör den till Admin.
Ange channel username
Ange channel name med @. Exempel: @my_channel
Enable summary delivery
Efter att en valid channel har registrerats, enable article summary delivery.
Konfigurera automatic article engine
I tabben Automatic article generation kan du hantera automatic article generation engine.
Smart topic selection
System använder keywords och Article Profile för att välja suitable, unique och publishable topics.
Brand-aware generation
Tone, audience, content goal och brand restrictions respekteras under article generation.
Scheduled publishing
Monthly capacity distribueras över månaden så att content publishing ser mer natural och consistent ut.
Monthly capacity
7 articles / month
10 articles / month
15 articles / month
30 articles / month30 articles betyder nästan en article per dag. 15 articles betyder nästan varannan dag. 10 articles betyder ungefär varannan till var tredje dag. 7 articles betyder ungefär var tredje till var fjärde dag.
Article length och image
Article length kan vara Short, Standard eller Long. Standard är ett balanced choice för de flesta webbplatser.
Article images kan vara enabled eller disabled, och image theme kan vara Light eller Dark.
Skapa första article från Generate
Efter att domain har förberetts öppnar du Generate menu, väljer domain och klickar på New Content eller New Article.
Language
Endast ett language väljs för varje content request.
Topic
Skriv ett tydligt topic. Source URL är optional.
Image
Enable eller disable image och välj theme Light eller Dark.
Payment och generation
Cost dras från wallet och article generation startar.
Granska Preview, SEO Report och JSON Output
Efter att article har generated finns flera important outputs tillgängliga för quality review och publishing preparation.
Preview
Visar article text och generated image. Den här section är användbar för att granska final quality före publishing.
SEO Report
Visar en understandable SEO report inklusive SEO Score, title, meta description, canonical, keywords, schema, headings, links, images och OpenGraph.
JSON Output
Visar full article output för technical users, inklusive slug, html_body, schema, image URLs, tags, category, reading time och created/updated timestamps.
Vad gör Export API och Webhook exakt?
Efter att en article har generated i OxiRanker kan destination website read den med Export API eller bli notified via Webhook när article är ready.
Metod 1: Export API
Destination website kan read articles från OxiRanker med Export Token när det behövs. Den här method används för manual sync, daily sync, article list pages och fetching full article JSON.
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/jsonMetod 2: Webhook
När en new article created eller en article updated notify OxiRanker destination website endpoint. Webhook skickar inte full article. Den skickar bara article identifier så att destination website kan fetch full article från Export JSON API.
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 och Image Mirroring
Destination website måste store final article och images på sin egen infrastructure.
Vad destination website måste store
Korrekt exempel på image path
Att använda OxiRanker image URLs directly och permanently är inte korrekt. Image måste downloadas och article URLs måste replace med interna URLs för destination website.
https://oxiranker.com/uploads/articles/3_5/cover.webphttps://example.com/uploads/site-articles/oxiranker/1/5/cover.webpHämta Export Token och ready endpoints
Export Token och ready-to-use endpoints finns tillgängliga från article page, Outputs tab och API Output section.
Var du hämtar Export Token
https://oxiranker.com/en/pannel/generate/{domainId}/articles/{articleId}Ready-to-use endpoints
På samma article page, i API Output section, visar Ready-to-use endpoints prepared URLs.
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=0Skapa Webhook Secret och destination endpoint
Webhook Secret skapas också från article page, Outputs tab och API Output section.
Var skapas Webhook Secret?
Fixed destination endpoint
OxiRanker accepterar endast följande fixed path för destination Webhook. Destination website måste create denna endpoint i sin backend.
POST /api/oxiranker/webhook
https://example.com/api/oxiranker/webhookDenna endpoint måste endast accept POST, preserve raw body, verify timestamp och signature, samt return successful response för webhook.test.
Required .env values för destination website
OxiRanker har inte access till destination website files eller .env. Destination website developer måste store dessa values i sin egen backend.
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 och ZIP
Destination website kan använda Export API för att receive article list eller full output för varje article.
Hämta list of articles för domain
GET https://oxiranker.com/api/v1/users/domains/:domainId/export/articles?limit=20&offset=0
Authorization: Bearer YOUR_EXPORT_TOKEN
Accept: application/jsonlimit definierar number of articles och är för närvarande limited mellan 1 och 50. offset används för pagination. lang är optional och används för att fetch articles för specific language som fa, en eller tr.
Hämta full article som JSON
GET https://oxiranker.com/api/v1/users/domains/:domainId/articles/:articleId/export/json
Authorization: Bearer YOUR_EXPORT_TOKEN
Accept: application/jsonDetta är den mest important output för destination website. Destination website måste store full article JSON i sin egen database och read från sin egen database för public display.
Hämta 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 passar websites som vill ha ready output, men en mer professional approach är att store JSON och render det med destination website egen template.
Hämta ZIP output
GET https://oxiranker.com/api/v1/users/domains/1/articles/5/export/zip
Authorization: Bearer YOUR_EXPORT_TOKENZIP output innehåller article.json, meta.json, assets.json, schema.jsonld, index.html, version.txt och README.txt.
Webhook Signature och secure Webhook receiving
Destination website måste verify Webhook signature med original raw body.
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)Om destination website först parse JSON och sedan stringify den igen kan order eller spacing ändras och signature kan bli invalid. Original raw body måste användas för signature verification.
Simple Node.js code sample för receiving Webhook
Detta Express sample handle raw body, timestamp, signature, webhook.test och article events.
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 måste store articles i sin egen database och prevent duplicates med unique constraint.
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);Förhindra duplicates med UPSERT
Article ska inte inserted igen för varje Webhook. Om den redan exists måste den updated.
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();Fetch full article och sync efter Webhook
När Webhook kommer måste destination website fetch full article från Export API, mirror images och UPSERT article.
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 och image storage rules
Destination website måste download images, store dem på sin egen infrastructure och replace article URLs.
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.webpDaily sync för reliability
Webhook alone räcker inte. Destination website bör också ha daily sync för att recover missed articles.
Varför behövs daily sync?
Daily sync algorithm
Visa articles på destination website
Efter storing articles i site_articles ska destination website inte connect till OxiRanker för varje public display. Den måste read från sin egen database.
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 och HTML rendering
Destination website måste read SEO data från sin egen database och render dem på article page.
SEO fields
HTML rendering
<div
className="article-content"
dangerouslySetInnerHTML={{ __html: article.html_body }}
/>Om destination website har strict security policy kan den sanitize HTML före saving eller före displaying.
RTL och LTR
Varje article har fields lang, dir och is_rtl. Om dir är rtl måste article page rendered right-to-left.
<article dir="rtl">
...
</article>Common errors och deras betydelse
Destination website errors måste vara English, clear och understandable så att troubleshooting kan göras snabbt.
Export API errors
Webhook errors
Final API och Webhook testing checklist
Efter implementation verify dessa tests connection från beginning till end.
Export Token och 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
Om du call destination Webhook med Postman utan signature måste du receive security error. Det betyder att endpoint är active och checks security.
{
"error": "missing_webhook_signature",
"message": "Webhook signature header is required."
}Klicka sedan på Test Webhook inuti OxiRanker. Om receiver supports webhook.test måste response_status vara 200.
Important rules för correct implementation
Dessa rules måste följas i API integration så att system förblir secure, stable och maintainable.
Complete API flow summary
Efter att dessa steps slutförts kan din custom website securely receive, store, mirror och publish articles som generated i OxiRanker.