SEO Reports

The SEO app (seo.inklura.fr) pulls data from several third-party providers and assembles it into reports. Because a full report takes longer than a render allows, generation runs off the render path on the warm task executor; the developer surface is one POST endpoint plus an SSE progress stream.

Data providers

Report data comes from outbound fetches to these providers. Their API keys live in the app's per-app bext KV store:

Provider Used for
DataForSEO SERP results + keyword volume / ideas.
SE Ranking Ranking data.
GA4 + Google Search Console Read-only traffic + search analytics.
On-page crawler / spider On-page audits.
Note

For GA4 and Google Search Console the Google OAuth dance happens in auth.1clic.pro — the SEO app only consumes the resulting token, it does not run the consent flow itself.

The async report pipeline

This is the main developer surface. Generate a report with:

POST /api/seo/reports
Content-Type: application/json

{ "websiteId": "<id>", "async": true }

The request is session-gated. Behaviour depends on async:

async: true (default)

The route writes a KV job and dispatches it to the warm task executor:

POST /__bext/sdk/tasks/run
Content-Type: application/json
X-Bext-App-Id: <seo app id>

{
  "task": "seo-report",
  "payload": { "jobId": "…", "websiteId": "…", "userId": "…" },
  "timeout_secs": 600
}

and immediately returns:

202 Accepted

{ "jobId": "<id>", "status": "pending" }

async: false

The report is generated inline, within the render deadline (~28s). Use this only for reports small enough to finish in time; anything heavier should stay async.

Following progress (SSE)

Track an async job over Server-Sent Events:

GET /api/seo/reports/stream?jobId=<id>

The response is text/event-stream. The handler polls the KV job roughly every 1.5s, emits event: status frames, and self-terminates after ~24s — an EventSource client will auto-reconnect and keep following until the job is done.

const es = new EventSource(`/api/seo/reports/stream?jobId=${jobId}`);
es.addEventListener("status", (e) => {
  const job = JSON.parse(e.data);
  // update UI; close when the job reports completion
});

The warm task-worker

The work behind seo-report runs warm, outside any render deadline, under the task supervisor. The app registers two tasks:

defineTask("seo-report", async (payload) => { /* build the report */ });
defineTask("seo-weekly", async (payload) => { /* scheduled weekly report */ });

Because these run on the warm executor rather than in the render pool, a report can take as long as its timeout_secs allows.

Other routes

The SEO app exposes further routes under /api/seo/*, including:

/api/seo/audit
/api/seo/keywords
/api/seo/onpage
/api/seo/traffic
/api/seo/websites
/api/seo/geo
/api/seo/research

(and more). The report pipeline above is the one to build automation against.

Related

  • Tasks — the warm task executor that runs seo-report
  • KV — where job state and provider keys are stored
  • Integrations Overview — the two-worlds map