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. |
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
- Tasks: the same background-work approach, for your own apps
- Integrations Overview