SEO Reports

The SEO app (seo.inklura.fr) gathers data from several sources and assembles it into reports. For a developer, report generation comes down to one POST endpoint and, for background generation, one SSE progress stream.

Data sources

Source Used for
Search engine data Rankings in results, keyword volumes and ideas.
Google Analytics 4 and Google Search Console Traffic and search statistics.
Crawler On-page audits of your site.
Note

For Google Analytics 4 and Google Search Console, you connect a Google account and choose, for each site, the Search Console and GA4 properties the SEO app should read.

Generating a report

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

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

The request is authenticated by the SEO app's session cookie and restricted to agency accounts. Behaviour depends on async.

Status Case
401 No session: { "error": "unauthorized" }.
403 Valid session, but not an agency account (for example a portal client account).
400 Missing websiteId or unreadable body: { "error": "websiteId requis" }.
404 Unknown site: { "error": "Site introuvable" }.

async: true

The report is queued and generated in the background. The response comes back immediately:

202 Accepted

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

Then follow the job with the progress stream below. This is the mode the console uses. The job goes through the statuses pending, running, then completed or failed; it can stay pending for a while before generation starts.

Without async (default)

The report is generated during the request, saved, then returned in the response:

200 OK

{
  "id": "<id>",
  "title": "...",
  "clientName": "...",
  "healthScore": 72,
  "healthGrade": null,
  "summary": "...",
  "metrics": "{...}"
}

healthGrade is always null (no letter grade is computed) and metrics is a JSON string to decode. The request must fit within a time limit of under 30 seconds, so keep this mode for small reports and use async: true for anything heavier.

Following progress (SSE)

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

The response is text/event-stream, authenticated by the session cookie (without a valid agency session, the request is redirected instead of opening the stream). The stream checks the job about every 1.5 seconds and sends an event: status frame each time the status changes (comment pings in between):

event: status
data: {"status":"completed","reportId":"<id>","error":null}

It closes when the job is completed or failed, or on its own after about 24 seconds. It sets retry: 2000, so an EventSource client reconnects automatically and keeps following the job until it finishes. A request without jobId gets an event: error frame and the stream closes. For an unknown jobId, the stream only sends pings until it closes.

const es = new EventSource(`/api/seo/reports/stream?jobId=${jobId}`);
es.addEventListener("status", (e) => {
  const job = JSON.parse(e.data); // { status, reportId, error }
  if (job.status === "completed" || job.status === "failed") es.close();
});

Scheduled emails

In addition to on-demand reports, the app's "Emails programmés" (scheduled emails) page lets you schedule report emails on a weekly or monthly basis.

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

For automation, build on the report generation described above.

Related